- tkuser 탭을 5개 카테고리로 그룹핑 (인력/현장/업무/거래/시스템) - 설비 관리 탭 신규 추가 (CRUD, 필터, 상세 보기) - tkfb 사이드바 admin 메뉴 6개를 tkuser 외부 링크로 교체 - tkfb admin HTML 6개를 tkuser 리다이렉트로 변경 - gateway 알림 벨 링크를 tkuser로 변경 - _tkuserBase 헬퍼로 개발/운영 환경 자동 분기 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
254 lines
15 KiB
JavaScript
254 lines
15 KiB
JavaScript
/* ===== tkuser 설비(Equipment) CRUD ===== */
|
|
let equipmentsLoaded = false;
|
|
let equipmentsList = [];
|
|
let selectedEquipmentIdTkuser = null;
|
|
|
|
const EQUIPMENT_STATUS_MAP = {
|
|
active: { label: '활성', cls: 'bg-green-100 text-green-700' },
|
|
maintenance: { label: '정비중', cls: 'bg-yellow-100 text-yellow-700' },
|
|
inactive: { label: '비활성', cls: 'bg-gray-100 text-gray-400' }
|
|
};
|
|
|
|
async function loadEquipmentsTab() {
|
|
if (equipmentsLoaded) return;
|
|
equipmentsLoaded = true;
|
|
if (currentUser && ['admin', 'system'].includes(currentUser.role)) {
|
|
document.getElementById('btnAddEquipment')?.classList.remove('hidden');
|
|
}
|
|
await Promise.all([loadEquipmentFilters(), loadEquipmentsList()]);
|
|
}
|
|
|
|
async function loadEquipmentFilters() {
|
|
try {
|
|
// 작업장 필터
|
|
const wRes = await api('/workplaces');
|
|
const workplaces = wRes.data || [];
|
|
const wSel = document.getElementById('equipmentFilterWorkplace');
|
|
if (wSel) {
|
|
const current = wSel.value;
|
|
wSel.innerHTML = '<option value="">전체 작업장</option>' +
|
|
workplaces.map(w => `<option value="${w.workplace_id}">${escHtml(w.workplace_name)}</option>`).join('');
|
|
wSel.value = current;
|
|
}
|
|
// 유형 필터
|
|
const tRes = await api('/equipments/types');
|
|
const types = tRes.data || [];
|
|
const tSel = document.getElementById('equipmentFilterType');
|
|
if (tSel) {
|
|
const current = tSel.value;
|
|
tSel.innerHTML = '<option value="">전체 유형</option>' +
|
|
types.map(t => `<option value="${escHtml(t)}">${escHtml(t)}</option>`).join('');
|
|
tSel.value = current;
|
|
}
|
|
// 모달 select 채우기 (작업장)
|
|
['newEquipmentWorkplaceTkuser', 'editEquipmentWorkplaceTkuser'].forEach(id => {
|
|
const el = document.getElementById(id);
|
|
if (el) el.innerHTML = '<option value="">선택</option>' +
|
|
workplaces.map(w => `<option value="${w.workplace_id}">${escHtml(w.workplace_name)}</option>`).join('');
|
|
});
|
|
} catch (e) { /* 필터 로드 실패는 무시 */ }
|
|
}
|
|
|
|
async function loadEquipmentsList() {
|
|
try {
|
|
const workplaceId = document.getElementById('equipmentFilterWorkplace')?.value || '';
|
|
const eqType = document.getElementById('equipmentFilterType')?.value || '';
|
|
const status = document.getElementById('equipmentFilterStatus')?.value || '';
|
|
const search = document.getElementById('equipmentSearchTkuser')?.value?.trim() || '';
|
|
const params = new URLSearchParams();
|
|
if (workplaceId) params.set('workplace_id', workplaceId);
|
|
if (eqType) params.set('equipment_type', eqType);
|
|
if (status) params.set('status', status);
|
|
if (search) params.set('search', search);
|
|
const r = await api('/equipments?' + params.toString());
|
|
equipmentsList = r.data || [];
|
|
renderEquipmentsListTkuser();
|
|
} catch (e) {
|
|
document.getElementById('equipmentsListTkuser').innerHTML = `<div class="text-red-500 text-center py-6"><i class="fas fa-exclamation-triangle text-xl"></i><p class="text-sm mt-2">${e.message}</p></div>`;
|
|
}
|
|
}
|
|
|
|
function renderEquipmentsListTkuser() {
|
|
const c = document.getElementById('equipmentsListTkuser');
|
|
if (!equipmentsList.length) {
|
|
c.innerHTML = '<p class="text-gray-400 text-center py-4 text-sm">등록된 설비가 없습니다.</p>';
|
|
return;
|
|
}
|
|
const isAdmin = currentUser && ['admin', 'system'].includes(currentUser.role);
|
|
c.innerHTML = equipmentsList.map(eq => {
|
|
const st = EQUIPMENT_STATUS_MAP[eq.status] || EQUIPMENT_STATUS_MAP.active;
|
|
return `<div class="flex items-center justify-between p-2.5 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer ${selectedEquipmentIdTkuser === eq.equipment_id ? 'ring-2 ring-indigo-400' : ''}" onclick="selectEquipmentTkuser(${eq.equipment_id})">
|
|
<div class="flex-1 min-w-0">
|
|
<div class="text-sm font-medium text-gray-800 truncate">
|
|
<i class="fas fa-cog mr-1.5 text-gray-400 text-xs"></i>${escHtml(eq.equipment_name)}
|
|
<span class="px-1.5 py-0.5 rounded text-xs ${st.cls} ml-1">${st.label}</span>
|
|
</div>
|
|
<div class="text-xs text-gray-500 flex items-center gap-1.5 mt-0.5">
|
|
<span>${escHtml(eq.equipment_code)}</span>
|
|
${eq.workplace_name ? `<span>· ${escHtml(eq.workplace_name)}</span>` : ''}
|
|
${eq.equipment_type ? `<span>· ${escHtml(eq.equipment_type)}</span>` : ''}
|
|
</div>
|
|
</div>
|
|
${isAdmin ? `<div class="flex gap-1 ml-2 flex-shrink-0">
|
|
<button onclick="event.stopPropagation(); openEditEquipmentTkuser(${eq.equipment_id})" class="p-1.5 text-slate-500 hover:text-slate-700 hover:bg-slate-200 rounded" title="수정"><i class="fas fa-pen text-xs"></i></button>
|
|
<button onclick="event.stopPropagation(); deleteEquipmentTkuser(${eq.equipment_id}, '${escHtml(eq.equipment_name).replace(/'/g, "\\'")}')" class="p-1.5 text-red-400 hover:text-red-600 hover:bg-red-100 rounded" title="삭제"><i class="fas fa-trash text-xs"></i></button>
|
|
</div>` : ''}
|
|
</div>`;
|
|
}).join('');
|
|
}
|
|
|
|
async function selectEquipmentTkuser(id) {
|
|
selectedEquipmentIdTkuser = id;
|
|
renderEquipmentsListTkuser();
|
|
try {
|
|
const r = await api(`/equipments/${id}`);
|
|
const eq = r.data;
|
|
renderEquipmentDetailTkuser(eq);
|
|
document.getElementById('equipmentDetailTkuser').classList.remove('hidden');
|
|
document.getElementById('equipmentEmptyTkuser').classList.add('hidden');
|
|
} catch (e) {
|
|
showToast('상세 조회 실패: ' + e.message, 'error');
|
|
}
|
|
}
|
|
|
|
function renderEquipmentDetailTkuser(eq) {
|
|
const st = EQUIPMENT_STATUS_MAP[eq.status] || EQUIPMENT_STATUS_MAP.active;
|
|
const installDate = eq.installation_date ? eq.installation_date.substring(0, 10) : '-';
|
|
document.getElementById('equipmentDetailTkuser').innerHTML = `
|
|
<div class="bg-white rounded-xl shadow-sm p-5">
|
|
<div class="flex items-center justify-between mb-3">
|
|
<h3 class="text-lg font-semibold text-gray-800">${escHtml(eq.equipment_name)}</h3>
|
|
<span class="px-2 py-1 rounded text-xs font-medium ${st.cls}">${st.label}</span>
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-3 text-sm">
|
|
<div><span class="text-gray-500">관리번호:</span> <span class="font-medium">${escHtml(eq.equipment_code)}</span></div>
|
|
<div><span class="text-gray-500">설비유형:</span> <span class="font-medium">${escHtml(eq.equipment_type) || '-'}</span></div>
|
|
<div><span class="text-gray-500">작업장:</span> <span class="font-medium">${escHtml(eq.workplace_name) || '-'}</span></div>
|
|
<div><span class="text-gray-500">제조사:</span> <span class="font-medium">${escHtml(eq.manufacturer) || '-'}</span></div>
|
|
<div><span class="text-gray-500">모델명:</span> <span class="font-medium">${escHtml(eq.model_name) || '-'}</span></div>
|
|
<div><span class="text-gray-500">시리얼번호:</span> <span class="font-medium">${escHtml(eq.serial_number) || '-'}</span></div>
|
|
<div><span class="text-gray-500">설치일:</span> <span class="font-medium">${installDate}</span></div>
|
|
<div><span class="text-gray-500">공급업체:</span> <span class="font-medium">${escHtml(eq.supplier) || '-'}</span></div>
|
|
${eq.purchase_price ? `<div><span class="text-gray-500">구매가격:</span> <span class="font-medium">${Number(eq.purchase_price).toLocaleString()}원</span></div>` : ''}
|
|
${eq.specifications ? `<div class="col-span-2"><span class="text-gray-500">사양:</span> <span class="font-medium">${escHtml(eq.specifications)}</span></div>` : ''}
|
|
${eq.notes ? `<div class="col-span-2"><span class="text-gray-500">비고:</span> <span class="font-medium">${escHtml(eq.notes)}</span></div>` : ''}
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
/* ===== 설비 등록 ===== */
|
|
function openAddEquipmentTkuser() {
|
|
// 다음 코드 자동 생성
|
|
api('/equipments/next-code').then(r => {
|
|
document.getElementById('newEquipmentCodeTkuser').value = r.data || '';
|
|
}).catch(() => {});
|
|
document.getElementById('addEquipmentModalTkuser').classList.remove('hidden');
|
|
}
|
|
function closeAddEquipmentTkuser() { document.getElementById('addEquipmentModalTkuser').classList.add('hidden'); document.getElementById('addEquipmentFormTkuser').reset(); }
|
|
|
|
async function submitAddEquipmentTkuser(e) {
|
|
e.preventDefault();
|
|
const data = {
|
|
equipment_code: document.getElementById('newEquipmentCodeTkuser').value.trim(),
|
|
equipment_name: document.getElementById('newEquipmentNameTkuser').value.trim(),
|
|
equipment_type: document.getElementById('newEquipmentTypeTkuser').value.trim() || null,
|
|
workplace_id: document.getElementById('newEquipmentWorkplaceTkuser').value || null,
|
|
manufacturer: document.getElementById('newEquipmentManufacturerTkuser').value.trim() || null,
|
|
model_name: document.getElementById('newEquipmentModelTkuser').value.trim() || null,
|
|
serial_number: document.getElementById('newEquipmentSerialTkuser').value.trim() || null,
|
|
installation_date: document.getElementById('newEquipmentInstallDateTkuser').value || null,
|
|
supplier: document.getElementById('newEquipmentSupplierTkuser').value.trim() || null,
|
|
purchase_price: document.getElementById('newEquipmentPriceTkuser').value || null,
|
|
status: document.getElementById('newEquipmentStatusTkuser').value || 'active',
|
|
specifications: document.getElementById('newEquipmentSpecsTkuser').value.trim() || null,
|
|
notes: document.getElementById('newEquipmentNotesTkuser').value.trim() || null,
|
|
};
|
|
if (!data.equipment_code) { showToast('관리번호는 필수입니다', 'error'); return; }
|
|
if (!data.equipment_name) { showToast('설비명은 필수입니다', 'error'); return; }
|
|
try {
|
|
await api('/equipments', { method: 'POST', body: JSON.stringify(data) });
|
|
showToast('설비가 등록되었습니다');
|
|
closeAddEquipmentTkuser();
|
|
await loadEquipmentFilters();
|
|
await loadEquipmentsList();
|
|
} catch (e) { showToast(e.message, 'error'); }
|
|
}
|
|
|
|
/* ===== 설비 수정 ===== */
|
|
function openEditEquipmentTkuser(id) {
|
|
const eq = equipmentsList.find(x => x.equipment_id === id);
|
|
if (!eq) return;
|
|
document.getElementById('editEquipmentIdTkuser').value = eq.equipment_id;
|
|
document.getElementById('editEquipmentCodeTkuser').value = eq.equipment_code;
|
|
document.getElementById('editEquipmentNameTkuser').value = eq.equipment_name;
|
|
document.getElementById('editEquipmentTypeTkuser').value = eq.equipment_type || '';
|
|
document.getElementById('editEquipmentWorkplaceTkuser').value = eq.workplace_id || '';
|
|
document.getElementById('editEquipmentManufacturerTkuser').value = eq.manufacturer || '';
|
|
document.getElementById('editEquipmentModelTkuser').value = eq.model_name || '';
|
|
document.getElementById('editEquipmentSerialTkuser').value = eq.serial_number || '';
|
|
document.getElementById('editEquipmentInstallDateTkuser').value = eq.installation_date ? eq.installation_date.substring(0, 10) : '';
|
|
document.getElementById('editEquipmentSupplierTkuser').value = eq.supplier || '';
|
|
document.getElementById('editEquipmentPriceTkuser').value = eq.purchase_price || '';
|
|
document.getElementById('editEquipmentStatusTkuser').value = eq.status || 'active';
|
|
document.getElementById('editEquipmentSpecsTkuser').value = eq.specifications || '';
|
|
document.getElementById('editEquipmentNotesTkuser').value = eq.notes || '';
|
|
document.getElementById('editEquipmentModalTkuser').classList.remove('hidden');
|
|
}
|
|
function closeEditEquipmentTkuser() { document.getElementById('editEquipmentModalTkuser').classList.add('hidden'); }
|
|
|
|
async function submitEditEquipmentTkuser(e) {
|
|
e.preventDefault();
|
|
const id = document.getElementById('editEquipmentIdTkuser').value;
|
|
const data = {
|
|
equipment_code: document.getElementById('editEquipmentCodeTkuser').value.trim(),
|
|
equipment_name: document.getElementById('editEquipmentNameTkuser').value.trim(),
|
|
equipment_type: document.getElementById('editEquipmentTypeTkuser').value.trim() || null,
|
|
workplace_id: document.getElementById('editEquipmentWorkplaceTkuser').value || null,
|
|
manufacturer: document.getElementById('editEquipmentManufacturerTkuser').value.trim() || null,
|
|
model_name: document.getElementById('editEquipmentModelTkuser').value.trim() || null,
|
|
serial_number: document.getElementById('editEquipmentSerialTkuser').value.trim() || null,
|
|
installation_date: document.getElementById('editEquipmentInstallDateTkuser').value || null,
|
|
supplier: document.getElementById('editEquipmentSupplierTkuser').value.trim() || null,
|
|
purchase_price: document.getElementById('editEquipmentPriceTkuser').value || null,
|
|
status: document.getElementById('editEquipmentStatusTkuser').value || 'active',
|
|
specifications: document.getElementById('editEquipmentSpecsTkuser').value.trim() || null,
|
|
notes: document.getElementById('editEquipmentNotesTkuser').value.trim() || null,
|
|
};
|
|
try {
|
|
await api(`/equipments/${id}`, { method: 'PUT', body: JSON.stringify(data) });
|
|
showToast('수정되었습니다');
|
|
closeEditEquipmentTkuser();
|
|
await loadEquipmentFilters();
|
|
await loadEquipmentsList();
|
|
if (selectedEquipmentIdTkuser == id) selectEquipmentTkuser(id);
|
|
} catch (e) { showToast(e.message, 'error'); }
|
|
}
|
|
|
|
/* ===== 설비 삭제 ===== */
|
|
async function deleteEquipmentTkuser(id, name) {
|
|
if (!confirm(`"${name}" 설비를 삭제하시겠습니까?`)) return;
|
|
try {
|
|
await api(`/equipments/${id}`, { method: 'DELETE' });
|
|
showToast('삭제 완료');
|
|
await loadEquipmentsList();
|
|
if (selectedEquipmentIdTkuser === id) {
|
|
document.getElementById('equipmentDetailTkuser').classList.add('hidden');
|
|
document.getElementById('equipmentEmptyTkuser').classList.remove('hidden');
|
|
selectedEquipmentIdTkuser = null;
|
|
}
|
|
} catch (e) { showToast(e.message, 'error'); }
|
|
}
|
|
|
|
/* ===== 필터 ===== */
|
|
let equipmentSearchTimeout;
|
|
function filterEquipmentsTkuser() {
|
|
clearTimeout(equipmentSearchTimeout);
|
|
equipmentSearchTimeout = setTimeout(loadEquipmentsList, 300);
|
|
}
|
|
|
|
// 검색/필터 이벤트 + 모달 폼 이벤트
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
document.getElementById('addEquipmentFormTkuser')?.addEventListener('submit', submitAddEquipmentTkuser);
|
|
document.getElementById('editEquipmentFormTkuser')?.addEventListener('submit', submitEditEquipmentTkuser);
|
|
});
|