merge: main 브랜치의 최신 변경 사항 병합 및 충돌 해결
This commit is contained in:
342
backup_refactor/src/components/Modal/PCModal.ts
Normal file
342
backup_refactor/src/components/Modal/PCModal.ts
Normal file
@@ -0,0 +1,342 @@
|
||||
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('');
|
||||
}
|
||||
Reference in New Issue
Block a user