Fix: Excel upload logic, field mapping for servers, and date format synchronization

This commit is contained in:
2026-04-27 11:12:20 +09:00
parent 8f0508a7d0
commit 68cb5f9767
10 changed files with 205 additions and 91 deletions

View File

@@ -1,13 +1,16 @@
import { state } from '../../core/state';
import { formatPrice, dynamicSort } from '../../core/utils';
import { formatPrice, dynamicSort, createBadge } from '../../core/utils';
import { createIcons, Plus, Edit2, Trash2 } from 'lucide';
import { openDomainModal } from '../../components/Modal/DomainModal';
import { setupTableSorting, SortState } from '../../core/tableHandler';
import { formatExcelDate } from '../../core/excelHandler';
// 정렬 상태를 모듈 수준에서 관리하여 화면 갱신 시에도 유지되도록 함
let persistentSortState: SortState = { key: '', direction: 'asc' };
export function renderDomainList(container: HTMLElement) {
container.innerHTML = '';
let sortState: SortState = { key: '', direction: 'asc' };
const fullList = state.masterData.domain;
const header = document.createElement('div');
@@ -30,12 +33,11 @@ export function renderDomainList(container: HTMLElement) {
<th style="text-align:center;" data-sort="corp">법인</th>
<th style="text-align:left;" data-sort="service_name">서비스명</th>
<th style="text-align:left;" data-sort="domain_name">관리도메인</th>
<th style="text-align:left;" data-sort="remarks">구매업체</th>
<th style="text-align:center;" data-sort="start_date">시작일</th>
<th style="text-align:center;" data-sort="expiry_date">만료일</th>
<th style="text-align:right;" data-sort="price">금액</th>
<th style="text-align:center;" data-sort="manager_main">담당자</th>
<th style="text-align:center;" data-sort="manager_sub">담당자(부)</th>
<th style="text-align:left;">비고</th>
<th style="text-align:center;" data-sort="manager_main">담당자(정/부)</th>
</tr>
</thead>
<tbody id="dynamic-tbody"></tbody>
@@ -48,13 +50,13 @@ export function renderDomainList(container: HTMLElement) {
const updateTable = () => {
let filtered = [...fullList];
if (sortState.key) {
filtered = dynamicSort(filtered, sortState.key, sortState.direction);
if (persistentSortState.key) {
filtered = dynamicSort(filtered, persistentSortState.key, persistentSortState.direction);
}
tbody.innerHTML = '';
if (filtered.length === 0) {
tbody.innerHTML = `<tr><td colspan="11" style="text-align:center; padding: 3rem; color: var(--text-muted);">등록된 도메인 정보가 없습니다.</td></tr>`;
tbody.innerHTML = `<tr><td colspan="10" style="text-align:center; padding: 3rem; color: var(--text-muted);">등록된 도메인 정보가 없습니다.</td></tr>`;
return;
}
@@ -62,25 +64,32 @@ export function renderDomainList(container: HTMLElement) {
const tr = document.createElement('tr');
tr.className = 'domain-row';
tr.style.cursor = 'pointer';
const managerHtml = [
item.manager_main ? `${createBadge('정', 'primary')} ${item.manager_main}` : '',
item.manager_sub ? `${createBadge('부', 'muted')} ${item.manager_sub}` : ''
].filter(v => v !== '').join(' / ');
tr.innerHTML = `
<td style="text-align:center;">${idx + 1}</td>
<td style="text-align:center;"><span class="badge badge-${item.type}">${item.type}</span></td>
<td style="text-align:center;">${item.corp || ''}</td>
<td>${item.service_name || ''}</td>
<td>${item.domain_name || ''}</td>
<td style="text-align:center;">${item.start_date || ''}</td>
<td style="text-align:center;">${item.expiry_date || ''}</td>
<td>${item.remarks || ''}</td>
<td style="text-align:center;">${formatExcelDate(item.start_date)}</td>
<td style="text-align:center;">${formatExcelDate(item.expiry_date)}</td>
<td style="text-align:right;">${formatPrice(item.price)}</td>
<td style="text-align:center;">${item.manager_main || ''}</td>
<td style="text-align:center;">${item.manager_sub || ''}</td>
<td class="text-truncate" style="max-width:200px;">${item.remarks || ''}</td>
<td style="text-align:center;">${managerHtml || '-'}</td>
`;
tr.addEventListener('click', () => openDomainModal(item));
tr.addEventListener('click', (e) => {
console.log('Row clicked:', item.domain_name);
openDomainModal(item);
});
tbody.appendChild(tr);
});
setupTableSorting(table, sortState, (key, dir) => {
sortState = { key, direction: dir };
setupTableSorting(table, persistentSortState, (key, dir) => {
persistentSortState = { key, direction: dir };
updateTable();
});
};

View File

@@ -44,8 +44,6 @@ export function renderPcList(container: HTMLElement) {
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.ORG.key}">${ASSET_SCHEMA.ORG.ui}</th>
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.ASSET_CODE.key}">${ASSET_SCHEMA.ASSET_CODE.ui}</th>
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.USER.key}">${ASSET_SCHEMA.USER.ui}</th>
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.LOCATION.key}">${ASSET_SCHEMA.LOCATION.ui}</th>
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.MANAGER_MAIN.key}">담당자(정/부)</th>
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.MAINBOARD.key}">${ASSET_SCHEMA.MAINBOARD.ui}</th>
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.CPU.key}">${ASSET_SCHEMA.CPU.ui}</th>
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.RAM.key}">${ASSET_SCHEMA.RAM.ui}</th>
@@ -53,6 +51,7 @@ export function renderPcList(container: HTMLElement) {
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.PURCHASE_YM.key}">${ASSET_SCHEMA.PURCHASE_YM.ui}</th>
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.PRICE.key}">${ASSET_SCHEMA.PRICE.ui}</th>
<th style="text-align:center;">${ASSET_SCHEMA.DOC_NAME.ui}</th>
<th style="text-align:center;" data-sort="${ASSET_SCHEMA.MANAGER_MAIN.key}">담당자(정/부)</th>
</tr>
</thead>
<tbody id="dynamic-tbody"></tbody>
@@ -85,7 +84,7 @@ export function renderPcList(container: HTMLElement) {
tbody.innerHTML = '';
if (filtered.length === 0) {
tbody.innerHTML = `<tr><td colspan="14" style="text-align:center; padding: 3rem; color: var(--text-muted);">${UI_TEXT.MESSAGES.NO_DATA}</td></tr>`;
tbody.innerHTML = `<tr><td colspan="13" style="text-align:center; padding: 3rem; color: var(--text-muted);">${UI_TEXT.MESSAGES.NO_DATA}</td></tr>`;
return;
}
@@ -108,8 +107,6 @@ export function renderPcList(container: HTMLElement) {
<td style="text-align:center;">${asset[ASSET_SCHEMA.ORG.key]||'-'}</td>
<td style="text-align:center;">${asset[ASSET_SCHEMA.ASSET_CODE.key]}</td>
<td style="text-align:center;">${asset[ASSET_SCHEMA.USER.key]||''}</td>
<td style="text-align:center;">${asset[ASSET_SCHEMA.LOCATION.key]||''}</td>
<td style="text-align:center;">${managerHtml || '-'}</td>
<td style="text-align:center;">${asset[ASSET_SCHEMA.MAINBOARD.key]||'-'}</td>
<td style="text-align:center;">${asset[ASSET_SCHEMA.CPU.key]||''}</td>
<td style="text-align:center;">${asset[ASSET_SCHEMA.RAM.key]||''}</td>
@@ -117,6 +114,7 @@ export function renderPcList(container: HTMLElement) {
<td style="text-align:center;">${asset[ASSET_SCHEMA.PURCHASE_YM.key] || ''}</td>
<td style="text-align:right;">${Number(asset[ASSET_SCHEMA.PRICE.key]||0).toLocaleString()}</td>
<td style="text-align:center;">${asset[ASSET_SCHEMA.DOC_NAME.key] ? '<i data-lucide="paperclip" class="text-primary"></i>' : '-'}</td>
<td style="text-align:center;">${managerHtml || '-'}</td>
`;
tr.addEventListener('click', () => openHwModal(asset, 'view'));
tbody.appendChild(tr);

View File

@@ -48,7 +48,7 @@ export function renderServerList(container: HTMLElement) {
<th class="text-center" data-sort="${ASSET_SCHEMA.CORP.key}">${ASSET_SCHEMA.CORP.ui}</th>
<th class="text-center" data-sort="${ASSET_SCHEMA.ORG.key}">${ASSET_SCHEMA.ORG.ui}</th>
<th class="text-center" data-sort="${ASSET_SCHEMA.ASSET_CODE.key}">${ASSET_SCHEMA.ASSET_CODE.ui}</th>
<th data-sort="용도">용도</th>
<th data-sort="${ASSET_SCHEMA.DETAIL_PURPOSE.key}">${ASSET_SCHEMA.DETAIL_PURPOSE.ui}</th>
<th data-sort="상세">상세</th>
<th class="text-center" data-sort="${ASSET_SCHEMA.LOCATION.key}">${ASSET_SCHEMA.LOCATION.ui}</th>
<th class="text-center" data-sort="${ASSET_SCHEMA.MANAGER_MAIN.key}">담당자(정/부)</th>
@@ -106,7 +106,7 @@ export function renderServerList(container: HTMLElement) {
<td class="text-center">${asset[ASSET_SCHEMA.CORP.key]}</td>
<td class="text-center">${asset[ASSET_SCHEMA.ORG.key]||'-'}</td>
<td class="text-center">${asset[ASSET_SCHEMA.ASSET_CODE.key]}</td>
<td>${formatInline(asset.)}</td>
<td>${formatInline(asset[ASSET_SCHEMA.DETAIL_PURPOSE.key])}</td>
<td>${formatInline(asset.)}</td>
<td class="text-center">${formatInline(asset[ASSET_SCHEMA.LOCATION.key])}</td>
<td class="text-center">${managerHtml || '-'}</td>