feat: update server asset details, ui labels, and excel mapping logic

This commit is contained in:
2026-04-17 10:34:32 +09:00
parent 7158689fd0
commit 54bfb9d482
3 changed files with 61 additions and 22 deletions

View File

@@ -21,7 +21,7 @@ const HW_MODAL_HTML = `
<!-- Group 1: 기본 정보 --> <!-- Group 1: 기본 정보 -->
<div class="form-section-title">기본 정보 (Identity)</div> <div class="form-section-title">기본 정보 (Identity)</div>
<div class="form-group"> <div class="form-group">
<label for="hw-법인">법인</label> <label for="hw-법인">구매법인</label>
<input type="text" id="hw-법인" required /> <input type="text" id="hw-법인" required />
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -32,6 +32,14 @@ const HW_MODAL_HTML = `
<label for="hw-구매일">구매일자</label> <label for="hw-구매일">구매일자</label>
<input type="text" id="hw-구매일" /> <input type="text" id="hw-구매일" />
</div> </div>
<div class="form-group">
<label for="hw-현사용조직">현 사용조직</label>
<input type="text" id="hw-현사용조직" />
</div>
<div class="form-group">
<label for="hw-이전사용조직">이전 사용조직</label>
<input type="text" id="hw-이전사용조직" />
</div>
<div class="form-group server-only"> <div class="form-group server-only">
<label for="hw-용도">용도</label> <label for="hw-용도">용도</label>
<input type="text" id="hw-용도" /> <input type="text" id="hw-용도" />
@@ -187,6 +195,8 @@ function fillHwFormData(asset: HardwareAsset) {
(document.getElementById('hw-법인') as HTMLInputElement).value = asset.; (document.getElementById('hw-법인') as HTMLInputElement).value = asset.;
(document.getElementById('hw-자산코드') as HTMLInputElement).value = asset.; (document.getElementById('hw-자산코드') as HTMLInputElement).value = asset.;
(document.getElementById('hw-위치') as HTMLInputElement).value = asset.; (document.getElementById('hw-위치') as HTMLInputElement).value = asset.;
(document.getElementById('hw-현사용조직') as HTMLInputElement).value = asset. || '';
(document.getElementById('hw-이전사용조직') as HTMLInputElement).value = asset. || '';
(document.getElementById('hw-모델명') as HTMLInputElement).value = asset. || ''; (document.getElementById('hw-모델명') as HTMLInputElement).value = asset. || '';
(document.getElementById('hw-OS') as HTMLInputElement).value = asset.OS || ''; (document.getElementById('hw-OS') as HTMLInputElement).value = asset.OS || '';
(document.getElementById('hw-CPU') as HTMLInputElement).value = asset.CPU || ''; (document.getElementById('hw-CPU') as HTMLInputElement).value = asset.CPU || '';
@@ -288,6 +298,8 @@ export function initHwModal() {
: (document.getElementById('hw-법인') as HTMLInputElement).value, : (document.getElementById('hw-법인') as HTMLInputElement).value,
: (document.getElementById('hw-자산코드') as HTMLInputElement).value, : (document.getElementById('hw-자산코드') as HTMLInputElement).value,
: (document.getElementById('hw-위치') as HTMLInputElement).value, : (document.getElementById('hw-위치') as HTMLInputElement).value,
: (document.getElementById('hw-현사용조직') as HTMLInputElement).value,
: (document.getElementById('hw-이전사용조직') as HTMLInputElement).value,
: (document.getElementById('hw-모델명') as HTMLInputElement).value, : (document.getElementById('hw-모델명') as HTMLInputElement).value,
OS: (document.getElementById('hw-OS') as HTMLInputElement).value, OS: (document.getElementById('hw-OS') as HTMLInputElement).value,
CPU: (document.getElementById('hw-CPU') as HTMLInputElement).value, CPU: (document.getElementById('hw-CPU') as HTMLInputElement).value,

View File

@@ -38,6 +38,8 @@ export interface HardwareAsset {
서버PW?: string; 서버PW?: string;
모니터링?: string; 모니터링?: string;
비고?: string; 비고?: string;
현사용조직?: string;
이전사용조직?: string;
} }
@@ -90,7 +92,7 @@ const SW_TABS = ['구독SW', '영구SW'];
const HW_HEADERS = ['법인', '자산코드', '명칭', '위치', '관리자', 'IP주소', 'MACaddress', 'HW사양', 'OS', '구매일', '금액', '납품업체', '품의서명']; const HW_HEADERS = ['법인', '자산코드', '명칭', '위치', '관리자', 'IP주소', 'MACaddress', 'HW사양', 'OS', '구매일', '금액', '납품업체', '품의서명'];
const PC_HEADERS = ['법인', '자산코드', '사용자', '위치', 'CPU', 'GPU', 'RAM', 'SSD1', 'SSD2', 'HDD1', 'HDD2', '구매일', '금액', '납품업체', '품의서명']; const PC_HEADERS = ['법인', '자산코드', '사용자', '위치', 'CPU', 'GPU', 'RAM', 'SSD1', 'SSD2', 'HDD1', 'HDD2', '구매일', '금액', '납품업체', '품의서명'];
const SERVER_HEADERS = ['법인', '자산번호', '유형', '용도', '설치위치', '담당자(정)', '담당자(부)', 'IP 주소', '원격접속', '모델명', 'OS', 'CPU', 'RAM', 'GPU', 'Storage1', 'Storage2', 'Storage3', '모니터링', '비고']; const SERVER_HEADERS = ['구매법인', '자산번호', '구매일자', '용도', '상세내용', '현 사용조직', '이전 사용조직', '설치위치', '담당자(정)', '담당자(부)', 'IP 주소 1', 'IP 주소 2', '원격도구', '서버 ID', '서버 PW', '모델명', 'OS', 'CPU', 'RAM', 'SSD1', 'SSD2', '모니터링', '비고'];
const STORAGE_HEADERS = ['법인', '유형', '자산코드', '명칭', '위치', '모델명', '용량', '담당자(정)', '담당자(부)', 'IP주소', 'MAC주소', '구매일', '금액', '납품업체', '품의서명']; const STORAGE_HEADERS = ['법인', '유형', '자산코드', '명칭', '위치', '모델명', '용량', '담당자(정)', '담당자(부)', 'IP주소', 'MAC주소', '구매일', '금액', '납품업체', '품의서명'];
const SUB_SW_HEADERS = ['ID', '분야', '법인', '부서', '제품명', '구매일', '구독일', '금액', '수량', '계정명', '납품업체', '비고']; const SUB_SW_HEADERS = ['ID', '분야', '법인', '부서', '제품명', '구매일', '구독일', '금액', '수량', '계정명', '납품업체', '비고'];
const PERM_SW_HEADERS = ['ID', '분야', '법인', '부서', '제품명', '구매일', '유지보수여부', '금액', '수량', '계정명', '납품업체', '비고']; const PERM_SW_HEADERS = ['ID', '분야', '법인', '부서', '제품명', '구매일', '유지보수여부', '금액', '수량', '계정명', '납품업체', '비고'];
@@ -112,7 +114,7 @@ export function downloadTemplate() {
wscols = [{wch:15}, {wch:25}, {wch:15}, {wch:20}, {wch:20}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:25}]; wscols = [{wch:15}, {wch:25}, {wch:15}, {wch:20}, {wch:20}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
} else if (tab === '서버') { } else if (tab === '서버') {
hd = SERVER_HEADERS; hd = SERVER_HEADERS;
wscols = [{wch:15}, {wch:20}, {wch:15}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:20}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:30}]; wscols = [{wch:15}, {wch:20}, {wch:15}, {wch:25}, {wch:30}, {wch:20}, {wch:20}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:20}, {wch:20}, {wch:20}, {wch:20}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:30}];
} else if (tab === '스토리지') { } else if (tab === '스토리지') {
hd = STORAGE_HEADERS; hd = STORAGE_HEADERS;
wscols = [{wch:15}, {wch:15}, {wch:25}, {wch:25}, {wch:20}, {wch:25}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:25}]; wscols = [{wch:15}, {wch:15}, {wch:25}, {wch:25}, {wch:20}, {wch:25}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:25}];
@@ -164,9 +166,13 @@ export function exportToExcel(masterData: MasterAssetData) {
} else if (tab === '서버') { } else if (tab === '서버') {
wsData = [ wsData = [
SERVER_HEADERS, SERVER_HEADERS,
...targetAssets.map(a => [a., a., a.storage유형 || '물리', a. || '', a., a._정 || '', a._부 || '', a.IP주소, a. || '', a. || '', a.OS, a.CPU, a.RAM, a.GPU || '', a.SSD1 || '', a.SSD2 || '', a.HDD1 || '', a. || '', a. || '']) ...targetAssets.map(a => [
a., a., a. || '', a. || '', a. || '', a. || '', a. || '',
a., a._정 || '', a._부 || '', a.IP주소, (a as any).IP2 || '', a. || '',
(a as any).ID || '', (a as any).PW || '', a. || '', a.OS, a.CPU, a.RAM, a.SSD1 || '', a.SSD2 || '', a. || '', a. || ''
])
]; ];
colsConfig = [{wch:15}, {wch:20}, {wch:15}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:20}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:30}]; colsConfig = [{wch:15}, {wch:20}, {wch:15}, {wch:25}, {wch:30}, {wch:20}, {wch:20}, {wch:20}, {wch:15}, {wch:15}, {wch:20}, {wch:20}, {wch:20}, {wch:20}, {wch:20}, {wch:25}, {wch:20}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:15}, {wch:30}];
} else if (tab === '스토리지') { } else if (tab === '스토리지') {
wsData = [ wsData = [
STORAGE_HEADERS, STORAGE_HEADERS,
@@ -262,18 +268,27 @@ export async function parseExcel(file: File): Promise<MasterAssetData> {
hwAssets.push({ hwAssets.push({
id: Math.random().toString(36).substring(2, 9), id: Math.random().toString(36).substring(2, 9),
type: sheetName, type: sheetName,
법인: row['법인'] || '', 법인: row['구매법인'] || row['법인'] || '',
자산코드: row['자산번호'] || row['자산코드'] || '', 자산코드: row['자산번호'] || row['자산코드'] || '',
명칭: row['용도'] || row['명칭'] || '', 명칭: row['용도'] || row['명칭'] || '',
용도: row['용도'] || '', 위치: row['설치위치'] || row['위치'] || '', 구매일: row['구매일자'] || row['구매일'] || '',
용도: row['용도'] || '',
상세: row['상세내용'] || row['상세'] || '',
현사용조직: row['현 사용조직'] || '',
이전사용조직: row['이전 사용조직'] || '',
위치: row['설치위치'] || row['위치'] || '',
관리자: row['담당자(정)'] || '', 담당자_정: row['담당자(정)'] || '', 담당자_부: row['담당자(부)'] || '', 관리자: row['담당자(정)'] || '', 담당자_정: row['담당자(정)'] || '', 담당자_부: row['담당자(부)'] || '',
IP주소: row['IP 주소'] || row['IP주소'] || '', IP2: row['IP2'] || '', IP주소: row['IP 주소 1'] || row['IP 주소'] || row['IP주소'] || '',
원격접속: row['원격접속'] || '', 서버ID: row['서버ID'] || '', 서버PW: row['서버PW'] || '', IP2: row['IP 주소 2'] || row['IP2'] || '',
원격접속: row['원격도구'] || row['원격접속'] || '',
서버ID: row['서버 ID'] || row['서버ID'] || '',
서버PW: row['서버 PW'] || row['서버PW'] || '',
모델명: row['모델명'] || '', OS: row['OS'] || '', 모델명: row['모델명'] || '', OS: row['OS'] || '',
CPU: row['CPU'] || '', RAM: row['RAM'] || '', GPU: row['GPU'] || '', CPU: row['CPU'] || '', RAM: row['RAM'] || '',
SSD1: row['Storage1'] || row['SSD1'] || '', SSD2: row['Storage2'] || row['SSD2'] || '', HDD1: row['Storage3'] || row['HDD1'] || '', SSD1: row['SSD1'] || row['Storage1'] || '',
모니터링: row['모니터링'] || '', 비고: row['비고'] || '', storage유형: row['유형'] || '물리', SSD2: row['SSD2'] || row['Storage2'] || '',
MACaddress: '', HW사양: '', : '', : '', : '', : '', 모니터링: row['모니터링'] || '', 비고: row['비고'] || '', storage유형: '물리',
MACaddress: '', HW사양: '', : '', : '', : '',
}); });
} else if (sheetName === '스토리지') { } else if (sheetName === '스토리지') {
hwAssets.push({ hwAssets.push({

View File

@@ -32,7 +32,7 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
tableWrapper.className = 'table-container'; tableWrapper.className = 'table-container';
if (state.activeSubTab === '개인PC') { if (state.activeSubTab === '개인PC') {
table.innerHTML = `<thead><tr><th>No</th><th>법인</th><th>자산코드</th><th>사용자</th><th>위치</th><th>CPU</th><th>GPU</th><th>RAM</th><th>SSD1</th><th>SSD2</th><th>HDD1</th><th>HDD2</th><th>구매일</th><th>금액</th><th>납품업체</th><th>품의서</th><th>관리</th></tr></thead><tbody id="dynamic-tbody"></tbody>`; table.innerHTML = `<thead><tr><th>No</th><th>구매법인</th><th>자산코드</th><th>사용자</th><th>위치</th><th>CPU</th><th>GPU</th><th>RAM</th><th>SSD1</th><th>SSD2</th><th>HDD1</th><th>HDD2</th><th>구매일</th><th>금액</th><th>납품업체</th><th>품의서</th><th>관리</th></tr></thead><tbody id="dynamic-tbody"></tbody>`;
tableWrapper.appendChild(table); tableWrapper.appendChild(table);
container.appendChild(tableWrapper); container.appendChild(tableWrapper);
mainContent.appendChild(container); mainContent.appendChild(container);
@@ -46,7 +46,7 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
tbody.appendChild(tr); tbody.appendChild(tr);
}); });
} else if (state.activeSubTab === '스토리지') { } else if (state.activeSubTab === '스토리지') {
table.innerHTML = `<thead><tr><th>No</th><th>법인</th><th>유형</th><th>자산코드</th><th>명칭</th><th>위치</th><th>모델명</th><th>용량</th><th>담당자(정)</th><th>IP주소</th><th>구매일</th><th>금액</th><th>관리</th></tr></thead><tbody id="dynamic-tbody"></tbody>`; table.innerHTML = `<thead><tr><th>No</th><th>구매법인</th><th>유형</th><th>자산코드</th><th>명칭</th><th>위치</th><th>모델명</th><th>용량</th><th>담당자(정)</th><th>IP주소</th><th>구매일</th><th>금액</th><th>관리</th></tr></thead><tbody id="dynamic-tbody"></tbody>`;
tableWrapper.appendChild(table); tableWrapper.appendChild(table);
container.appendChild(tableWrapper); container.appendChild(tableWrapper);
mainContent.appendChild(container); mainContent.appendChild(container);
@@ -64,8 +64,9 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
const filterBar = document.createElement('div'); const filterBar = document.createElement('div');
filterBar.className = 'search-bar'; filterBar.className = 'search-bar';
// 법인, 유형, 위치 고유값 추출 // 법인, 조직, 유형, 위치 고유값 추출
const corps = Array.from(new Set(fullList.map(a => a.))).filter(Boolean).sort(); const corps = Array.from(new Set(fullList.map(a => a.))).filter(Boolean).sort();
const orgUnits = Array.from(new Set(fullList.map(a => a.))).filter(Boolean).sort();
const types = Array.from(new Set(fullList.map(a => a.storage유형))).filter(Boolean).sort(); const types = Array.from(new Set(fullList.map(a => a.storage유형))).filter(Boolean).sort();
const locations = Array.from(new Set(fullList.map(a => { const locations = Array.from(new Set(fullList.map(a => {
const loc = String(a. || ''); const loc = String(a. || '');
@@ -75,16 +76,23 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
filterBar.innerHTML = ` filterBar.innerHTML = `
<div class="search-item flex-1"> <div class="search-item flex-1">
<label>통합 검색 (자산번호/용도/모델명)</label> <label>통합 검색 (자산번호/조직/용도/모델명)</label>
<input type="text" id="filter-keyword" placeholder="검색어를 입력하세요..." autocomplete="off"> <input type="text" id="filter-keyword" placeholder="검색어를 입력하세요..." autocomplete="off">
</div> </div>
<div class="search-item"> <div class="search-item">
<label>법인</label> <label>구매법인</label>
<select id="filter-corp"> <select id="filter-corp">
<option value="">전체 법인</option> <option value="">전체 법인</option>
${corps.map(c => `<option value="${c}">${c}</option>`).join('')} ${corps.map(c => `<option value="${c}">${c}</option>`).join('')}
</select> </select>
</div> </div>
<div class="search-item">
<label>현 사용조직</label>
<select id="filter-org-unit">
<option value="">전체 조직</option>
${orgUnits.map(o => `<option value="${o}">${o}</option>`).join('')}
</select>
</div>
<div class="search-item"> <div class="search-item">
<label>유형</label> <label>유형</label>
<select id="filter-type"> <select id="filter-type">
@@ -105,7 +113,7 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
`; `;
container.appendChild(filterBar); container.appendChild(filterBar);
table.innerHTML = `<thead><tr><th>No</th><th>법인</th><th>자산번호</th><th>유형</th><th>용도</th><th>상세</th><th>설치위치</th><th>담당자</th><th>모델명</th><th>OS</th><th>CPU</th><th>RAM</th><th>Storage</th></tr></thead><tbody id="dynamic-tbody"></tbody>`; table.innerHTML = `<thead><tr><th>No</th><th>구매법인</th><th>현 사용조직</th><th>자산번호</th><th>유형</th><th>용도</th><th>상세</th><th>설치위치</th><th>담당자</th><th>모델명</th><th>OS</th><th>CPU</th><th>RAM</th><th>Storage</th></tr></thead><tbody id="dynamic-tbody"></tbody>`;
tableWrapper.appendChild(table); tableWrapper.appendChild(table);
container.appendChild(tableWrapper); container.appendChild(tableWrapper);
mainContent.appendChild(container); mainContent.appendChild(container);
@@ -115,6 +123,7 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
const updateTable = () => { const updateTable = () => {
const keyword = (document.getElementById('filter-keyword') as HTMLInputElement).value.toLowerCase().trim(); const keyword = (document.getElementById('filter-keyword') as HTMLInputElement).value.toLowerCase().trim();
const corp = (document.getElementById('filter-corp') as HTMLSelectElement).value; const corp = (document.getElementById('filter-corp') as HTMLSelectElement).value;
const orgUnit = (document.getElementById('filter-org-unit') as HTMLSelectElement).value;
const type = (document.getElementById('filter-type') as HTMLSelectElement).value; const type = (document.getElementById('filter-type') as HTMLSelectElement).value;
const location = (document.getElementById('filter-location') as HTMLSelectElement).value; const location = (document.getElementById('filter-location') as HTMLSelectElement).value;
@@ -126,9 +135,11 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
formatAsset(asset.).includes(keyword) || formatAsset(asset.).includes(keyword) ||
formatAsset(asset.).includes(keyword) || formatAsset(asset.).includes(keyword) ||
formatAsset(asset._정).includes(keyword) || formatAsset(asset._정).includes(keyword) ||
formatAsset(asset.).includes(keyword) ||
formatAsset(asset._부).includes(keyword); formatAsset(asset._부).includes(keyword);
const matchCorp = !corp || asset. === corp; const matchCorp = !corp || asset. === corp;
const matchOrgUnit = !orgUnit || asset. === orgUnit;
const matchType = !type || asset.storage유형 === type; const matchType = !type || asset.storage유형 === type;
let matchLocation = true; let matchLocation = true;
@@ -141,12 +152,12 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
} }
} }
return matchKeyword && matchCorp && matchType && matchLocation; return matchKeyword && matchCorp && matchOrgUnit && matchType && matchLocation;
}); });
tbody.innerHTML = ''; tbody.innerHTML = '';
if (filtered.length === 0) { if (filtered.length === 0) {
tbody.innerHTML = `<tr><td colspan="13" style="text-align:center; padding: 3rem; color: var(--text-muted);">검색 결과가 없습니다.</td></tr>`; tbody.innerHTML = `<tr><td colspan="14" style="text-align:center; padding: 3rem; color: var(--text-muted);">검색 결과가 없습니다.</td></tr>`;
return; return;
} }
@@ -169,6 +180,7 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
tr.innerHTML = ` tr.innerHTML = `
<td>${idx+1}</td> <td>${idx+1}</td>
<td class="text-nowrap">${formatInline(asset.)}</td> <td class="text-nowrap">${formatInline(asset.)}</td>
<td class="text-nowrap">${formatInline(asset.)}</td>
<td class="text-nowrap">${formatInline(asset.)}</td> <td class="text-nowrap">${formatInline(asset.)}</td>
<td class="text-nowrap">${formatInline(asset.storage유형)}</td> <td class="text-nowrap">${formatInline(asset.storage유형)}</td>
<td class="text-nowrap">${formatInline(asset.)}</td> <td class="text-nowrap">${formatInline(asset.)}</td>
@@ -205,7 +217,7 @@ function renderHwTable(table: HTMLTableElement, container: HTMLElement, mainCont
updateTable(); updateTable();
} else { } else {
// 전산비품 // 전산비품
table.innerHTML = `<thead><tr><th>No</th><th>법인</th>${state.activeSubTab === '전산비품' ? '<th>유형</th>' : ''}<th>자산코드</th><th>명칭</th><th>위치</th><th>관리자</th><th>구매일</th><th>금액</th><th>관리</th></tr></thead><tbody id="dynamic-tbody"></tbody>`; table.innerHTML = `<thead><tr><th>No</th><th>구매법인</th>${state.activeSubTab === '전산비품' ? '<th>유형</th>' : ''}<th>자산코드</th><th>명칭</th><th>위치</th><th>관리자</th><th>구매일</th><th>금액</th><th>관리</th></tr></thead><tbody id="dynamic-tbody"></tbody>`;
tableWrapper.appendChild(table); tableWrapper.appendChild(table);
container.appendChild(tableWrapper); container.appendChild(tableWrapper);
mainContent.appendChild(container); mainContent.appendChild(container);