-
-
-
구독 SW 만료 예정(30일 이내)
-
${subExp}개 제품
-
-
-
-
-
-
유지보수 만료 예정(30일 이내)
-
${permExp}개 제품
-
-
-
- ${permExpPer}%
-
+
+
내부 소프트웨어 현황
+
등록된 내부 솔루션: ${intTotal}개
+
${intPer}%
+
@@ -122,42 +59,25 @@ export function renderSwDashboard(container: HTMLElement) {
-
구독 SW 누적 비용 (2026)
-
갱신 및 추가 비용 합계
-
₩ ${subCost2026.toLocaleString()}
-
+
외부 SW 누적 비용 (2026)
+
₩ ${extCost2026.toLocaleString()}
-
영구 SW 누적 비용 (2026)
-
유지보수 및 신규 도입 합계
-
₩ ${permCost2026.toLocaleString()}
-
+
내부 SW 누적 비용 (2026)
+
₩ ${intCost2026.toLocaleString()}
-
`;
- container.querySelector('[data-action="sub-usage"]')?.addEventListener('click', () => openSwUsageDetail('구독 소프트웨어 사용 목록', state.masterData.subSw));
- container.querySelector('[data-action="perm-usage"]')?.addEventListener('click', () => openSwUsageDetail('영구 소프트웨어 사용 목록', state.masterData.permSw));
- container.querySelector('[data-action="sub-exp"]')?.addEventListener('click', () => openSwDashboardDetail('구독 SW 만료 예정 목록', state.masterData.subSw.filter(sw => isSWExpiring(sw))));
- container.querySelector('[data-action="perm-exp"]')?.addEventListener('click', () => openSwDashboardDetail('유지보수 만료 예정 목록', state.masterData.permSw.filter(sw => isSWExpiring(sw))));
+ container.querySelector('[data-action="ext-usage"]')?.addEventListener('click', () => openSwUsageDetail('외부 소프트웨어 사용 목록', state.masterData.swExternal));
+ container.querySelector('[data-action="int-usage"]')?.addEventListener('click', () => openSwUsageDetail('내부 소프트웨어 사용 목록', state.masterData.swInternal));
}
-function isSWExpiring(sw: SoftwareAsset) {
- if (sw.type === '구독SW' && sw.만료일) {
- const endMs = new Date(normalizeDate(sw.만료일)).getTime();
- const diffDays = (endMs - Date.now()) / (1000 * 60 * 60 * 24);
- return diffDays >= 0 && diffDays <= 30;
- } else if (sw.type === '영구SW' && sw.비고 && sw.비고.includes('유지보수: ~')) {
- try {
- const parts = sw.비고.split('~');
- if (parts.length > 1) {
- const endMs = new Date(normalizeDate(parts[1].trim())).getTime();
- const diffDays = (endMs - Date.now()) / (1000 * 60 * 60 * 24);
- return diffDays >= 0 && diffDays <= 30;
- }
- } catch { return false; }
- }
- return false;
+function isSWExpiring(sw: any) {
+ const expiry = sw[ASSET_SCHEMA.EXPIRY_DATE.key];
+ if (!expiry) return false;
+ const endMs = new Date(normalizeDate(expiry)).getTime();
+ const diffDays = (endMs - Date.now()) / (1000 * 60 * 60 * 24);
+ return diffDays >= 0 && diffDays <= 30;
}
diff --git a/src/views/List/CloudListView.ts b/src/views/List/CloudListView.ts
index 304f5ae..a91b6d4 100644
--- a/src/views/List/CloudListView.ts
+++ b/src/views/List/CloudListView.ts
@@ -1,38 +1,23 @@
import { state } from '../../core/state';
import { openSwModal } from '../../components/Modal/SWModal';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
-import { dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { dynamicSort, formatInline, getActionButtonsHTML, renderPageHeader } from '../../core/utils';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, Cloud, CreditCard, DollarSign, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, Cloud, CreditCard, DollarSign, RefreshCcw, Plus } from 'lucide';
/**
* 클라우드(운영 서비스) 자산 목록 뷰
- * 라인 정렬 보정 및 헤더 통일
*/
export function renderCloudList(container: HTMLElement) {
- const getFullList = () => state.masterData.cloud || [];
+ renderPageHeader(container, '클라우드');
+
+ const fullList = state.masterData.cloud || [];
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', corp: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.PRODUCT_NAME.ui}/${ASSET_SCHEMA.CURRENT_DEPT.ui}/${ASSET_SCHEMA.EMAIL_ACCOUNT.ui})
-
-
-
- ${ASSET_SCHEMA.PURCHASE_METHOD.ui}
-
- 전체 결제수단
- 법인카드
- 인보이스 (월별송금)
-
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -41,12 +26,11 @@ export function renderCloudList(container: HTMLElement) {
table.innerHTML = `
- No.
${ASSET_SCHEMA.PRODUCT_NAME.ui}
${ASSET_SCHEMA.ASSET_PURPOSE.ui}
${ASSET_SCHEMA.PURCHASE_VENDOR.ui}
${ASSET_SCHEMA.PURCHASE_AMOUNT.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -57,20 +41,7 @@ export function renderCloudList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const paymentSelect = document.getElementById('filter-payment') as HTMLSelectElement;
-
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
- const payment = paymentSelect ? paymentSelect.value : '';
-
- let filtered = getFullList().filter(asset => {
- const kwMatch = !keyword ||
- (asset[ASSET_SCHEMA.PRODUCT_NAME.key] || '').toLowerCase().includes(keyword) ||
- (asset[ASSET_SCHEMA.ASSET_PURPOSE.key] || '').toLowerCase().includes(keyword) ||
- (asset[ASSET_SCHEMA.PURCHASE_VENDOR.key] || '').toLowerCase().includes(keyword);
- const payMatch = !payment || asset[ASSET_SCHEMA.PURCHASE_METHOD.key] === payment;
- return kwMatch && payMatch;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['PRODUCT_NAME', 'ASSET_PURPOSE', 'PURCHASE_VENDOR']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -78,7 +49,7 @@ export function renderCloudList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
@@ -87,12 +58,11 @@ export function renderCloudList(container: HTMLElement) {
tr.style.cursor = 'pointer';
tr.innerHTML = `
-
${idx+1}
${asset[ASSET_SCHEMA.PRODUCT_NAME.key]||''}
${asset[ASSET_SCHEMA.ASSET_PURPOSE.key]||''}
${asset[ASSET_SCHEMA.PURCHASE_VENDOR.key]||''}
₩ ${asset[ASSET_SCHEMA.PURCHASE_AMOUNT.key] ? Number(String(asset[ASSET_SCHEMA.PURCHASE_AMOUNT.key]).replace(/,/g, '')).toLocaleString() : '0'}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'')}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'')}
`;
tr.addEventListener('click', () => openSwModal(asset, 'view'));
@@ -104,16 +74,31 @@ export function renderCloudList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { Cloud, CreditCard, DollarSign, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
+ createIcons({ icons: { Cloud, CreditCard, DollarSign, RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('filter-payment')?.addEventListener('change', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- if (document.getElementById('filter-keyword')) (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- if (document.getElementById('filter-payment')) (document.getElementById('filter-payment') as HTMLSelectElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.PRODUCT_NAME.ui}/${ASSET_SCHEMA.PURCHASE_VENDOR.ui})`,
+ showCorp: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/List/CostListView.ts b/src/views/List/CostListView.ts
index c69fdf6..590bbd2 100644
--- a/src/views/List/CostListView.ts
+++ b/src/views/List/CostListView.ts
@@ -1,29 +1,22 @@
import { state } from '../../core/state';
-import { formatInline, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, RefreshCcw, Plus } from 'lucide';
/**
* 비용관리 자산 목록 뷰
*/
export function renderCostList(container: HTMLElement) {
- // 비용관리 데이터는 cloud 또는 별도 테이블에 있을 수 있음.
+ renderPageHeader(container, '비용관리');
+
const fullList = sortAssets(state.masterData.cloud?.filter((a: any) => a.category === '비용관리') || []);
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', corp: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.PRODUCT_NAME.ui}/${ASSET_SCHEMA.MANAGER_MAIN.ui})
-
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -32,14 +25,12 @@ export function renderCostList(container: HTMLElement) {
table.innerHTML = `
- No.
${ASSET_SCHEMA.ASSET_TYPE.ui}
${ASSET_SCHEMA.ASSET_PURPOSE.ui}
현 사용자
- ${ASSET_SCHEMA.LOCATION.ui}(건물)
- ${ASSET_SCHEMA.LOC_DETAIL.ui}
+ ${ASSET_SCHEMA.LOCATION.ui}
${ASSET_SCHEMA.EMAIL_ACCOUNT.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -50,16 +41,7 @@ export function renderCostList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.PRODUCT_NAME.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.MANAGER_MAIN.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.EMAIL_ACCOUNT.key]||'').toLowerCase().includes(keyword);
- return matchKeyword;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['PRODUCT_NAME', 'MANAGER_MAIN', 'EMAIL_ACCOUNT']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -67,24 +49,26 @@ export function renderCostList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
filtered.forEach((asset, idx) => {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
+
+ const loc = asset[ASSET_SCHEMA.LOCATION.key] || '';
+ const detail = asset[ASSET_SCHEMA.LOC_DETAIL.key] || '';
+ const displayLoc = detail ? `${loc}(${detail})` : (loc || '-');
+
tr.innerHTML = `
-
${idx + 1}
${asset[ASSET_SCHEMA.ASSET_TYPE.key] || ''}
${formatInline(asset[ASSET_SCHEMA.ASSET_PURPOSE.key] || '-')}
${asset[ASSET_SCHEMA.MANAGER_MAIN.key] || '-'}
-
${asset[ASSET_SCHEMA.LOCATION.key] || '-'}
-
${asset[ASSET_SCHEMA.LOC_DETAIL.key] || '-'}
+
${displayLoc}
${asset[ASSET_SCHEMA.EMAIL_ACCOUNT.key] || '-'}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
- // 비용관리 모달이 따로 없으면 일단 SW모달 또는 알림
tr.addEventListener('click', () => alert('상세 정보 준비 중입니다.'));
tbody.appendChild(tr);
});
@@ -94,14 +78,31 @@ export function renderCostList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
+ createIcons({ icons: { RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.PRODUCT_NAME.ui}/${ASSET_SCHEMA.MANAGER_MAIN.ui})`,
+ showCorp: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/List/DomainListView.ts b/src/views/List/DomainListView.ts
index e09e86c..75eff70 100644
--- a/src/views/List/DomainListView.ts
+++ b/src/views/List/DomainListView.ts
@@ -1,31 +1,23 @@
import { state } from '../../core/state';
-import { formatPrice, dynamicSort, createBadge, getActionButtonsHTML } from '../../core/utils';
-import { createIcons, Plus, Edit2, Trash2, RefreshCcw, Download, Upload, FileSpreadsheet } from 'lucide';
+import { dynamicSort, formatInline, getActionButtonsHTML, renderPageHeader } from '../../core/utils';
+import { createIcons, Plus, Edit2, Trash2, RefreshCcw } from 'lucide';
import { openDomainModal } from '../../components/Modal/DomainModal';
import { setupTableSorting, SortState } from '../../core/tableHandler';
import { ASSET_SCHEMA } from '../../core/schema';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
// 정렬 상태를 모듈 수준에서 관리하여 화면 갱신 시에도 유지되도록 함
let persistentSortState: SortState = { key: '', direction: 'asc' };
export function renderDomainList(container: HTMLElement) {
- container.innerHTML = '';
+ renderPageHeader(container, '도메인');
const fullList = state.masterData.domain;
+ let currentFilters = { keyword: '', corp: '', dept: '', field: '' };
- // 검색바 및 액션 버튼 추가
+ // 검색바 추가
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.DOMAIN_ADDR.ui}/${ASSET_SCHEMA.PRODUCT_NAME.ui})
-
-
-
- 필터 초기화
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -34,13 +26,12 @@ export function renderDomainList(container: HTMLElement) {
table.innerHTML = `
- No.
${ASSET_SCHEMA.DOMAIN_ADDR.ui}
${ASSET_SCHEMA.ASSET_PURPOSE.ui}
${ASSET_SCHEMA.ASSET_TYPE.ui}
${ASSET_SCHEMA.PURCHASE_CORP.ui}
- ${ASSET_SCHEMA.EXPIRY_DATE.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.EXPIRED_DATE.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -51,16 +42,7 @@ export function renderDomainList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
-
- let filtered = fullList.filter(item => {
- const matchKeyword = !keyword ||
- (item[ASSET_SCHEMA.DOMAIN_ADDR.key] || '').toLowerCase().includes(keyword) ||
- (item[ASSET_SCHEMA.ASSET_PURPOSE.key] || '').toLowerCase().includes(keyword) ||
- (item[ASSET_SCHEMA.PRODUCT_NAME.key] || '').toLowerCase().includes(keyword);
- return matchKeyword;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['DOMAIN_ADDR', 'ASSET_PURPOSE', 'PRODUCT_NAME']);
if (persistentSortState.key) {
filtered = dynamicSort(filtered, persistentSortState.key, persistentSortState.direction);
@@ -68,7 +50,7 @@ export function renderDomainList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
등록된 도메인 정보가 없습니다. `;
+ tbody.innerHTML = `
등록된 도메인 정보가 없습니다. `;
return;
}
@@ -78,13 +60,12 @@ export function renderDomainList(container: HTMLElement) {
tr.style.cursor = 'pointer';
tr.innerHTML = `
-
${idx + 1}
${item[ASSET_SCHEMA.DOMAIN_ADDR.key] || ''}
${item[ASSET_SCHEMA.ASSET_PURPOSE.key] || ''}
${item[ASSET_SCHEMA.ASSET_TYPE.key] || '-'}
${item[ASSET_SCHEMA.PURCHASE_CORP.key] || ''}
-
${item[ASSET_SCHEMA.EXPIRY_DATE.key] || ''}
-
${formatInline(item[ASSET_SCHEMA.MEMO.key]||'-')}
+
${item[ASSET_SCHEMA.EXPIRED_DATE.key] || ''}
+
${formatInline(item[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', (e) => {
openDomainModal(item);
@@ -97,14 +78,31 @@ export function renderDomainList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { Plus, Edit2, Trash2, RefreshCcw, Download, Upload, FileSpreadsheet } });
+ createIcons({ icons: { Plus, Edit2, Trash2, RefreshCcw } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- if (document.getElementById('filter-keyword')) (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.DOMAIN_ADDR.ui}/${ASSET_SCHEMA.PRODUCT_NAME.ui})`,
+ showCorp: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/List/EquipmentListView.ts b/src/views/List/EquipmentListView.ts
index 8738416..bafc3b6 100644
--- a/src/views/List/EquipmentListView.ts
+++ b/src/views/List/EquipmentListView.ts
@@ -1,36 +1,23 @@
import { state } from '../../core/state';
import { openHwModal } from '../../components/Modal/HWModal';
-import { formatInline, createBadge, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, RefreshCcw, Plus } from 'lucide';
/**
* 전산비품 자산 목록 뷰
- * 라인 정렬 보정 및 헤더 통일
*/
export function renderEquipmentList(container: HTMLElement) {
+ renderPageHeader(container, '업무지원장비');
+
const fullList = sortAssets(state.masterData.equipment);
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', loc: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
- const corps = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.PURCHASE_CORP.key]))).filter(Boolean).sort();
-
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui}/${ASSET_SCHEMA.MANAGER_MAIN.ui})
-
-
-
- ${ASSET_SCHEMA.PURCHASE_CORP.ui}
- 전체 법인 ${corps.map(c => `${c} `).join('')}
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -39,16 +26,14 @@ export function renderEquipmentList(container: HTMLElement) {
table.innerHTML = `
- No.
${ASSET_SCHEMA.HW_STATUS.ui}
- 현 사용자
+ ${ASSET_SCHEMA.CURRENT_USER.ui}
${ASSET_SCHEMA.ASSET_TYPE.ui}
${ASSET_SCHEMA.ASSET_MFR.ui}
${ASSET_SCHEMA.MODEL_NAME.ui}
${ASSET_SCHEMA.ASSET_COUNT.ui}
- ${ASSET_SCHEMA.LOCATION.ui}(건물)
- ${ASSET_SCHEMA.LOC_DETAIL.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.LOCATION.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -59,20 +44,7 @@ export function renderEquipmentList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const corpSelect = document.getElementById('filter-corp') as HTMLSelectElement;
-
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
- const corp = corpSelect ? corpSelect.value : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.MODEL_NAME.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.MANAGER_MAIN.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.ASSET_MFR.key]||'').toLowerCase().includes(keyword);
- const matchCorp = !corp || asset[ASSET_SCHEMA.PURCHASE_CORP.key] === corp;
- return matchKeyword && matchCorp;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['MODEL_NAME', 'CURRENT_USER', 'ASSET_MFR']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -80,7 +52,7 @@ export function renderEquipmentList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
@@ -88,17 +60,19 @@ export function renderEquipmentList(container: HTMLElement) {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
+ const loc = asset[ASSET_SCHEMA.LOCATION.key] || '';
+ const detail = asset[ASSET_SCHEMA.LOC_DETAIL.key] || '';
+ const displayLoc = detail ? `${loc}(${detail})` : (loc || '-');
+
tr.innerHTML = `
-
${idx + 1}
${asset[ASSET_SCHEMA.HW_STATUS.key] || '보관중'}
-
${asset[ASSET_SCHEMA.MANAGER_MAIN.key] || '-'}
+
${asset[ASSET_SCHEMA.CURRENT_USER.key] || '-'}
${asset[ASSET_SCHEMA.ASSET_TYPE.key] || ''}
${asset[ASSET_SCHEMA.ASSET_MFR.key] || ''}
${formatInline(asset[ASSET_SCHEMA.MODEL_NAME.key] || asset.명칭)}
${asset[ASSET_SCHEMA.ASSET_COUNT.key] || '1'}
-
${asset[ASSET_SCHEMA.LOCATION.key] || '-'}
-
${asset[ASSET_SCHEMA.LOC_DETAIL.key] || '-'}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${displayLoc}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));
tbody.appendChild(tr);
@@ -109,16 +83,43 @@ export function renderEquipmentList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
+ createIcons({ icons: { RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('filter-corp')?.addEventListener('change', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- (document.getElementById('filter-corp') as HTMLSelectElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui}/${ASSET_SCHEMA.ASSET_MFR.ui})`,
+ showLoc: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Location Options
+ const locSelect = container.querySelector('#filter-loc') as HTMLSelectElement;
+ if (locSelect) {
+ const locations = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.LOCATION.key]))).filter(Boolean).sort();
+ locations.forEach(loc => {
+ const opt = document.createElement('option');
+ opt.value = String(loc);
+ opt.textContent = String(loc);
+ locSelect.appendChild(opt);
+ });
+ }
+
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/List/FacilityListView.ts b/src/views/List/FacilityListView.ts
index 3663c94..335d917 100644
--- a/src/views/List/FacilityListView.ts
+++ b/src/views/List/FacilityListView.ts
@@ -1,30 +1,23 @@
import { state } from '../../core/state';
import { openHwModal } from '../../components/Modal/HWModal';
-import { formatInline, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, RefreshCcw, Plus } from 'lucide';
/**
* 시설자산 자산 목록 뷰
*/
export function renderFacilityList(container: HTMLElement) {
- // 시설자산 데이터는 equipment 또는 별도 테이블에 있을 수 있음.
+ renderPageHeader(container, '사무가구');
+
const fullList = sortAssets(state.masterData.equipment?.filter((a: any) => a.category === '시설자산') || []);
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', corp: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui})
-
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -33,15 +26,13 @@ export function renderFacilityList(container: HTMLElement) {
table.innerHTML = `
- No.
${ASSET_SCHEMA.HW_STATUS.ui}
${ASSET_SCHEMA.ASSET_TYPE.ui}
${ASSET_SCHEMA.ASSET_MFR.ui}
${ASSET_SCHEMA.MODEL_NAME.ui}
- ${ASSET_SCHEMA.LOCATION.ui}(건물)
- ${ASSET_SCHEMA.LOC_DETAIL.ui}
+ ${ASSET_SCHEMA.LOCATION.ui}
${ASSET_SCHEMA.ASSET_COUNT.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -52,15 +43,7 @@ export function renderFacilityList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.MODEL_NAME.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.ASSET_MFR.key]||'').toLowerCase().includes(keyword);
- return matchKeyword;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['MODEL_NAME', 'ASSET_MFR']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -68,23 +51,26 @@ export function renderFacilityList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
filtered.forEach((asset, idx) => {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
+
+ const loc = asset[ASSET_SCHEMA.LOCATION.key] || '';
+ const detail = asset[ASSET_SCHEMA.LOC_DETAIL.key] || '';
+ const displayLoc = detail ? `${loc}(${detail})` : (loc || '-');
+
tr.innerHTML = `
-
${idx + 1}
${asset[ASSET_SCHEMA.HW_STATUS.key] || '보관중'}
${asset[ASSET_SCHEMA.ASSET_TYPE.key] || ''}
${asset[ASSET_SCHEMA.ASSET_MFR.key] || ''}
${formatInline(asset[ASSET_SCHEMA.MODEL_NAME.key] || '-')}
-
${asset[ASSET_SCHEMA.LOCATION.key] || '-'}
-
${asset[ASSET_SCHEMA.LOC_DETAIL.key] || '-'}
+
${displayLoc}
${asset[ASSET_SCHEMA.ASSET_COUNT.key] || '1'}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));
tbody.appendChild(tr);
@@ -95,14 +81,43 @@ export function renderFacilityList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
+ createIcons({ icons: { RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui})`,
+ showLoc: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Loc Options
+ const locSelect = container.querySelector('#filter-loc') as HTMLSelectElement;
+ if (locSelect) {
+ const locations = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.LOCATION.key]))).filter(Boolean).sort();
+ locations.forEach(loc => {
+ const opt = document.createElement('option');
+ opt.value = String(loc);
+ opt.textContent = String(loc);
+ locSelect.appendChild(opt);
+ });
+ }
+
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/List/GiftListView.ts b/src/views/List/GiftListView.ts
index 3d9ddc5..3c63cbd 100644
--- a/src/views/List/GiftListView.ts
+++ b/src/views/List/GiftListView.ts
@@ -1,29 +1,22 @@
import { state } from '../../core/state';
-import { formatInline, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, RefreshCcw, Plus } from 'lucide';
/**
* 선물(내빈/외빈) 자산 목록 뷰
*/
export function renderGiftList(container: HTMLElement) {
- // 선물 데이터는 equipment 또는 별도 테이블에 있을 수 있음.
+ renderPageHeader(container, '선물');
+
const fullList = sortAssets(state.masterData.equipment?.filter((a: any) => a.category === '선물') || []);
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', corp: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.PRODUCT_NAME.ui})
-
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -32,12 +25,11 @@ export function renderGiftList(container: HTMLElement) {
table.innerHTML = `
- No.
자산명
구매연월
- ${ASSET_SCHEMA.EXPIRY_DATE.ui}
+ ${ASSET_SCHEMA.EXPIRED_DATE.ui}
${ASSET_SCHEMA.ASSET_COUNT.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -48,14 +40,7 @@ export function renderGiftList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.PRODUCT_NAME.key]||asset[ASSET_SCHEMA.MODEL_NAME.key]||'').toLowerCase().includes(keyword);
- return matchKeyword;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['PRODUCT_NAME', 'MODEL_NAME']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -63,7 +48,7 @@ export function renderGiftList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
@@ -71,12 +56,11 @@ export function renderGiftList(container: HTMLElement) {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
tr.innerHTML = `
-
${idx + 1}
${formatInline(asset[ASSET_SCHEMA.PRODUCT_NAME.key] || asset[ASSET_SCHEMA.MODEL_NAME.key] || '-')}
${asset[ASSET_SCHEMA.PURCHASE_DATE.key] || ''}
-
${asset[ASSET_SCHEMA.EXPIRY_DATE.key] || ''}
+
${asset[ASSET_SCHEMA.EXPIRED_DATE.key] || ''}
${asset[ASSET_SCHEMA.ASSET_COUNT.key] || '1'}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => alert('상세 정보 준비 중입니다.'));
tbody.appendChild(tr);
@@ -87,14 +71,31 @@ export function renderGiftList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
+ createIcons({ icons: { RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.PRODUCT_NAME.ui})`,
+ showCorp: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/List/MobileListView.ts b/src/views/List/MobileListView.ts
index 79477cc..7f04760 100644
--- a/src/views/List/MobileListView.ts
+++ b/src/views/List/MobileListView.ts
@@ -1,37 +1,23 @@
import { state } from '../../core/state';
import { openHwModal } from '../../components/Modal/HWModal';
-import { formatInline, createBadge, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, Paperclip, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, Paperclip, RefreshCcw, Plus } from 'lucide';
/**
* 모바일 자산 목록 뷰 (레거시 지원용)
*/
export function renderMobileList(container: HTMLElement) {
- // 모바일 데이터가 별도 테이블에 없으므로 일단 빈 배열 또는 장비군에서 필터링 시도
+ renderPageHeader(container, 'PC');
+
const fullList = sortAssets(state.masterData.mobile || []);
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', corp: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
-
- const corps = Array.from(new Set(fullList.map((a: any) => a[ASSET_SCHEMA.PURCHASE_CORP.key]))).filter(Boolean).sort();
-
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui})
-
-
-
- ${ASSET_SCHEMA.PURCHASE_CORP.ui}
- 전체 법인 ${corps.map(c => `${c} `).join('')}
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -40,7 +26,6 @@ export function renderMobileList(container: HTMLElement) {
table.innerHTML = `
- No
${ASSET_SCHEMA.HW_STATUS.ui}
${ASSET_SCHEMA.PURCHASE_CORP.ui}
${ASSET_SCHEMA.MODEL_NAME.ui}
@@ -48,6 +33,7 @@ export function renderMobileList(container: HTMLElement) {
${ASSET_SCHEMA.PURCHASE_DATE.ui}
${ASSET_SCHEMA.PURCHASE_AMOUNT.ui}
담당자
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -58,18 +44,7 @@ export function renderMobileList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const corpSelect = document.getElementById('filter-corp') as HTMLSelectElement;
-
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
- const corp = corpSelect ? corpSelect.value : '';
-
- let filtered = fullList.filter((asset: any) => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.MODEL_NAME.key]||'').toLowerCase().includes(keyword);
- const matchCorp = !corp || asset[ASSET_SCHEMA.PURCHASE_CORP.key] === corp;
- return matchKeyword && matchCorp;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['MODEL_NAME']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -88,14 +63,14 @@ export function renderMobileList(container: HTMLElement) {
const mainManager = asset[ASSET_SCHEMA.MANAGER_MAIN.key] || '';
tr.innerHTML = `
-
${idx+1}
${asset[ASSET_SCHEMA.HW_STATUS.key] || '운영중'}
${asset[ASSET_SCHEMA.PURCHASE_CORP.key] || ''}
${asset[ASSET_SCHEMA.MODEL_NAME.key] || ''}
-
${asset[ASSET_SCHEMA.LOCATION.key] || '-'}
+
${(asset[ASSET_SCHEMA.LOCATION.key] || '') + (asset[ASSET_SCHEMA.LOC_DETAIL.key] ? `(${asset[ASSET_SCHEMA.LOC_DETAIL.key]})` : (asset[ASSET_SCHEMA.LOCATION.key] ? '' : '-'))}
${asset[ASSET_SCHEMA.PURCHASE_DATE.key] || ''}
${Number(asset[ASSET_SCHEMA.PURCHASE_AMOUNT.key]||0).toLocaleString()}
${mainManager}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));
tbody.appendChild(tr);
@@ -106,16 +81,31 @@ export function renderMobileList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { Paperclip, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
+ createIcons({ icons: { Paperclip, RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('filter-corp')?.addEventListener('change', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- (document.getElementById('filter-corp') as HTMLSelectElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui})`,
+ showCorp: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/List/NetworkListView.ts b/src/views/List/NetworkListView.ts
index 02d0ad0..dcab486 100644
--- a/src/views/List/NetworkListView.ts
+++ b/src/views/List/NetworkListView.ts
@@ -1,29 +1,23 @@
import { state } from '../../core/state';
import { openHwModal } from '../../components/Modal/HWModal';
-import { formatInline, createBadge, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, RefreshCcw, Plus } from 'lucide';
/**
* 네트워크 자산 목록 뷰
*/
export function renderNetworkList(container: HTMLElement) {
+ renderPageHeader(container, '네트워크');
+
const fullList = sortAssets(state.masterData.network || []);
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', loc: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui}/${ASSET_SCHEMA.MANAGER_MAIN.ui})
-
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -32,16 +26,14 @@ export function renderNetworkList(container: HTMLElement) {
table.innerHTML = `
- No.
${ASSET_SCHEMA.HW_STATUS.ui}
- 현 사용자
+ ${ASSET_SCHEMA.CURRENT_USER.ui}
${ASSET_SCHEMA.ASSET_TYPE.ui}
${ASSET_SCHEMA.ASSET_MFR.ui}
${ASSET_SCHEMA.MODEL_NAME.ui}
${ASSET_SCHEMA.ASSET_COUNT.ui}
- ${ASSET_SCHEMA.LOCATION.ui}(건물)
- ${ASSET_SCHEMA.LOC_DETAIL.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.LOCATION.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -52,16 +44,7 @@ export function renderNetworkList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.MODEL_NAME.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.MANAGER_MAIN.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.ASSET_MFR.key]||'').toLowerCase().includes(keyword);
- return matchKeyword;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['MODEL_NAME', 'CURRENT_USER', 'ASSET_MFR']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -69,24 +52,27 @@ export function renderNetworkList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
filtered.forEach((asset, idx) => {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
+
+ const loc = asset[ASSET_SCHEMA.LOCATION.key] || '';
+ const detail = asset[ASSET_SCHEMA.LOC_DETAIL.key] || '';
+ const displayLoc = detail ? `${loc}(${detail})` : (loc || '-');
+
tr.innerHTML = `
-
${idx + 1}
${asset[ASSET_SCHEMA.HW_STATUS.key] || '운영중'}
-
${asset[ASSET_SCHEMA.MANAGER_MAIN.key] || '-'}
+
${asset[ASSET_SCHEMA.CURRENT_USER.key] || '-'}
${asset[ASSET_SCHEMA.ASSET_TYPE.key] || ''}
${asset[ASSET_SCHEMA.ASSET_MFR.key] || ''}
${formatInline(asset[ASSET_SCHEMA.MODEL_NAME.key] || '-')}
${asset[ASSET_SCHEMA.ASSET_COUNT.key] || '1'}
-
${asset[ASSET_SCHEMA.LOCATION.key] || '-'}
-
${asset[ASSET_SCHEMA.LOC_DETAIL.key] || '-'}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${displayLoc}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));
tbody.appendChild(tr);
@@ -97,14 +83,43 @@ export function renderNetworkList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
+ createIcons({ icons: { RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui}/${ASSET_SCHEMA.ASSET_MFR.ui})`,
+ showLoc: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Location Options
+ const locSelect = container.querySelector('#filter-loc') as HTMLSelectElement;
+ if (locSelect) {
+ const locations = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.LOCATION.key]))).filter(Boolean).sort();
+ locations.forEach(loc => {
+ const opt = document.createElement('option');
+ opt.value = String(loc);
+ opt.textContent = String(loc);
+ locSelect.appendChild(opt);
+ });
+ }
+
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/List/PcListView.ts b/src/views/List/PcListView.ts
index 6882a1b..552973f 100644
--- a/src/views/List/PcListView.ts
+++ b/src/views/List/PcListView.ts
@@ -1,37 +1,21 @@
import { state } from '../../core/state';
import { openHwModal } from '../../components/Modal/HWModal';
-import { formatInline, createBadge, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, Paperclip, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, Paperclip, RefreshCcw, Plus } from 'lucide';
-/**
- * PC 자산 목록 뷰
- * 담당자(부) 추가 및 정렬 보정
- */
export function renderPcList(container: HTMLElement) {
- const fullList = sortAssets(state.masterData.pc);
+ renderPageHeader(container, 'PC');
+
+ // asset_pc 데이터 중 '서버PC' 유형은 제외하고 렌더링 (서버 리스트에서 보여줌)
+ const fullList = sortAssets((state.masterData.pc || []).filter((a: any) => a.asset_type !== '서버PC'));
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', corp: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
-
- const corps = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.PURCHASE_CORP.key]))).filter(Boolean).sort();
-
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui}/${ASSET_SCHEMA.MANAGER_MAIN.ui}/${ASSET_SCHEMA.CURRENT_USER.ui})
-
-
-
- ${ASSET_SCHEMA.PURCHASE_CORP.ui}
- 전체 법인 ${corps.map(c => `${c} `).join('')}
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -40,10 +24,7 @@ export function renderPcList(container: HTMLElement) {
table.innerHTML = `
- No
- ${ASSET_SCHEMA.CURRENT_DEPT.ui}
${ASSET_SCHEMA.CURRENT_USER.ui}
- ${ASSET_SCHEMA.MANAGER_MAIN.ui}
${ASSET_SCHEMA.CPU.ui}
${ASSET_SCHEMA.MAINBOARD.ui}
${ASSET_SCHEMA.RAM.ui}
@@ -52,8 +33,10 @@ export function renderPcList(container: HTMLElement) {
SSD2
HDD1
HDD2
+ HDD3
+ HDD4
${ASSET_SCHEMA.MAC_ADDR.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -64,22 +47,7 @@ export function renderPcList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const corpSelect = document.getElementById('filter-corp') as HTMLSelectElement;
-
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
- const corp = corpSelect ? corpSelect.value : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.MANAGER_MAIN.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.CURRENT_DEPT.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.MODEL_NAME.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.MAC_ADDR.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.CURRENT_USER.key]||'').toLowerCase().includes(keyword);
- const matchCorp = !corp || asset[ASSET_SCHEMA.PURCHASE_CORP.key] === corp;
- return matchKeyword && matchCorp;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['CURRENT_DEPT', 'CURRENT_USER', 'MODEL_NAME', 'MAC_ADDR', 'MANAGER_MAIN']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -87,7 +55,7 @@ export function renderPcList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
@@ -96,10 +64,7 @@ export function renderPcList(container: HTMLElement) {
tr.style.cursor = 'pointer';
tr.innerHTML = `
-
${idx+1}
-
${asset[ASSET_SCHEMA.CURRENT_DEPT.key]||'-'}
${asset[ASSET_SCHEMA.CURRENT_USER.key]||'-'}
-
${asset[ASSET_SCHEMA.MANAGER_MAIN.key]||'-'}
${asset[ASSET_SCHEMA.CPU.key]||''}
${asset[ASSET_SCHEMA.MAINBOARD.key]||'-'}
${asset[ASSET_SCHEMA.RAM.key]||''}
@@ -108,8 +73,10 @@ export function renderPcList(container: HTMLElement) {
${asset[ASSET_SCHEMA.SSD2.key]||'-'}
${asset[ASSET_SCHEMA.HDD1.key]||'-'}
${asset[ASSET_SCHEMA.HDD2.key]||'-'}
+
${asset[ASSET_SCHEMA.HDD3.key]||'-'}
+
${asset[ASSET_SCHEMA.HDD4.key]||'-'}
${asset[ASSET_SCHEMA.MAC_ADDR.key]||'-'}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));
tbody.appendChild(tr);
@@ -119,17 +86,42 @@ export function renderPcList(container: HTMLElement) {
sortState = { key, direction: dir };
updateTable();
});
-
- createIcons({ icons: { Paperclip, RefreshCcw } });
+ createIcons({ icons: { Paperclip, RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('filter-corp')?.addEventListener('change', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- (document.getElementById('filter-corp') as HTMLSelectElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui}/${ASSET_SCHEMA.MANAGER_MAIN.ui}/${ASSET_SCHEMA.CURRENT_USER.ui})`,
+ showLoc: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Loc Options
+ const locSelect = container.querySelector('#filter-loc') as HTMLSelectElement;
+ if (locSelect) {
+ const locations = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.LOCATION.key]))).filter(Boolean).sort();
+ locations.forEach(loc => {
+ const opt = document.createElement('option');
+ opt.value = String(loc);
+ opt.textContent = String(loc);
+ locSelect.appendChild(opt);
+ });
+ }
+
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key] || a['현사용부서'] || a['현사용조직']))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
diff --git a/src/views/List/PcPartListView.ts b/src/views/List/PcPartListView.ts
index 5f190eb..718accd 100644
--- a/src/views/List/PcPartListView.ts
+++ b/src/views/List/PcPartListView.ts
@@ -1,30 +1,23 @@
import { state } from '../../core/state';
import { openHwModal } from '../../components/Modal/HWModal';
-import { formatInline, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, RefreshCcw, Plus } from 'lucide';
/**
* PC부품 자산 목록 뷰
*/
export function renderPcPartList(container: HTMLElement) {
- // PC부품 데이터는 survey 또는 별도 테이블에 있을 수 있음. 여기선 equipment에서 필터링하거나 빈 배열 지원
+ renderPageHeader(container, 'PC부품');
+
const fullList = sortAssets(state.masterData.equipment?.filter((a: any) => a.category === 'PC부품') || []);
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', loc: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui})
-
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -33,7 +26,6 @@ export function renderPcPartList(container: HTMLElement) {
table.innerHTML = `
- No.
${ASSET_SCHEMA.HW_STATUS.ui}
${ASSET_SCHEMA.ASSET_TYPE.ui}
${ASSET_SCHEMA.ASSET_MFR.ui}
@@ -41,9 +33,8 @@ export function renderPcPartList(container: HTMLElement) {
${ASSET_SCHEMA.VOLUME.ui}
${ASSET_SCHEMA.MONITOR_INCH.ui}
${ASSET_SCHEMA.ASSET_COUNT.ui}
- ${ASSET_SCHEMA.LOCATION.ui}(건물)
- ${ASSET_SCHEMA.LOC_DETAIL.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.LOCATION.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -54,15 +45,7 @@ export function renderPcPartList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.MODEL_NAME.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.ASSET_TYPE.key]||'').toLowerCase().includes(keyword);
- return matchKeyword;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['MODEL_NAME', 'ASSET_TYPE']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -70,15 +53,19 @@ export function renderPcPartList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
filtered.forEach((asset, idx) => {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
+
+ const loc = asset[ASSET_SCHEMA.LOCATION.key] || '';
+ const detail = asset[ASSET_SCHEMA.LOC_DETAIL.key] || '';
+ const displayLoc = detail ? `${loc}(${detail})` : (loc || '-');
+
tr.innerHTML = `
-
${idx + 1}
${asset[ASSET_SCHEMA.HW_STATUS.key] || '보관중'}
${asset[ASSET_SCHEMA.ASSET_TYPE.key] || ''}
${asset[ASSET_SCHEMA.ASSET_MFR.key] || ''}
@@ -86,9 +73,8 @@ export function renderPcPartList(container: HTMLElement) {
${asset[ASSET_SCHEMA.VOLUME.key] || '-'}
${asset[ASSET_SCHEMA.MONITOR_INCH.key] || '-'}
${asset[ASSET_SCHEMA.ASSET_COUNT.key] || '1'}
-
${asset[ASSET_SCHEMA.LOCATION.key] || '-'}
-
${asset[ASSET_SCHEMA.LOC_DETAIL.key] || '-'}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${displayLoc}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));
tbody.appendChild(tr);
@@ -99,14 +85,43 @@ export function renderPcPartList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
+ createIcons({ icons: { RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui})`,
+ showLoc: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Location Options
+ const locSelect = container.querySelector('#filter-loc') as HTMLSelectElement;
+ if (locSelect) {
+ const locations = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.LOCATION.key]))).filter(Boolean).sort();
+ locations.forEach(loc => {
+ const opt = document.createElement('option');
+ opt.value = String(loc);
+ opt.textContent = String(loc);
+ locSelect.appendChild(opt);
+ });
+ }
+
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/List/ServerListView.ts b/src/views/List/ServerListView.ts
index 8c47a03..a9126db 100644
--- a/src/views/List/ServerListView.ts
+++ b/src/views/List/ServerListView.ts
@@ -1,42 +1,24 @@
import { state } from '../../core/state';
import { openHwModal } from '../../components/Modal/HWModal';
-import { formatInline, createBadge, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, RefreshCcw, Plus } from 'lucide';
-/**
- * 서버 자산 목록 뷰
- * 라인 정렬 보정 및 헤더 통일
- */
export function renderServerList(container: HTMLElement) {
- const fullList = sortAssets(state.masterData.server);
- let sortState: SortState = { key: '', direction: 'asc' };
+ renderPageHeader(container, '서버');
+
+ // asset_server 데이터와 asset_pc 데이터 중 '서버PC' 유형만 추출하여 병합
+ const serverList = state.masterData.server || [];
+ const serverPcList = (state.masterData.pc || []).filter((a: any) => a.asset_type === '서버PC');
+ const fullList = sortAssets([...serverList, ...serverPcList]);
+ let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', loc: '', dept: '', field: '' };
+
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
-
- const corps = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.PURCHASE_CORP.key]))).filter(Boolean).sort();
- const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
-
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.CURRENT_DEPT.ui}/${ASSET_SCHEMA.MODEL_NAME.ui})
-
-
-
- ${ASSET_SCHEMA.PURCHASE_CORP.ui}
- 전체 법인 ${corps.map(c => `${c} `).join('')}
-
-
- ${ASSET_SCHEMA.CURRENT_DEPT.ui}
- 전체 조직 ${orgUnits.map(o => `${o} `).join('')}
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -45,13 +27,11 @@ export function renderServerList(container: HTMLElement) {
table.innerHTML = `
- No
${ASSET_SCHEMA.CURRENT_DEPT.ui}
${ASSET_SCHEMA.ASSET_PURPOSE.ui}
- ${ASSET_SCHEMA.MODEL_NAME.ui}
- ${ASSET_SCHEMA.LOCATION.ui}(건물)
- ${ASSET_SCHEMA.LOC_DETAIL.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.MODEL_NAME.ui}
+ ${ASSET_SCHEMA.LOCATION.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -62,23 +42,7 @@ export function renderServerList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const corpSelect = document.getElementById('filter-corp') as HTMLSelectElement;
- const orgSelect = document.getElementById('filter-org-unit') as HTMLSelectElement;
-
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
- const corp = corpSelect ? corpSelect.value : '';
- const orgUnit = orgSelect ? orgSelect.value : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.CURRENT_DEPT.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.MODEL_NAME.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.ASSET_PURPOSE.key]||'').toLowerCase().includes(keyword);
- const matchCorp = !corp || asset[ASSET_SCHEMA.PURCHASE_CORP.key] === corp;
- const matchOrg = !orgUnit || asset[ASSET_SCHEMA.CURRENT_DEPT.key] === orgUnit;
- return matchKeyword && matchCorp && matchOrg;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['CURRENT_DEPT', 'MODEL_NAME', 'ASSET_PURPOSE']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -86,7 +50,7 @@ export function renderServerList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
@@ -94,14 +58,16 @@ export function renderServerList(container: HTMLElement) {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
+ const loc = asset[ASSET_SCHEMA.LOCATION.key] || '';
+ const detail = asset[ASSET_SCHEMA.LOC_DETAIL.key] || '';
+ const displayLoc = detail ? `${loc}(${detail})` : (loc || '-');
+
tr.innerHTML = `
-
${idx+1}
${asset[ASSET_SCHEMA.CURRENT_DEPT.key]||'-'}
-
${formatInline(asset[ASSET_SCHEMA.ASSET_PURPOSE.key])}
+
${formatInline(asset[ASSET_SCHEMA.ASSET_PURPOSE.key]||'-')}
${formatInline(asset[ASSET_SCHEMA.MODEL_NAME.key]||asset[ASSET_SCHEMA.ASSET_NAME.key]||'-')}
-
${formatInline(asset[ASSET_SCHEMA.LOCATION.key])}
-
${formatInline(asset[ASSET_SCHEMA.LOC_DETAIL.key]||'-')}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${displayLoc}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));
tbody.appendChild(tr);
@@ -111,18 +77,42 @@ export function renderServerList(container: HTMLElement) {
sortState = { key, direction: dir };
updateTable();
});
+ createIcons({ icons: { RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('filter-corp')?.addEventListener('change', updateTable);
- document.getElementById('filter-org-unit')?.addEventListener('change', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- (document.getElementById('filter-corp') as HTMLSelectElement).value = '';
- (document.getElementById('filter-org-unit') as HTMLSelectElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.CURRENT_DEPT.ui}/${ASSET_SCHEMA.MODEL_NAME.ui})`,
+ showLoc: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Location Options
+ const locSelect = container.querySelector('#filter-loc') as HTMLSelectElement;
+ if (locSelect) {
+ const locations = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.LOCATION.key]))).filter(Boolean).sort();
+ locations.forEach(loc => {
+ const opt = document.createElement('option');
+ opt.value = String(loc);
+ opt.textContent = String(loc);
+ locSelect.appendChild(opt);
+ });
+ }
+
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = dept;
+ opt.textContent = dept;
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
- createIcons({ icons: { RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
}
diff --git a/src/views/List/SpaceInfoListView.ts b/src/views/List/SpaceInfoListView.ts
index 96d1106..58225b0 100644
--- a/src/views/List/SpaceInfoListView.ts
+++ b/src/views/List/SpaceInfoListView.ts
@@ -1,30 +1,23 @@
import { state } from '../../core/state';
import { openHwModal } from '../../components/Modal/HWModal';
-import { formatInline, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, RefreshCcw, Plus } from 'lucide';
/**
* 공간정보장비 자산 목록 뷰
*/
export function renderSpaceInfoList(container: HTMLElement) {
- // 공간정보장비 데이터는 survey 또는 별도 테이블에 있을 수 있음.
+ renderPageHeader(container, '공간정보장비');
+
const fullList = sortAssets(state.masterData.equipment?.filter((a: any) => a.category === '공간정보장비') || []);
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', loc: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui}/${ASSET_SCHEMA.MANAGER_MAIN.ui})
-
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -33,14 +26,12 @@ export function renderSpaceInfoList(container: HTMLElement) {
table.innerHTML = `
- No.
${ASSET_SCHEMA.HW_STATUS.ui}
- 현 사용자
- 자산명
+ ${ASSET_SCHEMA.CURRENT_USER.ui}
+ ${ASSET_SCHEMA.ASSET_NAME.ui}
${ASSET_SCHEMA.ASSET_TYPE.ui}
- ${ASSET_SCHEMA.LOCATION.ui}(건물)
- ${ASSET_SCHEMA.LOC_DETAIL.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.LOCATION.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -51,15 +42,7 @@ export function renderSpaceInfoList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.MODEL_NAME.key]||asset[ASSET_SCHEMA.PRODUCT_NAME.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.MANAGER_MAIN.key]||'').toLowerCase().includes(keyword);
- return matchKeyword;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['MODEL_NAME', 'PRODUCT_NAME', 'CURRENT_USER']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -67,22 +50,25 @@ export function renderSpaceInfoList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
filtered.forEach((asset, idx) => {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
+
+ const loc = asset[ASSET_SCHEMA.LOCATION.key] || '';
+ const detail = asset[ASSET_SCHEMA.LOC_DETAIL.key] || '';
+ const displayLoc = detail ? `${loc}(${detail})` : (loc || '-');
+
tr.innerHTML = `
-
${idx + 1}
${asset[ASSET_SCHEMA.HW_STATUS.key] || '운영중'}
-
${asset[ASSET_SCHEMA.MANAGER_MAIN.key] || '-'}
-
${formatInline(asset[ASSET_SCHEMA.PRODUCT_NAME.key] || asset[ASSET_SCHEMA.MODEL_NAME.key] || '-')}
+
${asset[ASSET_SCHEMA.CURRENT_USER.key] || '-'}
+
${formatInline(asset[ASSET_SCHEMA.PRODUCT_NAME.key] || asset[ASSET_SCHEMA.MODEL_NAME.key] || asset[ASSET_SCHEMA.ASSET_NAME.key] || '-')}
${asset[ASSET_SCHEMA.ASSET_TYPE.key] || ''}
-
${asset[ASSET_SCHEMA.LOCATION.key] || '-'}
-
${asset[ASSET_SCHEMA.LOC_DETAIL.key] || '-'}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${displayLoc}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));
tbody.appendChild(tr);
@@ -93,14 +79,43 @@ export function renderSpaceInfoList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
+ createIcons({ icons: { RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui}/${ASSET_SCHEMA.CURRENT_USER.ui})`,
+ showLoc: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Location Options
+ const locSelect = container.querySelector('#filter-loc') as HTMLSelectElement;
+ if (locSelect) {
+ const locations = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.LOCATION.key]))).filter(Boolean).sort();
+ locations.forEach(loc => {
+ const opt = document.createElement('option');
+ opt.value = String(loc);
+ opt.textContent = String(loc);
+ locSelect.appendChild(opt);
+ });
+ }
+
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/List/StorageListView.ts b/src/views/List/StorageListView.ts
index 0f6b684..b3e1647 100644
--- a/src/views/List/StorageListView.ts
+++ b/src/views/List/StorageListView.ts
@@ -1,42 +1,23 @@
import { state } from '../../core/state';
import { openHwModal } from '../../components/Modal/HWModal';
-import { formatInline, createBadge, sortAssets, dynamicSort, getActionButtonsHTML } from '../../core/utils';
+import { formatInline, sortAssets, dynamicSort, renderPageHeader } from '../../core/utils';
import { ASSET_SCHEMA, UI_TEXT } from '../../core/schema';
import { setupTableSorting, SortState } from '../../core/tableHandler';
-import { createIcons, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, RefreshCcw, Plus } from 'lucide';
/**
* 스토리지 자산 목록 뷰
- * 라인 정렬 보정 및 헤더 통일
*/
export function renderStorageList(container: HTMLElement) {
+ renderPageHeader(container, '스토리지');
+
const fullList = sortAssets(state.masterData.storage);
let sortState: SortState = { key: '', direction: 'asc' };
-
+ let currentFilters = { keyword: '', loc: '', dept: '', field: '' };
+
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
-
- const corps = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.PURCHASE_CORP.key]))).filter(Boolean).sort();
- const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
-
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.CURRENT_DEPT.ui})
-
-
-
- ${ASSET_SCHEMA.PURCHASE_CORP.ui}
- 전체 법인 ${corps.map(c => `${c} `).join('')}
-
-
- ${ASSET_SCHEMA.CURRENT_DEPT.ui}
- 전체 조직 ${orgUnits.map(o => `${o} `).join('')}
-
-
- ${UI_TEXT.ACTION.RESET_FILTER}
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -45,43 +26,25 @@ export function renderStorageList(container: HTMLElement) {
table.innerHTML = `
- No
${ASSET_SCHEMA.HW_STATUS.ui}
- 현 사용자
+ ${ASSET_SCHEMA.CURRENT_USER.ui}
${ASSET_SCHEMA.ASSET_TYPE.ui}
${ASSET_SCHEMA.VOLUME.ui}
${ASSET_SCHEMA.MODEL_NAME.ui}
${ASSET_SCHEMA.SERIAL_NUM.ui}
- ${ASSET_SCHEMA.LOCATION.ui}(건물)
- ${ASSET_SCHEMA.LOC_DETAIL.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.LOCATION.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
`;
-
+
tableWrapper.appendChild(table);
container.appendChild(tableWrapper);
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const corpSelect = document.getElementById('filter-corp') as HTMLSelectElement;
- const orgSelect = document.getElementById('filter-org-unit') as HTMLSelectElement;
-
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
- const corp = corpSelect ? corpSelect.value : '';
- const orgUnit = orgSelect ? orgUnit.value : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- String(asset[ASSET_SCHEMA.MODEL_NAME.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.MANAGER_MAIN.key]||'').toLowerCase().includes(keyword) ||
- String(asset[ASSET_SCHEMA.SERIAL_NUM.key]||'').toLowerCase().includes(keyword);
- const matchCorp = !corp || asset[ASSET_SCHEMA.PURCHASE_CORP.key] === corp;
- const matchOrg = !orgUnit || asset[ASSET_SCHEMA.CURRENT_DEPT.key] === orgUnit;
- return matchKeyword && matchCorp && matchOrg;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['MODEL_NAME', 'CURRENT_USER', 'SERIAL_NUM']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -89,25 +52,27 @@ export function renderStorageList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
+ tbody.innerHTML = `
${UI_TEXT.MESSAGES.NO_DATA} `;
return;
}
filtered.forEach((asset, idx) => {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
-
+
+ const loc = asset[ASSET_SCHEMA.LOCATION.key] || '';
+ const detail = asset[ASSET_SCHEMA.LOC_DETAIL.key] || '';
+ const displayLoc = detail ? `${loc}(${detail})` : (loc || '-');
+
tr.innerHTML = `
-
${idx+1}
${asset[ASSET_SCHEMA.HW_STATUS.key]||'-'}
-
${asset[ASSET_SCHEMA.MANAGER_MAIN.key]||'-'}
+
${asset[ASSET_SCHEMA.CURRENT_USER.key]||'-'}
${asset[ASSET_SCHEMA.ASSET_TYPE.key]||'-'}
${asset[ASSET_SCHEMA.VOLUME.key]||'-'}
${formatInline(asset[ASSET_SCHEMA.MODEL_NAME.key]||asset[ASSET_SCHEMA.ASSET_NAME.key]||'-')}
${asset[ASSET_SCHEMA.SERIAL_NUM.key]||'-'}
-
${formatInline(asset[ASSET_SCHEMA.LOCATION.key])}
-
${formatInline(asset[ASSET_SCHEMA.LOC_DETAIL.key]||'-')}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${displayLoc}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));
tbody.appendChild(tr);
@@ -117,18 +82,42 @@ export function renderStorageList(container: HTMLElement) {
sortState = { key, direction: dir };
updateTable();
});
+ createIcons({ icons: { RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('filter-corp')?.addEventListener('change', updateTable);
- document.getElementById('filter-org-unit')?.addEventListener('change', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- (document.getElementById('filter-corp') as HTMLSelectElement).value = '';
- (document.getElementById('filter-org-unit') as HTMLSelectElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.MODEL_NAME.ui}/${ASSET_SCHEMA.CURRENT_USER.ui})`,
+ showLoc: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Location Options
+ const locSelect = container.querySelector('#filter-loc') as HTMLSelectElement;
+ if (locSelect) {
+ const locations = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.LOCATION.key]))).filter(Boolean).sort();
+ locations.forEach(loc => {
+ const opt = document.createElement('option');
+ opt.value = String(loc);
+ opt.textContent = String(loc);
+ locSelect.appendChild(opt);
+ });
+ }
+
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
- createIcons({ icons: { RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
}
diff --git a/src/views/List/SwListView.ts b/src/views/List/SwListView.ts
index 6899f60..2bdfe01 100644
--- a/src/views/List/SwListView.ts
+++ b/src/views/List/SwListView.ts
@@ -1,45 +1,22 @@
import { state } from '../../core/state';
import { openSwModal } from '../../components/Modal/SWModal';
-import { openSwUserModal } from '../../components/Modal/SWUserModal';
-import { sortAssets, dynamicSort, formatPrice, getActionButtonsHTML } from '../../core/utils';
+import { sortAssets, dynamicSort, formatInline, getActionButtonsHTML, renderPageHeader } from '../../core/utils';
import { setupTableSorting, SortState } from '../../core/tableHandler';
import { ASSET_SCHEMA } from '../../core/schema';
-import { CORP_LIST } from '../../components/Modal/SharedData';
-import { generateOptionsHTML } from '../../components/Modal/ModalUtils';
-import { createIcons, Edit2, Users, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } from 'lucide';
+import { renderFilterBar, applyCommonFilters } from '../../core/filterHandler';
+import { createIcons, Edit2, Users, RefreshCcw, Plus } from 'lucide';
export function renderSwList(container: HTMLElement) {
const isInternal = state.activeSubTab === '내부';
+ renderPageHeader(container, isInternal ? '내부' : '외부');
+
const fullList = sortAssets(isInternal ? state.masterData.swInternal : state.masterData.swExternal);
let sortState: SortState = { key: '', direction: 'asc' };
+ let currentFilters = { keyword: '', corp: '', dept: '', field: '' };
const filterBar = document.createElement('div');
filterBar.className = 'search-bar';
- filterBar.innerHTML = `
-
- 통합 검색 (${ASSET_SCHEMA.PRODUCT_NAME.ui}/${ASSET_SCHEMA.CURRENT_DEPT.ui})
-
-
-
- ${ASSET_SCHEMA.SW_FIELD.ui}
-
- 전체 분야
- 업무공통
- 개발S/W
- 디자인
- 설계S/W
-
-
-
- ${ASSET_SCHEMA.PURCHASE_CORP.ui}
- ${generateOptionsHTML(CORP_LIST, '', true)}
-
-
- 필터 초기화
-
- ${getActionButtonsHTML()}
- `;
container.appendChild(filterBar);
const tableWrapper = document.createElement('div');
@@ -50,12 +27,11 @@ export function renderSwList(container: HTMLElement) {
table.innerHTML = `
- No.
${ASSET_SCHEMA.SW_FIELD.ui}
${ASSET_SCHEMA.DEV_OBJ.ui}
${ASSET_SCHEMA.SW_STATUS.ui}
${ASSET_SCHEMA.SW_TYPE.ui}
- ${ASSET_SCHEMA.MEMO.ui}
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -64,17 +40,17 @@ export function renderSwList(container: HTMLElement) {
table.innerHTML = `
- No.
자산명
유형
${ASSET_SCHEMA.SW_STATUS.ui}
${ASSET_SCHEMA.SW_FIELD.ui}
${ASSET_SCHEMA.CURRENT_DEPT.ui}
- 현 사용자
+ ${ASSET_SCHEMA.CURRENT_USER.ui}
+ ${ASSET_SCHEMA.PREV_USER.ui}
구매연월
시작일
- 만료일
- ${ASSET_SCHEMA.MEMO.ui}
+ 만료일
+ ${ASSET_SCHEMA.MEMO.ui}
@@ -86,22 +62,7 @@ export function renderSwList(container: HTMLElement) {
const tbody = table.querySelector('tbody')!;
const updateTable = () => {
- const keywordInput = document.getElementById('filter-keyword') as HTMLInputElement;
- const fieldSelect = document.getElementById('filter-field') as HTMLSelectElement;
- const corpSelect = document.getElementById('filter-corp') as HTMLSelectElement;
-
- const keyword = keywordInput ? keywordInput.value.toLowerCase().trim() : '';
- const field = fieldSelect ? fieldSelect.value : '';
- const corp = corpSelect ? corpSelect.value : '';
-
- let filtered = fullList.filter(asset => {
- const matchKeyword = !keyword ||
- (asset[ASSET_SCHEMA.PRODUCT_NAME.key] || '').toLowerCase().includes(keyword) ||
- (asset[ASSET_SCHEMA.CURRENT_DEPT.key] || '').toLowerCase().includes(keyword);
- const matchField = !field || asset[ASSET_SCHEMA.SW_FIELD.key] === field;
- const matchCorp = !corp || asset[ASSET_SCHEMA.PURCHASE_CORP.key] === corp;
- return matchKeyword && matchField && matchCorp;
- });
+ let filtered = applyCommonFilters(fullList, currentFilters, ['PRODUCT_NAME', 'CURRENT_USER', 'CURRENT_DEPT']);
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
@@ -109,7 +70,7 @@ export function renderSwList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
- tbody.innerHTML = `
검색 결과가 없습니다. `;
+ tbody.innerHTML = `
검색 결과가 없습니다. `;
return;
}
@@ -118,33 +79,30 @@ export function renderSwList(container: HTMLElement) {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
tr.innerHTML = `
-
${idx+1}
${asset[ASSET_SCHEMA.SW_FIELD.key]||''}
${asset[ASSET_SCHEMA.DEV_OBJ.key]||''}
${asset[ASSET_SCHEMA.SW_STATUS.key]||'보유중'}
${asset[ASSET_SCHEMA.SW_TYPE.key]||'내부'}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openSwModal(asset, 'view'));
tbody.appendChild(tr);
} else {
const tr = document.createElement('tr');
tr.style.cursor = 'pointer';
- const users = state.masterData.swUsers.filter(u => u.sw_id === asset.id);
- const userText = users.length > 0 ? `${users[0].user_name}${users.length > 1 ? ' 외 ' + (users.length - 1) : ''}` : '-';
-
+
tr.innerHTML = `
-
${idx+1}
${asset[ASSET_SCHEMA.PRODUCT_NAME.key]||''}
${asset[ASSET_SCHEMA.ASSET_TYPE.key]||'외부'}
${asset[ASSET_SCHEMA.SW_STATUS.key]||'사용중'}
${asset[ASSET_SCHEMA.SW_FIELD.key]||''}
${asset[ASSET_SCHEMA.CURRENT_DEPT.key]||''}
-
${userText}
+
${asset[ASSET_SCHEMA.CURRENT_USER.key]||'-'}
+
${asset[ASSET_SCHEMA.PREV_USER.key]||'-'}
${asset[ASSET_SCHEMA.PURCHASE_DATE.key]||''}
${asset[ASSET_SCHEMA.PURCHASE_DATE.key]||''}
-
${asset[ASSET_SCHEMA.EXPIRY_DATE.key]||''}
-
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
+
${asset[ASSET_SCHEMA.EXPIRED_DATE.key]||''}
+
${formatInline(asset[ASSET_SCHEMA.MEMO.key]||'-')}
`;
tr.addEventListener('click', () => openSwModal(asset, 'view'));
tbody.appendChild(tr);
@@ -156,18 +114,32 @@ export function renderSwList(container: HTMLElement) {
updateTable();
});
- createIcons({ icons: { Edit2, Users, RefreshCcw, Download, Upload, FileSpreadsheet, Plus } });
+ createIcons({ icons: { Edit2, Users, RefreshCcw, Plus } });
};
- document.getElementById('filter-keyword')?.addEventListener('input', updateTable);
- document.getElementById('filter-field')?.addEventListener('change', updateTable);
- document.getElementById('filter-corp')?.addEventListener('change', updateTable);
- document.getElementById('btn-reset-filters')?.addEventListener('click', () => {
- (document.getElementById('filter-keyword') as HTMLInputElement).value = '';
- (document.getElementById('filter-field') as HTMLSelectElement).value = '';
- (document.getElementById('filter-corp') as HTMLSelectElement).value = '';
- updateTable();
+ renderFilterBar(filterBar, {
+ keywordLabel: `통합 검색 (${ASSET_SCHEMA.PRODUCT_NAME.ui}/${ASSET_SCHEMA.CURRENT_DEPT.ui})`,
+ showField: true,
+ showCorp: true,
+ showDept: true,
+ onFilterChange: (filters) => {
+ currentFilters = filters;
+ updateTable();
+ }
});
+ // Populate Dept Options
+ const deptSelect = container.querySelector('#filter-dept') as HTMLSelectElement;
+ if (deptSelect) {
+ const orgUnits = Array.from(new Set(fullList.map(a => a[ASSET_SCHEMA.CURRENT_DEPT.key]))).filter(Boolean).sort();
+ orgUnits.forEach(dept => {
+ const opt = document.createElement('option');
+ opt.value = String(dept);
+ opt.textContent = String(dept);
+ deptSelect.appendChild(opt);
+ });
+ }
+
updateTable();
}
+
diff --git a/src/views/SW_Table.ts b/src/views/SW_Table.ts
index dd94b86..cc15dfc 100644
--- a/src/views/SW_Table.ts
+++ b/src/views/SW_Table.ts
@@ -13,7 +13,8 @@ import { renderSpaceInfoList } from './List/SpaceInfoListView';
import { renderGiftList } from './List/GiftListView';
import { renderFacilityList } from './List/FacilityListView';
import { renderCostList } from './List/CostListView';
-import { createIcons, Download, Upload, FileSpreadsheet, Plus, X, LayoutDashboard, Monitor, Server, Database, Laptop, CalendarClock, Key, Cpu, Layers, Users, Paperclip, Edit2, RefreshCcw } from 'lucide';
+import { renderUserList } from './List/UserListView';
+import { createIcons, Plus, X, LayoutDashboard, Monitor, Server, Database, Laptop, CalendarClock, Key, Cpu, Layers, Users, Paperclip, Edit2, RefreshCcw } from 'lucide';
/**
* 자산 목록 테이블 렌더링 통합 허브
@@ -69,7 +70,7 @@ export function renderSWTable(mainContent: HTMLElement) {
// 전역 아이콘 초기화 (한 번 더 실행하여 누락 방지)
createIcons({
- icons: { Download, Upload, FileSpreadsheet, Plus, X, LayoutDashboard, Monitor, Server, Database, Laptop, CalendarClock, Key, Cpu, Layers, Users, Paperclip, Edit2, RefreshCcw }
+ icons: { Plus, X, LayoutDashboard, Monitor, Server, Database, Laptop, CalendarClock, Key, Cpu, Layers, Users, Paperclip, Edit2, RefreshCcw }
});
} catch (err: any) {
console.error('❌ Error rendering table view:', err);