343 lines
15 KiB
TypeScript
343 lines
15 KiB
TypeScript
import { state } from '../../core/state';
|
|
import { HardwareAsset, HardwareLog } from '../../core/excelHandler';
|
|
import { openModal } from './BaseModal';
|
|
|
|
const PC_MODAL_HTML = `
|
|
<div id="pc-asset-modal" class="modal-overlay hidden">
|
|
<div class="modal-content wide">
|
|
<div class="modal-header">
|
|
<h2 id="pc-modal-title">개인PC 상세 정보</h2>
|
|
<button id="btn-close-pc-modal" class="btn-icon" aria-label="닫기"><i data-lucide="x"></i></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="modal-body-split">
|
|
<div class="modal-form-area">
|
|
<form id="pc-asset-form" class="grid-form">
|
|
<input type="hidden" id="pc-asset-id" />
|
|
<input type="hidden" id="pc-asset-type" value="개인PC" />
|
|
<div class="form-group">
|
|
<label for="pc-법인">법인</label>
|
|
<select id="pc-법인" required>
|
|
<option value="한맥">한맥 (HM)</option><option value="삼안">삼안 (SM)</option><option value="바론">바론 (BR)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-자산코드">자산코드</label>
|
|
<input type="text" id="pc-자산코드" placeholder="ex) HM-PC-2018-001" required />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-사용자">사용자</label>
|
|
<input type="text" id="pc-사용자" required />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-위치">위치</label>
|
|
<input type="text" id="pc-위치" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-CPU">CPU</label>
|
|
<input type="text" id="pc-CPU" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-GPU">GPU</label>
|
|
<input type="text" id="pc-GPU" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-RAM">RAM</label>
|
|
<input type="text" id="pc-RAM" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-SSD1">SSD1</label>
|
|
<input type="text" id="pc-SSD1" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-SSD2">SSD2</label>
|
|
<input type="text" id="pc-SSD2" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-HDD1">HDD1</label>
|
|
<input type="text" id="pc-HDD1" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-HDD2">HDD2</label>
|
|
<input type="text" id="pc-HDD2" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-구매일">구매일</label>
|
|
<input type="text" id="pc-구매일" placeholder="ex) 2024-01-01" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-금액">금액</label>
|
|
<input type="text" id="pc-금액" placeholder="ex) 1,000,000" oninput="this.value = this.value.replace(/[^0-9]/g, '').replace(/\\B(?=(\\d{3})+(?!\d))/g, ',')" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="pc-납품업체">납품업체</label>
|
|
<input type="text" id="pc-납품업체" />
|
|
</div>
|
|
|
|
<div class="form-group full-width">
|
|
<label>품의서 (파일)</label>
|
|
<div style="display:flex; align-items:center; gap:0.5rem;">
|
|
<input type="file" id="pc-품의서" />
|
|
<span id="pc-품의서명" style="font-size:0.75rem; color:var(--text-light)"></span>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-history-area">
|
|
<div class="history-header">
|
|
<h3><i data-lucide="history" style="width:16px; height:16px;"></i> 수정 이력</h3>
|
|
</div>
|
|
<div id="pc-history-list" class="history-timeline">
|
|
<div class="empty-history">이력이 없습니다.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button id="btn-delete-pc-asset" class="btn btn-outline btn-danger">삭제</button>
|
|
<div class="footer-actions">
|
|
<button id="btn-revert-pc-edit" class="btn btn-outline hidden">수정 취소</button>
|
|
<button id="btn-close-pc-footer" class="btn btn-outline">닫기</button>
|
|
<button id="btn-save-pc-asset" class="btn btn-primary">수정</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
export function initPcModal(renderContent: () => void, closeModals: () => void) {
|
|
if (!document.getElementById('pc-asset-modal')) {
|
|
document.body.insertAdjacentHTML('beforeend', PC_MODAL_HTML);
|
|
}
|
|
|
|
const pcForm = document.getElementById('pc-asset-form') as HTMLFormElement;
|
|
const btnRevertEdit = document.getElementById('btn-revert-pc-edit') as HTMLButtonElement;
|
|
const btnSavePc = document.getElementById('btn-save-pc-asset') as HTMLButtonElement;
|
|
const btnDeletePc = document.getElementById('btn-delete-pc-asset') as HTMLButtonElement;
|
|
const btnCloseHeader = document.getElementById('btn-close-pc-modal') as HTMLButtonElement;
|
|
const btnCloseFooter = document.getElementById('btn-close-pc-footer') as HTMLButtonElement;
|
|
|
|
let isEditMode = false;
|
|
let currentAsset: HardwareAsset | null = null;
|
|
|
|
const setEditMode = (edit: boolean) => {
|
|
isEditMode = edit;
|
|
if (edit) {
|
|
pcForm.classList.add('is-edit-mode');
|
|
pcForm.classList.remove('is-view-mode');
|
|
btnSavePc.textContent = '저장';
|
|
btnRevertEdit.classList.remove('hidden');
|
|
btnCloseFooter.classList.add('hidden');
|
|
} else {
|
|
pcForm.classList.add('is-view-mode');
|
|
pcForm.classList.remove('is-edit-mode');
|
|
btnSavePc.textContent = '수정';
|
|
btnRevertEdit.classList.add('hidden');
|
|
btnCloseFooter.classList.remove('hidden');
|
|
if (currentAsset) fillFormData(currentAsset);
|
|
}
|
|
};
|
|
|
|
function fillFormData(asset: HardwareAsset) {
|
|
(document.getElementById('pc-asset-id') as HTMLInputElement).value = asset.id;
|
|
(document.getElementById('pc-법인') as HTMLSelectElement).value = asset.법인;
|
|
(document.getElementById('pc-자산코드') as HTMLInputElement).value = asset.자산코드;
|
|
(document.getElementById('pc-사용자') as HTMLInputElement).value = asset.사용자 || '';
|
|
(document.getElementById('pc-위치') as HTMLInputElement).value = asset.위치 || '';
|
|
(document.getElementById('pc-CPU') as HTMLInputElement).value = asset.CPU || '';
|
|
(document.getElementById('pc-GPU') as HTMLInputElement).value = asset.GPU || '';
|
|
(document.getElementById('pc-RAM') as HTMLInputElement).value = asset.RAM || '';
|
|
(document.getElementById('pc-SSD1') as HTMLInputElement).value = asset.SSD1 || '';
|
|
(document.getElementById('pc-SSD2') as HTMLInputElement).value = asset.SSD2 || '';
|
|
(document.getElementById('pc-HDD1') as HTMLInputElement).value = asset.HDD1 || '';
|
|
(document.getElementById('pc-HDD2') as HTMLInputElement).value = asset.HDD2 || '';
|
|
(document.getElementById('pc-구매일') as HTMLInputElement).value = asset.구매일 || '';
|
|
(document.getElementById('pc-금액') as HTMLInputElement).value = asset.금액 || '';
|
|
(document.getElementById('pc-납품업체') as HTMLInputElement).value = asset.납품업체 || '';
|
|
(document.getElementById('pc-품의서명') as HTMLElement).innerText = asset.품의서명 ? `첨부: ${asset.품의서명}` : '';
|
|
}
|
|
|
|
btnRevertEdit?.addEventListener('click', () => setEditMode(false));
|
|
btnCloseHeader?.addEventListener('click', closeModals);
|
|
btnCloseFooter?.addEventListener('click', closeModals);
|
|
|
|
btnSavePc?.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
if (!isEditMode) {
|
|
setEditMode(true);
|
|
return;
|
|
}
|
|
|
|
if (!pcForm.checkValidity()) { pcForm.reportValidity(); return; }
|
|
|
|
// ... (저장 로직 유지)
|
|
e.preventDefault();
|
|
if (!pcForm.checkValidity()) { pcForm.reportValidity(); return; }
|
|
|
|
const id = (document.getElementById('pc-asset-id') as HTMLInputElement).value;
|
|
const fileInput = document.getElementById('pc-품의서') as HTMLInputElement;
|
|
const 품의서명 = fileInput.files && fileInput.files.length > 0 ? fileInput.files[0].name : (document.getElementById('pc-품의서명') as HTMLElement).innerText.replace('첨부: ', '');
|
|
|
|
const newAsset: HardwareAsset = {
|
|
id: id || Math.random().toString(36).substring(2, 9),
|
|
type: '개인PC',
|
|
법인: (document.getElementById('pc-법인') as HTMLSelectElement).value,
|
|
자산코드: (document.getElementById('pc-자산코드') as HTMLInputElement).value,
|
|
명칭: '',
|
|
위치: (document.getElementById('pc-위치') as HTMLInputElement).value,
|
|
관리자: '', IP주소: '', MACaddress: '', HW사양: '', OS: '', 납품업체: (document.getElementById('pc-납품업체') as HTMLInputElement).value,
|
|
사용자: (document.getElementById('pc-사용자') as HTMLInputElement).value,
|
|
CPU: (document.getElementById('pc-CPU') as HTMLInputElement).value,
|
|
GPU: (document.getElementById('pc-GPU') as HTMLInputElement).value,
|
|
RAM: (document.getElementById('pc-RAM') as HTMLInputElement).value,
|
|
SSD1: (document.getElementById('pc-SSD1') as HTMLInputElement).value,
|
|
SSD2: (document.getElementById('pc-SSD2') as HTMLInputElement).value,
|
|
HDD1: (document.getElementById('pc-HDD1') as HTMLInputElement).value,
|
|
HDD2: (document.getElementById('pc-HDD2') as HTMLInputElement).value,
|
|
구매일: (document.getElementById('pc-구매일') as HTMLInputElement).value,
|
|
금액: (document.getElementById('pc-금액') as HTMLInputElement).value,
|
|
품의서명
|
|
};
|
|
|
|
if (id) {
|
|
const idx = state.masterData.hw.findIndex(a => a.id === id);
|
|
if(idx !== -1) {
|
|
const oldAsset = state.masterData.hw[idx];
|
|
const changes = getChangeDetails(oldAsset, newAsset);
|
|
if (changes) {
|
|
state.masterData.logs.push({
|
|
id: Math.random().toString(36).substring(2, 9),
|
|
assetId: id,
|
|
date: new Date().toLocaleString(),
|
|
details: changes,
|
|
user: '관리자'
|
|
});
|
|
}
|
|
state.masterData.hw[idx] = newAsset;
|
|
}
|
|
} else {
|
|
state.masterData.hw.push(newAsset);
|
|
}
|
|
|
|
closeModals();
|
|
renderContent();
|
|
});
|
|
|
|
btnDeletePc?.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
const id = (document.getElementById('pc-asset-id') as HTMLInputElement).value;
|
|
if (confirm('삭제하시겠습니까?')) {
|
|
state.masterData.hw = state.masterData.hw.filter(a => a.id !== id);
|
|
closeModals();
|
|
renderContent();
|
|
}
|
|
});
|
|
}
|
|
|
|
export function openPcModal(asset?: HardwareAsset) {
|
|
const pcForm = document.getElementById('pc-asset-form') as HTMLFormElement;
|
|
const deleteBtn = document.getElementById('btn-delete-pc-asset')!;
|
|
const historyArea = document.querySelector('.modal-history-area') as HTMLElement;
|
|
|
|
openModal('pc-asset-modal');
|
|
pcForm.reset();
|
|
|
|
if (asset) {
|
|
document.getElementById('pc-modal-title')!.textContent = '개인PC 상세 정보 수정';
|
|
deleteBtn.style.display = 'block';
|
|
if (historyArea) historyArea.style.display = 'flex';
|
|
|
|
(document.getElementById('pc-asset-id') as HTMLInputElement).value = asset.id;
|
|
(document.getElementById('pc-법인') as HTMLSelectElement).value = asset.법인;
|
|
(document.getElementById('pc-자산코드') as HTMLInputElement).value = asset.자산코드;
|
|
(document.getElementById('pc-사용자') as HTMLInputElement).value = asset.사용자 || '';
|
|
(document.getElementById('pc-위치') as HTMLInputElement).value = asset.위치 || '';
|
|
(document.getElementById('pc-CPU') as HTMLInputElement).value = asset.CPU || '';
|
|
(document.getElementById('pc-GPU') as HTMLInputElement).value = asset.GPU || '';
|
|
(document.getElementById('pc-RAM') as HTMLInputElement).value = asset.RAM || '';
|
|
(document.getElementById('pc-SSD1') as HTMLInputElement).value = asset.SSD1 || '';
|
|
(document.getElementById('pc-SSD2') as HTMLInputElement).value = asset.SSD2 || '';
|
|
(document.getElementById('pc-HDD1') as HTMLInputElement).value = asset.HDD1 || '';
|
|
(document.getElementById('pc-HDD2') as HTMLInputElement).value = asset.HDD2 || '';
|
|
(document.getElementById('pc-구매일') as HTMLInputElement).value = asset.구매일 || '';
|
|
(document.getElementById('pc-금액') as HTMLInputElement).value = asset.금액 || '';
|
|
(document.getElementById('pc-납품업체') as HTMLInputElement).value = asset.납품업체 || '';
|
|
(document.getElementById('pc-품의서명') as HTMLElement).innerText = asset.품의서명 ? `첨부: ${asset.품의서명}` : '';
|
|
|
|
renderHistory(asset.id);
|
|
} else {
|
|
document.getElementById('pc-modal-title')!.textContent = '신규 개인PC 자산 추가';
|
|
deleteBtn.style.display = 'none';
|
|
if (historyArea) historyArea.style.display = 'none';
|
|
|
|
(document.getElementById('pc-asset-id') as HTMLInputElement).value = '';
|
|
(document.getElementById('pc-법인') as HTMLSelectElement).value = '한맥';
|
|
(document.getElementById('pc-품의서명') as HTMLElement).innerText = '';
|
|
}
|
|
}
|
|
|
|
function getChangeDetails(oldAsset: HardwareAsset, newAsset: HardwareAsset): string {
|
|
const changes: string[] = [];
|
|
const fields = [
|
|
{ key: '법인', label: '법인' },
|
|
{ key: '자산코드', label: '자산코드' },
|
|
{ key: '사용자', label: '사용자' },
|
|
{ key: '위치', label: '위치' },
|
|
{ key: 'CPU', label: 'CPU' },
|
|
{ key: 'GPU', label: 'GPU' },
|
|
{ key: 'RAM', label: 'RAM' },
|
|
{ key: 'SSD1', label: 'SSD1' },
|
|
{ key: 'SSD2', label: 'SSD2' },
|
|
{ key: 'HDD1', label: 'HDD1' },
|
|
{ key: 'HDD2', label: 'HDD2' },
|
|
{ key: '구매일', label: '구매일' },
|
|
{ key: '금액', label: '금액' },
|
|
{ key: '납품업체', label: '납품업체' },
|
|
{ key: '품의서명', label: '품의서' },
|
|
];
|
|
|
|
fields.forEach(field => {
|
|
const oldVal = (oldAsset as any)[field.key] || '';
|
|
const newVal = (newAsset as any)[field.key] || '';
|
|
if (oldVal !== newVal) {
|
|
changes.push(`${field.label}: ${oldVal || '없음'} → ${newVal || '없음'}`);
|
|
}
|
|
});
|
|
return changes.join('\n');
|
|
}
|
|
|
|
function renderHistory(assetId: string) {
|
|
const historyList = document.getElementById('pc-history-list');
|
|
if (!historyList) return;
|
|
|
|
const logs = state.masterData.logs
|
|
.filter(l => l.assetId === assetId)
|
|
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
|
|
|
if (logs.length === 0) {
|
|
historyList.innerHTML = '<div class="empty-history">이력이 없습니다.</div>';
|
|
return;
|
|
}
|
|
|
|
historyList.innerHTML = logs.map(log => `
|
|
<div class="history-item">
|
|
<div class="history-date">${log.date}</div>
|
|
<div class="history-user">수정자: ${log.user}</div>
|
|
<div class="history-details">${log.details.replace(/\\n/g, '<br>')}</div>
|
|
</div>
|
|
`).join('');
|
|
}
|