- Restored HW/SW Dashboard full features (Chart.js, filters, tables) from main - Unified Search Bar & Filter Bar across all views (List, Location) - Integrated asset identity info into all Modal Headers - Standardized 'Remove Row' buttons as high-visibility circular circles - Centralized hardcoded inline styles into dedicated CSS files - Fixed various ReferenceErrors and layout regressions in HWModal
271 lines
12 KiB
TypeScript
271 lines
12 KiB
TypeScript
import { state } from '../../core/state';
|
|
import { BaseModal } from './BaseModal';
|
|
import { createIcons, Edit2, X, Paperclip, Calendar, Plus } from 'lucide';
|
|
import { ORG_LIST } from './SharedData';
|
|
import { generateOptionsHTML, setFieldValue, getFieldValue, applyDateMask } from './ModalUtils';
|
|
|
|
class SwUserModal extends BaseModal {
|
|
private tempSwUsers: any[] = [];
|
|
|
|
constructor() {
|
|
super('sw-user', '소프트웨어 사용자 관리');
|
|
}
|
|
|
|
protected renderFrameHTML(): string {
|
|
return `
|
|
<div id="sw-user-asset-modal" class="modal-overlay hidden">
|
|
<div class="modal-content wide">
|
|
<div class="modal-header">
|
|
<h2 id="sw-user-title" class="modal-title">${this.title}</h2>
|
|
<button id="btn-close-sw-user-modal" class="btn-icon" aria-label="닫기">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="sw-info-summary" id="sw-user-sw-info"></div>
|
|
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h3 class="detail-section-title mb-0">할당된 사용자 목록</h3>
|
|
<button type="button" id="btn-open-add-user" class="btn btn-primary btn-sm"><i data-lucide="plus" class="icon-sm"></i> 사용자 추가</button>
|
|
</div>
|
|
|
|
<div class="table-container">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>조직</th>
|
|
<th>부서</th>
|
|
<th>직위</th>
|
|
<th>이름</th>
|
|
<th class="text-center">사용기간</th>
|
|
<th class="text-center">신청서</th>
|
|
<th class="text-center">관리</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="sw-user-table-body"></tbody>
|
|
</table>
|
|
</div>
|
|
<!-- 더미 폼 (BaseModal 필수 요건 충족용) -->
|
|
<form id="sw-user-asset-form" class="hidden"></form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button id="btn-cancel-sw-user" class="btn btn-outline">취소</button>
|
|
<button id="btn-save-sw-user" class="btn btn-primary">저장</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 사용자 추가/수정 서브 모달 -->
|
|
<div id="sw-user-edit-modal" class="modal-overlay hidden sub-modal">
|
|
<div class="modal-content narrow">
|
|
<div class="modal-header">
|
|
<h3 id="sw-user-edit-title" class="modal-title">사용자 정보</h3>
|
|
<button id="btn-close-user-edit" class="btn-icon">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="sw-user-edit-form" class="grid-form vertical-form">
|
|
<input type="hidden" id="edit-user-index" value="-1" />
|
|
<div class="form-group">
|
|
<label>조직</label>
|
|
<select id="new-user-조직">${generateOptionsHTML(ORG_LIST)}</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>부서</label>
|
|
<input type="text" id="new-user-부서" />
|
|
</div>
|
|
<div class="form-group">
|
|
<label>직위</label>
|
|
<input type="text" id="new-user-직위" />
|
|
</div>
|
|
<div class="form-group">
|
|
<label>이름</label>
|
|
<input type="text" id="new-user-이름" required />
|
|
</div>
|
|
<div class="form-group">
|
|
<label>사용 시작일</label>
|
|
<div class="input-with-btn">
|
|
<input type="text" id="new-user-시작일" />
|
|
<button type="button" class="btn-icon" onclick="const p = document.getElementById('new-user-시작일-picker'); p.value = document.getElementById('new-user-시작일').value; p.showPicker();">
|
|
<i data-lucide="calendar" class="icon-sm"></i>
|
|
</button>
|
|
<input type="date" id="new-user-시작일-picker" class="hidden-picker" onchange="document.getElementById('new-user-시작일').value = this.value" tabindex="-1" />
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>사용 종료일</label>
|
|
<div class="input-with-btn">
|
|
<input type="text" id="new-user-종료일" />
|
|
<button type="button" class="btn-icon" onclick="const p = document.getElementById('new-user-종료일-picker'); p.value = document.getElementById('new-user-종료일').value; p.showPicker();">
|
|
<i data-lucide="calendar" class="icon-sm"></i>
|
|
</button>
|
|
<input type="date" id="new-user-종료일-picker" class="hidden-picker" onchange="document.getElementById('new-user-종료일').value = this.value" tabindex="-1" />
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>신청서 (증빙)</label>
|
|
<input type="file" id="new-user-신청서" />
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button id="btn-close-user-sub" class="btn btn-outline">취소</button>
|
|
<button id="btn-confirm-user-edit" class="btn btn-primary">확인</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<style>
|
|
.hidden-picker {
|
|
position: absolute;
|
|
width: 0;
|
|
height: 0;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
</style>
|
|
`;
|
|
}
|
|
|
|
protected initChildLogic(onSave: () => void, closeModals: () => void): void {
|
|
const mainSaveBtn = document.getElementById('btn-save-sw-user')!;
|
|
const addUserBtn = document.getElementById('btn-open-add-user')!;
|
|
const confirmUserBtn = document.getElementById('btn-confirm-user-edit')!;
|
|
|
|
['new-user-시작일', 'new-user-종료일'].forEach(id => {
|
|
const el = document.getElementById(id) as HTMLInputElement;
|
|
if (el) applyDateMask(el);
|
|
});
|
|
|
|
addUserBtn.addEventListener('click', () => this.openUserEditSubModal());
|
|
confirmUserBtn.addEventListener('click', () => this.saveUserDataToList());
|
|
|
|
mainSaveBtn.addEventListener('click', () => {
|
|
if (!this.currentAsset) return;
|
|
const existingIdx = state.masterData.swUsers.findIndex(u => u.sw_id === this.currentAsset!.id);
|
|
const newMapping = {
|
|
sw_id: this.currentAsset!.id,
|
|
userData: this.tempSwUsers.map(u => [u.조직, u.부서, u.직위, u.이름, u.사용기간, u.신청서명])
|
|
};
|
|
if (existingIdx > -1) state.masterData.swUsers[existingIdx] = newMapping as any;
|
|
else state.masterData.swUsers.push(newMapping as any);
|
|
|
|
onSave(); this.close(); closeModals();
|
|
});
|
|
|
|
document.getElementById('btn-close-sw-user-modal')?.addEventListener('click', () => this.close());
|
|
document.getElementById('btn-cancel-sw-user')?.addEventListener('click', () => this.close());
|
|
|
|
const subModal = document.getElementById('sw-user-edit-modal')!;
|
|
const closeSub = () => subModal.classList.add('hidden');
|
|
document.getElementById('btn-close-user-edit')?.addEventListener('click', closeSub);
|
|
document.getElementById('btn-close-user-sub')?.addEventListener('click', closeSub);
|
|
|
|
createIcons({ icons: { X, Plus, Calendar, Edit2, Paperclip } });
|
|
}
|
|
|
|
protected fillFormData(asset: any): void {
|
|
const swInfo = document.getElementById('sw-user-sw-info')!;
|
|
swInfo.innerHTML = `
|
|
<div class="sw-info-header border-b border-hairline pb-4 mb-6">
|
|
<div class="detail-label-sm">${asset.purchase_corp || asset.법인 || ''}</div>
|
|
<div class="asset-code-title">${asset.product_name || asset.제품명 || ''}</div>
|
|
</div>
|
|
`;
|
|
|
|
const existingMapping = state.masterData.swUsers.find(u => u.sw_id === asset.id);
|
|
this.tempSwUsers = existingMapping ? (existingMapping.userData || []).map((u: any) => ({
|
|
조직: u[0], 부서: u[1], 직위: u[2], 이름: u[3], 사용기간: u[4], 신청서명: u[5]
|
|
})) : [];
|
|
|
|
this.renderUserList();
|
|
}
|
|
|
|
protected onAfterOpen(): void {}
|
|
|
|
private renderUserList() {
|
|
const tbody = document.getElementById('sw-user-table-body')!;
|
|
if (!tbody) return;
|
|
tbody.innerHTML = '';
|
|
if (this.tempSwUsers.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="7" class="empty-cell text-center p-8">할당된 사용자가 없습니다.</td></tr>';
|
|
return;
|
|
}
|
|
|
|
this.tempSwUsers.forEach((user, idx) => {
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td>${user.조직 || ''}</td>
|
|
<td>${user.부서 || ''}</td>
|
|
<td>${user.직위 || ''}</td>
|
|
<td>${user.이름 || ''}</td>
|
|
<td class="text-center">${user.사용기간 || ''}</td>
|
|
<td class="text-center">${user.신청서명 ? '<i data-lucide="paperclip" class="text-primary icon-sm"></i>' : '-'}</td>
|
|
<td class="text-center">
|
|
<div class="flex gap-2 justify-center items-center">
|
|
<button class="btn btn-outline btn-sm btn-edit-user" data-idx="${idx}">수정</button>
|
|
<button class="btn-circle-remove btn-del-user" data-idx="${idx}">×</button>
|
|
</div>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(tr);
|
|
});
|
|
|
|
tbody.querySelectorAll('.btn-edit-user').forEach(btn => {
|
|
btn.addEventListener('click', (e) => {
|
|
const idx = parseInt((e.currentTarget as HTMLElement).getAttribute('data-idx')!);
|
|
this.openUserEditSubModal(idx);
|
|
});
|
|
});
|
|
|
|
tbody.querySelectorAll('.btn-del-user').forEach(btn => {
|
|
btn.addEventListener('click', (e) => {
|
|
const idx = parseInt((e.currentTarget as HTMLElement).getAttribute('data-idx')!);
|
|
if (confirm('사용자 할당을 삭제하시겠습니까?')) {
|
|
this.tempSwUsers.splice(idx, 1); this.renderUserList();
|
|
}
|
|
});
|
|
});
|
|
createIcons({ icons: { Paperclip } });
|
|
}
|
|
|
|
private openUserEditSubModal(idx: number = -1) {
|
|
const subModal = document.getElementById('sw-user-edit-modal')!;
|
|
const form = document.getElementById('sw-user-edit-form') as HTMLFormElement;
|
|
form.reset();
|
|
setFieldValue('edit-user-index', idx);
|
|
if (idx > -1) {
|
|
const user = this.tempSwUsers[idx];
|
|
setFieldValue('new-user-조직', user.조직);
|
|
setFieldValue('new-user-부서', user.부서);
|
|
setFieldValue('new-user-직위', user.직위);
|
|
setFieldValue('new-user-이름', user.이름);
|
|
if (user.사용기간 && user.사용기간.includes('~')) {
|
|
const parts = user.사용기간.split('~');
|
|
setFieldValue('new-user-시작일', parts[0].trim());
|
|
setFieldValue('new-user-종료일', parts[1].trim());
|
|
}
|
|
}
|
|
subModal.classList.remove('hidden');
|
|
}
|
|
|
|
private saveUserDataToList() {
|
|
const idx = parseInt(getFieldValue('edit-user-index'));
|
|
const 신청서Input = document.getElementById('new-user-신청서') as HTMLInputElement;
|
|
const 신청서명 = 신청서Input.files && 신청서Input.files.length > 0 ? 신청서Input.files[0].name : (idx > -1 ? this.tempSwUsers[idx].신청서명 : '');
|
|
|
|
const userData: any = {
|
|
조직: getFieldValue('new-user-조직'),
|
|
부서: getFieldValue('new-user-부서'),
|
|
직위: getFieldValue('new-user-직위'),
|
|
이름: getFieldValue('new-user-이름'),
|
|
사용기간: `${getFieldValue('new-user-시작일')} ~ ${getFieldValue('new-user-종료일')}`,
|
|
신청서명
|
|
};
|
|
if (idx === -1) this.tempSwUsers.push(userData);
|
|
else this.tempSwUsers[idx] = userData;
|
|
document.getElementById('sw-user-edit-modal')?.classList.add('hidden');
|
|
this.renderUserList();
|
|
}
|
|
}
|
|
|
|
export const swUserModal = new SwUserModal();
|
|
export function initSwUserModal(onSave: () => void, closeModals: () => void) { swUserModal.init(onSave, closeModals); }
|
|
export function openSwUserModal(asset: any) { swUserModal.open(asset); }
|