diff --git a/index.html b/index.html
index 5bbbd7f..758464e 100644
--- a/index.html
+++ b/index.html
@@ -394,6 +394,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Modal/SWModal.ts b/src/components/Modal/SWModal.ts
index cb2ccc0..c6d7dd8 100644
--- a/src/components/Modal/SWModal.ts
+++ b/src/components/Modal/SWModal.ts
@@ -19,7 +19,9 @@ export function initSWModal(renderContent: () => void, closeModals: () => void)
const newAsset: SoftwareAsset = {
id: id || Math.random().toString(36).substring(2, 9),
type: (document.getElementById('sw-asset-type') as HTMLInputElement).value,
+ 분야: (document.getElementById('sw-분야') as HTMLSelectElement).value,
법인: (document.getElementById('sw-법인') as HTMLSelectElement).value,
+ 부서: (document.getElementById('sw-부서') as HTMLInputElement).value,
제품명: (document.getElementById('sw-제품명') as HTMLInputElement).value,
구매일: (document.getElementById('sw-구매일') as HTMLInputElement).value,
구독일: (document.getElementById('sw-구독일') as HTMLInputElement).value,
@@ -82,7 +84,9 @@ export function openSwModal(asset?: SoftwareAsset) {
(document.getElementById('sw-asset-id') as HTMLInputElement).value = asset.id;
(document.getElementById('sw-asset-type') as HTMLInputElement).value = asset.type;
+ (document.getElementById('sw-분야') as HTMLSelectElement).value = asset.분야 || '업무공통';
(document.getElementById('sw-법인') as HTMLSelectElement).value = asset.법인;
+ (document.getElementById('sw-부서') as HTMLInputElement).value = asset.부서 || '';
(document.getElementById('sw-제품명') as HTMLInputElement).value = asset.제품명;
(document.getElementById('sw-구매일') as HTMLInputElement).value = asset.구매일 || '';
(document.getElementById('sw-구독일') as HTMLInputElement).value = asset.구독일 || '';
@@ -97,6 +101,8 @@ export function openSwModal(asset?: SoftwareAsset) {
deleteBtn.style.display = 'none';
(document.getElementById('sw-asset-id') as HTMLInputElement).value = '';
(document.getElementById('sw-asset-type') as HTMLInputElement).value = state.activeSubTab;
+ (document.getElementById('sw-분야') as HTMLSelectElement).value = '업무공통';
(document.getElementById('sw-법인') as HTMLSelectElement).value = '한맥';
+ (document.getElementById('sw-부서') as HTMLInputElement).value = '';
}
}
diff --git a/src/dummyDataGenerator.ts b/src/dummyDataGenerator.ts
index 3a292a8..5b48c9a 100644
--- a/src/dummyDataGenerator.ts
+++ b/src/dummyDataGenerator.ts
@@ -140,7 +140,9 @@ export function generateDummyData(): MasterAssetData {
sw.push({
id: swId,
type: '구독SW',
+ 분야: rand(['업무공통', '개발S/W', '디자인', '설계S/W']),
법인: rand(corps),
+ 부서: rand(depts),
제품명: rand(['Adobe CC All Apps', 'Microsoft 365', 'Slack Pro', 'Notion Team']),
구매일: '2024-01-01',
구독일: `2024.01.01 ~ ${endStr}`,
@@ -182,7 +184,9 @@ export function generateDummyData(): MasterAssetData {
sw.push({
id: swId,
type: '영구SW',
+ 분야: rand(['업무공통', '개발S/W', '디자인', '설계S/W']),
법인: rand(corps),
+ 부서: rand(depts),
제품명: rand(['AutoCAD 2024', 'Windows 10 Pro', '한컴오피스 2022', 'Visual Studio 2022']),
구매일: '2020-05-15',
유지보수여부: true,
diff --git a/src/excelHandler.ts b/src/excelHandler.ts
index 36479bc..6b9bc1a 100644
--- a/src/excelHandler.ts
+++ b/src/excelHandler.ts
@@ -35,7 +35,9 @@ export interface HardwareAsset {
export interface SoftwareAsset {
id: string;
type: string; // '구독SW', '영구SW'
+ 분야?: string;
법인: string;
+ 부서?: string;
제품명: string;
구매일: string;
구독일?: string;
@@ -71,8 +73,8 @@ const SW_TABS = ['구독SW', '영구SW'];
const HW_HEADERS = ['법인', '자산코드', '명칭', '위치', '관리자', 'IP주소', 'MACaddress', 'HW사양', 'OS', '구매일', '금액', '납품업체', '품의서명'];
const PC_HEADERS = ['법인', '자산코드', '사용자', '위치', 'CPU', 'GPU', 'RAM', 'SSD1', 'SSD2', 'HDD1', 'HDD2', '구매일', '금액', '납품업체', '품의서명'];
const STORAGE_HEADERS = ['법인', '유형', '자산코드', '명칭', '위치', '모델명', '용량', '담당자(정)', '담당자(부)', 'IP주소', 'MAC주소', '구매일', '금액', '납품업체', '품의서명'];
-const SUB_SW_HEADERS = ['ID', '법인', '제품명', '구매일', '구독일', '금액', '수량', '계정명', '납품업체', '비고'];
-const PERM_SW_HEADERS = ['ID', '법인', '제품명', '구매일', '유지보수여부', '금액', '수량', '계정명', '납품업체', '비고'];
+const SUB_SW_HEADERS = ['ID', '분야', '법인', '부서', '제품명', '구매일', '구독일', '금액', '수량', '계정명', '납품업체', '비고'];
+const PERM_SW_HEADERS = ['ID', '분야', '법인', '부서', '제품명', '구매일', '유지보수여부', '금액', '수량', '계정명', '납품업체', '비고'];
const SW_USER_HEADERS = ['id', 'swId', '법인', '부서', '팀', '직위', '이름', '사용기간', '신청서명'];
/**
@@ -102,7 +104,7 @@ export function downloadTemplate() {
SW_TABS.forEach(tab => {
let hd = tab === '구독SW' ? SUB_SW_HEADERS : PERM_SW_HEADERS;
const ws = XLSX.utils.aoa_to_sheet([hd]);
- ws['!cols'] = [{wch:15}, {wch:15}, {wch:30}, {wch:15}, {wch:20}, {wch:15}, {wch:10}, {wch:20}, {wch:20}, {wch:30}];
+ ws['!cols'] = [{wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:30}, {wch:15}, {wch:20}, {wch:15}, {wch:10}, {wch:20}, {wch:20}, {wch:30}];
XLSX.utils.book_append_sheet(wb, ws, tab);
});
@@ -157,16 +159,16 @@ export function exportToExcel(masterData: MasterAssetData) {
if (tab === '구독SW') {
wsData = [
SUB_SW_HEADERS,
- ...targetAssets.map(a => [a.id, a.법인, a.제품명, a.구매일, a.구독일, a.금액, a.수량, a.계정명, a.납품업체, a.비고])
+ ...targetAssets.map(a => [a.id, a.분야||'', a.법인, a.부서||'', a.제품명, a.구매일, a.구독일, a.금액, a.수량, a.계정명, a.납품업체, a.비고])
];
} else {
wsData = [
PERM_SW_HEADERS,
- ...targetAssets.map(a => [a.id, a.법인, a.제품명, a.구매일, a.유지보수여부 ? 'Y' : 'N', a.금액, a.수량, a.계정명, a.납품업체, a.비고])
+ ...targetAssets.map(a => [a.id, a.분야||'', a.법인, a.부서||'', a.제품명, a.구매일, a.유지보수여부 ? 'Y' : 'N', a.금액, a.수량, a.계정명, a.납품업체, a.비고])
];
}
const ws = XLSX.utils.aoa_to_sheet(wsData);
- ws['!cols'] = [{wch:15}, {wch:15}, {wch:30}, {wch:15}, {wch:20}, {wch:15}, {wch:10}, {wch:20}, {wch:20}, {wch:30}];
+ ws['!cols'] = [{wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:30}, {wch:15}, {wch:20}, {wch:15}, {wch:10}, {wch:20}, {wch:20}, {wch:30}];
XLSX.utils.book_append_sheet(wb, ws, tab);
});
@@ -281,7 +283,9 @@ export async function parseExcel(file: File): Promise {
swAssets.push({
id: row['ID'] ? String(row['ID']) : Math.random().toString(36).substring(2, 9),
type: sheetName,
+ 분야: row['분야'] || '',
법인: row['법인'] || '',
+ 부서: row['부서'] || '',
제품명: row['제품명'] || '',
구매일: row['구매일'] || '',
구독일: row['구독일'] || '',
diff --git a/src/style.css b/src/style.css
index 4b6238e..0bcbce1 100644
--- a/src/style.css
+++ b/src/style.css
@@ -300,6 +300,10 @@ tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background-color: var(--bg-color); }
.empty-row td { text-align: center; padding: 3rem; color: var(--text-muted); }
+.sw-table td {
+ text-align: center;
+}
+
/* Modal */
.modal-overlay {
position: fixed;
diff --git a/src/views/AssetTableView.ts b/src/views/AssetTableView.ts
index 6bb2d81..e193d79 100644
--- a/src/views/AssetTableView.ts
+++ b/src/views/AssetTableView.ts
@@ -78,18 +78,19 @@ function renderSwTable(table: HTMLTableElement, container: HTMLElement, mainCont
const list = state.masterData.sw.filter(a => a.type === state.activeSubTab);
const isSub = state.activeSubTab === '구독SW';
- table.innerHTML = `| No | 법인 | 제품명 | 구매일 | ${isSub ? '구독일 | ' : ''}수량 | 사용가능 | 관리 |
`;
+ table.classList.add('sw-table');
+ table.innerHTML = `| No. | 분야 | 법인 | 부서 | 제품명 | 구매일 | ${isSub ? '구독일 | ' : ''}수량 | 사용가능 | 관리 |
`;
container.appendChild(table);
mainContent.appendChild(container);
const tbody = document.getElementById('dynamic-tbody')!;
- if (list.length === 0) { tbody.innerHTML = `| 정보가 없습니다. |
`; return; }
+ if (list.length === 0) { tbody.innerHTML = `| 정보가 없습니다. |
`; return; }
list.forEach((asset, idx) => {
const assigned = state.masterData.swUsers.filter(u => u.swId === asset.id).length;
const avail = (typeof asset.수량 === 'number' ? asset.수량 : parseInt(asset.수량||'0', 10)) - assigned;
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
- tr.innerHTML = `${idx+1} | ${asset.법인} | ${asset.제품명} | ${asset.구매일||''} | ${isSub ? `${asset.구독일||''} | ` : ''}${asset.수량} | ${avail} | | `;
+ tr.innerHTML = `${idx+1} | ${asset.분야||''} | ${asset.법인} | ${asset.부서||''} | ${asset.제품명} | ${asset.구매일||''} | ${isSub ? `${asset.구독일||''} | ` : ''}${asset.수량} | ${avail} | | `;
tr.addEventListener('click', (e) => { if (!(e.target as HTMLElement).closest('button')) openSwModal(asset); });
tr.querySelector('.btn-edit')?.addEventListener('click', () => openSwModal(asset));
tr.querySelector('.btn-users')?.addEventListener('click', () => openSwUserModal(asset));