596 lines
25 KiB
TypeScript
596 lines
25 KiB
TypeScript
import { state, loadMasterDataFromDB } from '../../core/state';
|
|
import { createIcons, Search, Monitor, RefreshCw } from 'lucide';
|
|
import { API_BASE_URL } from '../../core/utils';
|
|
|
|
export class PCFlowModal {
|
|
private static instance: PCFlowModal | null = null;
|
|
|
|
private modalEl: HTMLElement | null = null;
|
|
private currentFlowType: 'checkout' | 'return' | 'move' = 'checkout';
|
|
|
|
// Selected state
|
|
private selectedUser: any = null;
|
|
private selectedTargetUser: any = null;
|
|
private selectedPC: any = null;
|
|
|
|
private constructor() {}
|
|
|
|
public static getInstance(): PCFlowModal {
|
|
if (!PCFlowModal.instance) {
|
|
PCFlowModal.instance = new PCFlowModal();
|
|
}
|
|
return PCFlowModal.instance;
|
|
}
|
|
|
|
public init(onSave: () => void) {
|
|
if (document.getElementById('pc-flow-modal')) return;
|
|
|
|
// Inject HTML
|
|
document.body.insertAdjacentHTML('beforeend', this.renderHTML());
|
|
|
|
this.modalEl = document.getElementById('pc-flow-modal');
|
|
this.setupEventListeners(onSave);
|
|
|
|
// Set default date to today
|
|
const dateInput = document.getElementById('pc-flow-date') as HTMLInputElement;
|
|
if (dateInput) {
|
|
dateInput.value = new Date().toISOString().split('T')[0];
|
|
}
|
|
|
|
createIcons({ icons: { Search, Monitor, RefreshCw } });
|
|
}
|
|
|
|
public open() {
|
|
this.resetState();
|
|
if (this.modalEl) {
|
|
this.modalEl.classList.remove('hidden');
|
|
}
|
|
this.updateUI();
|
|
}
|
|
|
|
public close() {
|
|
if (this.modalEl) {
|
|
this.modalEl.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
private resetState() {
|
|
this.selectedUser = null;
|
|
this.selectedTargetUser = null;
|
|
this.selectedPC = null;
|
|
this.currentFlowType = 'checkout';
|
|
|
|
const radioCheckout = document.querySelector('input[name="flow-type"][value="checkout"]') as HTMLInputElement;
|
|
if (radioCheckout) {
|
|
radioCheckout.checked = true;
|
|
document.querySelectorAll('.flow-type-label').forEach(l => {
|
|
l.classList.toggle('active', l.contains(radioCheckout));
|
|
});
|
|
}
|
|
|
|
// Reset text fields
|
|
const userSearch = document.getElementById('pc-flow-user-search') as HTMLInputElement;
|
|
if (userSearch) userSearch.value = '';
|
|
|
|
const targetUserSearch = document.getElementById('pc-flow-target-user-search') as HTMLInputElement;
|
|
if (targetUserSearch) targetUserSearch.value = '';
|
|
|
|
const stockSearch = document.getElementById('pc-flow-stock-search') as HTMLInputElement;
|
|
if (stockSearch) stockSearch.value = '';
|
|
|
|
const details = document.getElementById('pc-flow-details') as HTMLTextAreaElement;
|
|
if (details) details.value = '';
|
|
}
|
|
|
|
private setupEventListeners(onSave: () => void) {
|
|
const btnClose = document.getElementById('btn-close-pc-flow-modal');
|
|
const btnCancel = document.getElementById('btn-cancel-pc-flow-modal');
|
|
const btnSubmit = document.getElementById('btn-submit-pc-flow');
|
|
|
|
btnClose?.addEventListener('click', () => this.close());
|
|
btnCancel?.addEventListener('click', () => this.close());
|
|
|
|
// Flow Type Radio Buttons
|
|
const labels = document.querySelectorAll('.flow-type-label');
|
|
labels.forEach(label => {
|
|
const radio = label.querySelector('input[name="flow-type"]') as HTMLInputElement;
|
|
label.addEventListener('click', () => {
|
|
labels.forEach(l => l.classList.remove('active'));
|
|
label.classList.add('active');
|
|
radio.checked = true;
|
|
this.currentFlowType = radio.value as any;
|
|
|
|
// Reset selected PC when switching flow types
|
|
this.selectedPC = null;
|
|
this.updateUI();
|
|
});
|
|
});
|
|
|
|
// 1. Source User Autocomplete Search
|
|
const userSearch = document.getElementById('pc-flow-user-search') as HTMLInputElement;
|
|
const userSuggestions = document.getElementById('pc-flow-user-suggestions')!;
|
|
|
|
userSearch?.addEventListener('input', () => {
|
|
const query = userSearch.value.trim().toLowerCase();
|
|
if (!query) {
|
|
userSuggestions.classList.add('hidden');
|
|
return;
|
|
}
|
|
|
|
const users = state.masterData.users || [];
|
|
const filtered = users.filter((u: any) =>
|
|
(u.user_name && u.user_name.toLowerCase().includes(query)) ||
|
|
(u.dept_name && u.dept_name.toLowerCase().includes(query)) ||
|
|
(u.emp_no && u.emp_no.toString().includes(query))
|
|
);
|
|
|
|
const uniqueFiltered: any[] = [];
|
|
const seen = new Set();
|
|
filtered.forEach((u: any) => {
|
|
const key = u.emp_no || u.user_name;
|
|
if (!seen.has(key)) {
|
|
seen.add(key);
|
|
uniqueFiltered.push(u);
|
|
}
|
|
});
|
|
|
|
this.renderUserSuggestions(uniqueFiltered, userSuggestions, (user) => {
|
|
this.selectedUser = user;
|
|
userSearch.value = `${user.user_name} (${user.dept_name} / 사번:${user.emp_no || '-'})`;
|
|
userSuggestions.classList.add('hidden');
|
|
|
|
// Automatically populate details if return or move
|
|
if (this.currentFlowType === 'return' || this.currentFlowType === 'move') {
|
|
this.selectedPC = null; // Reset selection
|
|
}
|
|
this.updateUI();
|
|
});
|
|
});
|
|
|
|
// Close suggestion overlays on clicking outside
|
|
document.addEventListener('click', (e) => {
|
|
const target = e.target as HTMLElement;
|
|
if (!target.closest('#pc-flow-user-search') && !target.closest('#pc-flow-user-suggestions')) {
|
|
userSuggestions.classList.add('hidden');
|
|
}
|
|
if (!target.closest('#pc-flow-target-user-search') && !target.closest('#pc-flow-target-user-suggestions')) {
|
|
const targetSuggestions = document.getElementById('pc-flow-target-user-suggestions');
|
|
targetSuggestions?.classList.add('hidden');
|
|
}
|
|
if (!target.closest('#pc-flow-stock-search') && !target.closest('#pc-flow-stock-suggestions')) {
|
|
const stockSuggestions = document.getElementById('pc-flow-stock-suggestions');
|
|
stockSuggestions?.classList.add('hidden');
|
|
}
|
|
});
|
|
|
|
// 2. Target User Autocomplete Search (For Moves)
|
|
const targetUserSearch = document.getElementById('pc-flow-target-user-search') as HTMLInputElement;
|
|
const targetSuggestions = document.getElementById('pc-flow-target-user-suggestions')!;
|
|
|
|
targetUserSearch?.addEventListener('input', () => {
|
|
const query = targetUserSearch.value.trim().toLowerCase();
|
|
if (!query) {
|
|
targetSuggestions.classList.add('hidden');
|
|
return;
|
|
}
|
|
|
|
const users = state.masterData.users || [];
|
|
const filtered = users.filter((u: any) =>
|
|
(u.user_name && u.user_name.toLowerCase().includes(query)) ||
|
|
(u.dept_name && u.dept_name.toLowerCase().includes(query)) ||
|
|
(u.emp_no && u.emp_no.toString().includes(query))
|
|
);
|
|
|
|
const uniqueFiltered: any[] = [];
|
|
const seen = new Set();
|
|
filtered.forEach((u: any) => {
|
|
const key = u.emp_no || u.user_name;
|
|
if (!seen.has(key)) {
|
|
seen.add(key);
|
|
uniqueFiltered.push(u);
|
|
}
|
|
});
|
|
|
|
this.renderUserSuggestions(uniqueFiltered, targetSuggestions, (user) => {
|
|
this.selectedTargetUser = user;
|
|
targetUserSearch.value = `${user.user_name} (${user.dept_name} / 사번:${user.emp_no || '-'})`;
|
|
targetSuggestions.classList.add('hidden');
|
|
this.updateUI();
|
|
});
|
|
});
|
|
|
|
// 3. Stock PC Autocomplete Search (For Checkout)
|
|
const stockSearch = document.getElementById('pc-flow-stock-search') as HTMLInputElement;
|
|
const stockSuggestions = document.getElementById('pc-flow-stock-suggestions')!;
|
|
|
|
const showStockSuggestions = () => {
|
|
const query = stockSearch.value.trim().toLowerCase();
|
|
|
|
// Filter available PCs (category PC, status '대기', '미할당', or '재고')
|
|
const pcs = state.masterData.pc || [];
|
|
const filtered = pcs.filter((p: any) => {
|
|
const status = (p.hw_status || '').trim();
|
|
const matchesQuery = !query ||
|
|
(p.asset_code && p.asset_code.toLowerCase().includes(query)) ||
|
|
(p.model_name && p.model_name.toLowerCase().includes(query)) ||
|
|
(p.cpu && p.cpu.toLowerCase().includes(query));
|
|
|
|
return (status === '대기' || status === '미할당' || status === '재고') && matchesQuery;
|
|
});
|
|
|
|
this.renderPCSuggestions(filtered, stockSuggestions, (pc) => {
|
|
this.selectedPC = pc;
|
|
stockSearch.value = `${pc.asset_code} - ${pc.model_name}`;
|
|
stockSuggestions.classList.add('hidden');
|
|
this.updateUI();
|
|
});
|
|
};
|
|
|
|
stockSearch?.addEventListener('input', showStockSuggestions);
|
|
stockSearch?.addEventListener('focus', showStockSuggestions);
|
|
stockSearch?.addEventListener('click', showStockSuggestions);
|
|
|
|
// 4. Submit Transaction
|
|
btnSubmit?.addEventListener('click', async () => {
|
|
if (!this.validateInputs()) return;
|
|
|
|
const dateVal = (document.getElementById('pc-flow-date') as HTMLInputElement).value;
|
|
const detailsVal = (document.getElementById('pc-flow-details') as HTMLTextAreaElement).value.trim();
|
|
const loginUser = state.currentUserRole === 'admin' ? '관리자' : '실무담당자';
|
|
|
|
// Build Details Message as JSON
|
|
const logData = {
|
|
type: this.currentFlowType,
|
|
user: this.selectedUser ? this.selectedUser.user_name : '',
|
|
dept: this.selectedUser ? this.selectedUser.dept_name : '',
|
|
targetUser: this.selectedTargetUser ? this.selectedTargetUser.user_name : '',
|
|
targetDept: this.selectedTargetUser ? this.selectedTargetUser.dept_name : '',
|
|
assetCode: this.selectedPC ? this.selectedPC.asset_code : '',
|
|
memo: detailsVal
|
|
};
|
|
const finalDetails = JSON.stringify(logData);
|
|
|
|
const payload: any = {
|
|
action: this.currentFlowType,
|
|
assetId: this.selectedPC.id,
|
|
date: dateVal,
|
|
details: finalDetails,
|
|
manager: loginUser
|
|
};
|
|
|
|
if (this.currentFlowType === 'checkout') {
|
|
payload.userName = this.selectedUser.user_name;
|
|
payload.dept = this.selectedUser.dept_name;
|
|
payload.empNo = this.selectedUser.emp_no;
|
|
payload.position = this.selectedUser.position || '사원';
|
|
} else if (this.currentFlowType === 'move') {
|
|
payload.userName = this.selectedTargetUser.user_name;
|
|
payload.dept = this.selectedTargetUser.dept_name;
|
|
payload.empNo = this.selectedTargetUser.emp_no;
|
|
payload.position = this.selectedTargetUser.position || '사원';
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/api/pc/flow`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
|
|
if (response.ok) {
|
|
alert('PC 이동/반납 처리가 완료되었습니다.');
|
|
this.close();
|
|
onSave(); // Refresh views
|
|
} else {
|
|
const errData = await response.json();
|
|
alert(`오류 발생: ${errData.error || '처리 실패'}`);
|
|
}
|
|
} catch (err) {
|
|
console.error('API Error:', err);
|
|
alert('서버 전송 중 오류가 발생했습니다.');
|
|
}
|
|
});
|
|
}
|
|
|
|
private validateInputs(): boolean {
|
|
if (this.currentFlowType === 'checkout') {
|
|
if (!this.selectedUser) { alert('대상 사원을 선택해주세요.'); return false; }
|
|
if (!this.selectedPC) { alert('불출할 재고 PC를 선택해주세요.'); return false; }
|
|
} else if (this.currentFlowType === 'return') {
|
|
if (!this.selectedUser) { alert('반납 대상 사원을 선택해주세요.'); return false; }
|
|
if (!this.selectedPC) { alert('반납할 PC 자산을 선택해주세요.'); return false; }
|
|
} else if (this.currentFlowType === 'move') {
|
|
if (!this.selectedUser) { alert('인계 사원을 선택해주세요.'); return false; }
|
|
if (!this.selectedPC) { alert('이동할 PC 자산을 선택해주세요.'); return false; }
|
|
if (!this.selectedTargetUser) { alert('인수 사원을 선택해주세요.'); return false; }
|
|
if (this.selectedUser.emp_no === this.selectedTargetUser.emp_no) {
|
|
alert('인계자와 인수자는 동일할 수 없습니다.');
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private renderUserSuggestions(users: any[], container: HTMLElement, onSelect: (user: any) => void) {
|
|
container.innerHTML = '';
|
|
if (users.length === 0) {
|
|
container.innerHTML = '<div class="autocomplete-item-empty">일치하는 사원이 없습니다.</div>';
|
|
container.classList.remove('hidden');
|
|
return;
|
|
}
|
|
|
|
users.forEach(u => {
|
|
const item = document.createElement('div');
|
|
item.className = 'autocomplete-item';
|
|
item.innerHTML = `
|
|
<div class="suggestion-name">${u.user_name}</div>
|
|
<div class="suggestion-meta">
|
|
<span>부서: ${u.dept_name}</span>
|
|
<span>|</span>
|
|
<span>사번: ${u.emp_no || '-'}</span>
|
|
</div>
|
|
`;
|
|
item.addEventListener('click', () => onSelect(u));
|
|
container.appendChild(item);
|
|
});
|
|
container.classList.remove('hidden');
|
|
}
|
|
|
|
private renderPCSuggestions(pcs: any[], container: HTMLElement, onSelect: (pc: any) => void) {
|
|
container.innerHTML = '';
|
|
if (pcs.length === 0) {
|
|
container.innerHTML = '<div class="autocomplete-item-empty">불출 가능한 대기 PC 재고가 없습니다.</div>';
|
|
container.classList.remove('hidden');
|
|
return;
|
|
}
|
|
|
|
pcs.forEach(p => {
|
|
const item = document.createElement('div');
|
|
item.className = 'autocomplete-item';
|
|
item.innerHTML = `
|
|
<div class="suggestion-name">${p.asset_code} (${p.model_name || '모델명 없음'})</div>
|
|
<div class="suggestion-meta">
|
|
사양: CPU ${p.cpu || '-'} / RAM ${p.ram || '-'} / 위치: ${p.location || '-'}
|
|
</div>
|
|
`;
|
|
item.addEventListener('click', () => onSelect(p));
|
|
container.appendChild(item);
|
|
});
|
|
container.classList.remove('hidden');
|
|
}
|
|
|
|
private updateUI() {
|
|
// 1. Hide/Show dynamic sections based on flow type
|
|
const stockContainer = document.getElementById('stock-pc-search-container')!;
|
|
const targetUserContainer = document.getElementById('target-user-search-container')!;
|
|
const userPcsContainer = document.getElementById('user-pcs-container')!;
|
|
const labelStep2 = document.getElementById('user-search-label')!;
|
|
|
|
if (this.currentFlowType === 'checkout') {
|
|
stockContainer.classList.remove('hidden');
|
|
targetUserContainer.classList.add('hidden');
|
|
userPcsContainer.classList.add('hidden');
|
|
labelStep2.textContent = '2. 불출 대상 사원 검색';
|
|
} else if (this.currentFlowType === 'return') {
|
|
stockContainer.classList.add('hidden');
|
|
targetUserContainer.classList.add('hidden');
|
|
userPcsContainer.classList.remove('hidden');
|
|
labelStep2.textContent = '2. 반납 대상 사원 검색';
|
|
} else if (this.currentFlowType === 'move') {
|
|
stockContainer.classList.add('hidden');
|
|
targetUserContainer.classList.remove('hidden');
|
|
userPcsContainer.classList.remove('hidden');
|
|
labelStep2.textContent = '2. 인계 사원 검색';
|
|
}
|
|
|
|
// 2. Update summary panels on the right
|
|
const summaryUserName = document.getElementById('summary-user-name')!;
|
|
const summaryUserDept = document.getElementById('summary-user-dept')!;
|
|
if (this.selectedUser) {
|
|
summaryUserName.textContent = this.selectedUser.user_name;
|
|
summaryUserDept.textContent = `${this.selectedUser.dept_name} / 사번: ${this.selectedUser.emp_no || '-'}`;
|
|
} else {
|
|
summaryUserName.textContent = '선택된 사원 없음';
|
|
summaryUserDept.textContent = '-';
|
|
}
|
|
|
|
const summaryTargetCard = document.getElementById('summary-target-user-card')!;
|
|
const summaryTargetUserName = document.getElementById('summary-target-user-name')!;
|
|
const summaryTargetUserDept = document.getElementById('summary-target-user-dept')!;
|
|
if (this.currentFlowType === 'move') {
|
|
summaryTargetCard.classList.remove('hidden');
|
|
if (this.selectedTargetUser) {
|
|
summaryTargetUserName.textContent = this.selectedTargetUser.user_name;
|
|
summaryTargetUserDept.textContent = `${this.selectedTargetUser.dept_name} / 사번: ${this.selectedTargetUser.emp_no || '-'}`;
|
|
} else {
|
|
summaryTargetUserName.textContent = '선택된 사원 없음';
|
|
summaryTargetUserDept.textContent = '-';
|
|
}
|
|
} else {
|
|
summaryTargetCard.classList.add('hidden');
|
|
}
|
|
|
|
const summaryPcCode = document.getElementById('summary-pc-code')!;
|
|
const summaryPcModel = document.getElementById('summary-pc-model')!;
|
|
if (this.selectedPC) {
|
|
summaryPcCode.textContent = this.selectedPC.asset_code;
|
|
summaryPcModel.textContent = `${this.selectedPC.model_name || '모델명 없음'} (${this.selectedPC.cpu || '-'} / ${this.selectedPC.ram || '-'})`;
|
|
} else {
|
|
summaryPcCode.textContent = '선택된 PC 없음';
|
|
summaryPcModel.textContent = '-';
|
|
}
|
|
|
|
// 3. Render user's active PCs list on the right (For Return & Move)
|
|
const userPcsList = document.getElementById('user-pcs-list')!;
|
|
if (this.selectedUser && (this.currentFlowType === 'return' || this.currentFlowType === 'move')) {
|
|
const allPcs = state.masterData.pc || [];
|
|
const userPcs = allPcs.filter((p: any) =>
|
|
(p.emp_no && p.emp_no.toString() === this.selectedUser.emp_no?.toString()) ||
|
|
(p.user_current && p.user_current === this.selectedUser.user_name)
|
|
);
|
|
|
|
if (userPcs.length === 0) {
|
|
userPcsList.innerHTML = '<div class="empty-list-message">이 사용자가 소유한 PC 자산이 없습니다.</div>';
|
|
} else {
|
|
userPcsList.innerHTML = userPcs.map(p => {
|
|
const isSelected = this.selectedPC && this.selectedPC.id === p.id;
|
|
return `
|
|
<div class="user-pc-item ${isSelected ? 'selected' : ''}" data-id="${p.id}">
|
|
<div class="pc-item-code">${p.asset_code}</div>
|
|
<div class="pc-item-meta">
|
|
${p.model_name || '모델명 없음'} | CPU: ${p.cpu || '-'} | RAM: ${p.ram || '-'}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
|
|
// Bind clicks to list items
|
|
userPcsList.querySelectorAll('.user-pc-item').forEach(item => {
|
|
item.addEventListener('click', () => {
|
|
const pcId = item.getAttribute('data-id');
|
|
const foundPC = userPcs.find(p => p.id === pcId);
|
|
if (foundPC) {
|
|
this.selectedPC = foundPC;
|
|
this.updateUI();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
} else {
|
|
userPcsList.innerHTML = '';
|
|
}
|
|
}
|
|
|
|
private renderHTML(): string {
|
|
return `
|
|
<div id="pc-flow-modal" class="modal-overlay hidden">
|
|
<div class="modal-content wide">
|
|
<div class="modal-header">
|
|
<h2 class="modal-title">
|
|
<i data-lucide="refresh-cw"></i> PC 이동/반납 (불출/반납/이동)
|
|
</h2>
|
|
<button id="btn-close-pc-flow-modal" class="btn-icon" aria-label="닫기">×</button>
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
<div class="modal-body-split">
|
|
<!-- 왼쪽 영역: 입력 폼 -->
|
|
<div class="modal-form-area">
|
|
<div class="grid-form flex-col">
|
|
|
|
<!-- 1. 처리 유형 -->
|
|
<div class="form-group">
|
|
<label>1. 처리 유형 선택</label>
|
|
<div class="view-toggle w-full flex-row">
|
|
<label class="flow-type-label toggle-btn active flex-1 text-center">
|
|
<input type="radio" name="flow-type" value="checkout" checked class="hidden" />
|
|
불출 (지급)
|
|
</label>
|
|
<label class="flow-type-label toggle-btn flex-1 text-center">
|
|
<input type="radio" name="flow-type" value="return" class="hidden" />
|
|
입고 (반납)
|
|
</label>
|
|
<label class="flow-type-label toggle-btn flex-1 text-center">
|
|
<input type="radio" name="flow-type" value="move" class="hidden" />
|
|
이동 (이관)
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2. 대상 사용자 검색 -->
|
|
<div class="form-group relative">
|
|
<label id="user-search-label">2. 대상 사원 검색</label>
|
|
<div class="input-with-icon">
|
|
<input type="text" id="pc-flow-user-search" placeholder="사원명, 부서, 사번 검색..." />
|
|
<i data-lucide="search" class="icon-sm"></i>
|
|
</div>
|
|
<div id="pc-flow-user-suggestions" class="autocomplete-list hidden"></div>
|
|
</div>
|
|
|
|
<!-- 3. 새 인수자 검색 (이동 시 노출) -->
|
|
<div id="target-user-search-container" class="form-group hidden relative">
|
|
<label>새 인수 사원 검색</label>
|
|
<div class="input-with-icon">
|
|
<input type="text" id="pc-flow-target-user-search" placeholder="사원명, 부서, 사번 검색..." />
|
|
<i data-lucide="search" class="icon-sm"></i>
|
|
</div>
|
|
<div id="pc-flow-target-user-suggestions" class="autocomplete-list hidden"></div>
|
|
</div>
|
|
|
|
<!-- 4. 재고 PC 검색 (불출 시 노출) -->
|
|
<div id="stock-pc-search-container" class="form-group relative">
|
|
<label>3. 불출할 재고 PC 선택</label>
|
|
<div class="input-with-icon">
|
|
<input type="text" id="pc-flow-stock-search" placeholder="자산코드 또는 모델명 검색..." />
|
|
<i data-lucide="monitor" class="icon-sm"></i>
|
|
</div>
|
|
<div id="pc-flow-stock-suggestions" class="autocomplete-list hidden"></div>
|
|
</div>
|
|
|
|
<!-- 5. 상세 공통 입력 -->
|
|
<div class="detail-grid-2col">
|
|
<div class="form-group">
|
|
<label>처리 일자</label>
|
|
<input type="date" id="pc-flow-date" />
|
|
</div>
|
|
<div class="form-group">
|
|
<label>상세 사유</label>
|
|
<textarea id="pc-flow-details" rows="2" placeholder="미입력 시 기본 문구로 자동 입력됩니다."></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 오른쪽 영역: 선택 요약 & 사원 소유 자산 목록 -->
|
|
<div class="modal-history-area">
|
|
<div class="history-header">
|
|
<h3>선택 내역 요약</h3>
|
|
</div>
|
|
|
|
<div class="dynamic-row-container">
|
|
<!-- 사원 요약 카드 -->
|
|
<div id="summary-user-card" class="summary-info-card">
|
|
<div class="detail-label-sm">대상 사원</div>
|
|
<div id="summary-user-name" class="detail-value-lg">선택된 사원 없음</div>
|
|
<div id="summary-user-dept" class="detail-label-sm">-</div>
|
|
</div>
|
|
|
|
<!-- 인수 사원 요약 카드 (이동 전용) -->
|
|
<div id="summary-target-user-card" class="summary-info-card hidden bg-primary-light">
|
|
<div class="detail-label-sm">새 인수 사원</div>
|
|
<div id="summary-target-user-name" class="detail-value-lg">선택된 사원 없음</div>
|
|
<div id="summary-target-user-dept" class="detail-label-sm">-</div>
|
|
</div>
|
|
|
|
<!-- 대상 PC 자산 요약 카드 -->
|
|
<div id="summary-pc-card" class="summary-info-card">
|
|
<div class="detail-label-sm">대상 PC 자산</div>
|
|
<div id="summary-pc-code" class="detail-value-lg text-success">선택된 PC 없음</div>
|
|
<div id="summary-pc-model" class="detail-label-sm">-</div>
|
|
</div>
|
|
|
|
<!-- 사용자 보유 PC 목록 선택 (반납/이동 시) -->
|
|
<div id="user-pcs-container" class="form-group hidden">
|
|
<label>사원 보유 PC 선택 (클릭하여 매핑)</label>
|
|
<div id="user-pcs-list" class="user-pc-selection-list"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
<div></div>
|
|
<div class="footer-actions">
|
|
<button id="btn-cancel-pc-flow-modal" class="btn btn-outline">취소</button>
|
|
<button id="btn-submit-pc-flow" class="btn btn-primary">이동/반납 처리 완료</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
export const pcFlowModal = PCFlowModal.getInstance();
|