import { state, saveJobSpec, deleteJobSpec } from '../../core/state'; import { BaseModal } from './BaseModal'; import { setFieldValue } from './ModalUtils'; import { UI_TEXT } from '../../core/schema'; import { calculatePcScoreDeductive } from '../../core/utils'; class JobSpecModal extends BaseModal { constructor() { super('job-spec', '직무별 기준 사양'); } protected renderFrameHTML(): string { const sharedStyle = 'height: 38px !important; box-sizing: border-box !important; font-size: 13px; margin: 0;'; const inputStyle = sharedStyle; return ` `; } protected initChildLogic(onSave: () => void, closeModals: () => void): void { const saveBtn = document.getElementById('btn-save-job-spec-asset')!; const revertBtn = document.getElementById('btn-revert-job-spec-edit')!; const deleteBtn = document.getElementById('btn-delete-job-spec-asset')!; saveBtn.addEventListener('click', async () => { if (!this.currentAsset) return; if (!this.isEditMode) { this.setEditLockMode('edit'); this.isEditMode = true; return; } const jobName = (document.getElementById('job-spec-job-name') as HTMLInputElement).value.trim(); const cpuStd = (document.getElementById('job-spec-cpu-standard') as HTMLInputElement).value.trim(); const ramStd = (document.getElementById('job-spec-ram-standard') as HTMLInputElement).value.trim(); const gpuStd = (document.getElementById('job-spec-gpu-standard') as HTMLInputElement).value.trim(); const minScoreStr = (document.getElementById('job-spec-min-score') as HTMLInputElement).value; const remarks = (document.getElementById('job-spec-remarks') as HTMLTextAreaElement).value.trim(); if (!jobName) { alert('직무명을 입력해 주세요.'); return; } const updated = { id: this.currentAsset.id || null, job_name: jobName, cpu_standard: cpuStd, ram_standard: ramStd, gpu_standard: gpuStd, min_score: minScoreStr !== '' ? parseInt(minScoreStr, 10) : 0, remarks: remarks }; if (await saveJobSpec(updated)) { alert(UI_TEXT.MESSAGES.SAVE_SUCCESS); onSave(); this.close(); closeModals(); } }); revertBtn.addEventListener('click', () => { this.setEditLockMode('view'); if (this.currentAsset) this.fillFormData(this.currentAsset); }); deleteBtn.addEventListener('click', async () => { if (!this.currentAsset || !this.currentAsset.id) return; if (!confirm('정말로 이 직무별 기준 사양을 삭제하시겠습니까?')) return; if (await deleteJobSpec(this.currentAsset.id)) { alert('성공적으로 삭제되었습니다.'); onSave(); this.close(); closeModals(); } }); // 자동완성 바인딩 this.bindAutocomplete('job-spec-cpu-standard', 'job-spec-cpu-autocomplete', 'CPU'); this.bindAutocomplete('job-spec-ram-standard', 'job-spec-ram-autocomplete', 'RAM'); this.bindAutocomplete('job-spec-gpu-standard', 'job-spec-gpu-autocomplete', 'GPU'); // 실시간 점수 계산 이벤트 바인딩 const inputs = ['job-spec-cpu-standard', 'job-spec-ram-standard', 'job-spec-gpu-standard']; inputs.forEach(id => { const el = document.getElementById(id); el?.addEventListener('input', () => this.updateMinScore()); el?.addEventListener('change', () => this.updateMinScore()); }); } private bindAutocomplete(inputId: string, autocompleteId: string, category: string) { const input = document.getElementById(inputId) as HTMLInputElement; const list = document.getElementById(autocompleteId) as HTMLDivElement; if (!input || !list) return; const showList = (filterText: string = '') => { if (!this.isEditMode) return; const items = (state.masterData.partsMaster || []).filter((c: any) => c.category === category); const filtered = filterText ? items.filter((c: any) => c.component_name.toLowerCase().includes(filterText.toLowerCase())) : items; if (filtered.length === 0) { list.innerHTML = '
검색 결과 없음
'; } else { list.innerHTML = filtered.map((c: any) => `
${c.component_name}
`).join(''); } list.classList.remove('hidden'); }; input.addEventListener('focus', () => { showList(input.value); }); input.addEventListener('input', () => { showList(input.value); }); list.addEventListener('mousedown', (e) => { const item = (e.target as HTMLElement).closest('.autocomplete-item'); if (item && item.getAttribute('data-val')) { input.value = item.getAttribute('data-val') || ''; list.classList.add('hidden'); this.updateMinScore(); } }); document.addEventListener('mousedown', (e) => { if (e.target !== input && !list.contains(e.target as Node)) { list.classList.add('hidden'); } }); } private updateMinScore(): void { const cpu = (document.getElementById('job-spec-cpu-standard') as HTMLInputElement)?.value || ''; const ram = (document.getElementById('job-spec-ram-standard') as HTMLInputElement)?.value || ''; const gpu = (document.getElementById('job-spec-gpu-standard') as HTMLInputElement)?.value || ''; const score = calculatePcScoreDeductive(cpu, ram, gpu, ''); const minScoreEl = document.getElementById('job-spec-min-score') as HTMLInputElement; if (minScoreEl) { minScoreEl.value = score.toString(); } } protected fillFormData(asset: any): void { setFieldValue('job-spec-id', asset.id || ''); setFieldValue('job-spec-job-name', asset.job_name || ''); setFieldValue('job-spec-cpu-standard', asset.cpu_standard || ''); setFieldValue('job-spec-ram-standard', asset.ram_standard || ''); setFieldValue('job-spec-gpu-standard', asset.gpu_standard || ''); setFieldValue('job-spec-min-score', asset.min_score !== undefined ? asset.min_score.toString() : '100'); setFieldValue('job-spec-remarks', asset.remarks || ''); } protected onAfterOpen(asset: any, mode: string): void { const titleEl = document.getElementById('job-spec-modal-title'); if (titleEl) { if (mode === 'add') { titleEl.textContent = '신규 직무별 기준 사양 등록'; } else { titleEl.textContent = '직무별 기준 사양 상세 편집'; } } const deleteBtn = document.getElementById('btn-delete-job-spec-asset')!; const saveBtn = document.getElementById('btn-save-job-spec-asset')!; deleteBtn.style.display = (mode === 'add') ? 'none' : 'block'; if (mode === 'add') { this.setEditLockMode('edit'); this.isEditMode = true; saveBtn.textContent = '등록'; saveBtn.style.display = 'block'; } else { this.setEditLockMode('view'); this.isEditMode = false; saveBtn.textContent = '수정'; saveBtn.style.display = 'block'; } this.updateMinScore(); } } export const jobSpecModal = new JobSpecModal(); export function initJobSpecModal(onSave: () => void, closeModals: () => void) { jobSpecModal.init(onSave, closeModals); } export function openJobSpecModal(asset: any, mode: 'view' | 'edit' | 'add' = 'view') { jobSpecModal.open(asset, mode); }