/* ===== tkuser 소모품 마스터 CRUD ===== */ let consumablesLoaded = false; let consumablesList = []; const CONSUMABLE_CATEGORIES = { consumable: '소모품', safety: '안전용품', repair: '수선비', equipment: '설비' }; const CONSUMABLE_CAT_COLORS = { consumable: 'bg-blue-50 text-blue-600', safety: 'bg-green-50 text-green-600', repair: 'bg-amber-50 text-amber-600', equipment: 'bg-purple-50 text-purple-600' }; async function loadConsumablesTab() { if (consumablesLoaded) return; consumablesLoaded = true; if (currentUser && ['admin', 'system'].includes(currentUser.role)) { document.getElementById('btnAddConsumableTkuser')?.classList.remove('hidden'); } await loadConsumablesList(); } async function loadConsumablesList() { try { const category = document.getElementById('consumableFilterCategoryTkuser')?.value || ''; const isActive = document.getElementById('consumableFilterActiveTkuser')?.value; const search = document.getElementById('consumableSearchTkuser')?.value?.trim() || ''; const params = new URLSearchParams(); if (category) params.set('category', category); if (isActive !== '' && isActive !== undefined) params.set('is_active', isActive); if (search) params.set('search', search); const r = await api('/consumable-items?' + params.toString()); consumablesList = r.data || []; renderConsumablesListTkuser(); } catch (e) { document.getElementById('consumablesListTkuser').innerHTML = `

${e.message}

`; } } function renderConsumablesListTkuser() { const c = document.getElementById('consumablesListTkuser'); if (!consumablesList.length) { c.innerHTML = '

등록된 소모품이 없습니다.

'; return; } const isAdmin = currentUser && ['admin', 'system'].includes(currentUser.role); c.innerHTML = `
` + consumablesList.map(item => { const catLabel = CONSUMABLE_CATEGORIES[item.category] || item.category; const catColor = CONSUMABLE_CAT_COLORS[item.category] || 'bg-gray-50 text-gray-600'; const price = item.base_price ? Number(item.base_price).toLocaleString() + '원' : '-'; return `
${item.photo_path ? `` : `
`}
${escHtml(item.item_name)}
${escHtml(item.maker) || '-'}
${catLabel} ${price} ${escHtml(item.unit) || 'EA'}
${!item.is_active ? '비활성' : ''}
${isAdmin ? `
${item.is_active ? `` : ''}
` : ''}
`; }).join('') + `
`; } /* ===== 소모품 등록 ===== */ function openAddConsumableTkuser() { document.getElementById('addConsumablePhotoPreviewTkuser').innerHTML = ''; document.getElementById('addConsumableModalTkuser').classList.remove('hidden'); } function closeAddConsumableTkuser() { document.getElementById('addConsumableModalTkuser').classList.add('hidden'); document.getElementById('addConsumableFormTkuser').reset(); document.getElementById('addConsumablePhotoPreviewTkuser').innerHTML = ''; } function previewAddConsumablePhoto() { const file = document.getElementById('newConsumablePhotoTkuser').files[0]; const preview = document.getElementById('addConsumablePhotoPreviewTkuser'); if (!file) { preview.innerHTML = ''; return; } const reader = new FileReader(); reader.onload = e => { preview.innerHTML = ``; }; reader.readAsDataURL(file); } async function submitAddConsumableTkuser(e) { e.preventDefault(); const itemName = document.getElementById('newConsumableNameTkuser').value.trim(); const category = document.getElementById('newConsumableCategoryTkuser').value; if (!itemName) { showToast('품명은 필수입니다', 'error'); return; } if (!category) { showToast('분류는 필수입니다', 'error'); return; } const fd = new FormData(); fd.append('item_name', itemName); fd.append('maker', document.getElementById('newConsumableMakerTkuser').value.trim()); fd.append('category', category); fd.append('base_price', document.getElementById('newConsumablePriceTkuser').value || '0'); fd.append('unit', document.getElementById('newConsumableUnitTkuser').value.trim() || 'EA'); const photoFile = document.getElementById('newConsumablePhotoTkuser').files[0]; if (photoFile) fd.append('photo', photoFile); try { const token = getToken(); const res = await fetch('/api/consumable-items', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }, body: fd }); const data = await res.json(); if (!res.ok) throw new Error(data.error || '등록 실패'); showToast('소모품이 등록되었습니다'); closeAddConsumableTkuser(); await loadConsumablesList(); } catch (e) { showToast(e.message, 'error'); } } /* ===== 소모품 수정 ===== */ function openEditConsumableTkuser(id) { const item = consumablesList.find(x => x.item_id === id); if (!item) return; document.getElementById('editConsumableIdTkuser').value = item.item_id; document.getElementById('editConsumableNameTkuser').value = item.item_name; document.getElementById('editConsumableMakerTkuser').value = item.maker || ''; document.getElementById('editConsumableCategoryTkuser').value = item.category; document.getElementById('editConsumablePriceTkuser').value = item.base_price || ''; document.getElementById('editConsumableUnitTkuser').value = item.unit || 'EA'; const preview = document.getElementById('editConsumablePhotoPreviewTkuser'); preview.innerHTML = item.photo_path ? `` : ''; document.getElementById('editConsumablePhotoTkuser').value = ''; document.getElementById('editConsumableModalTkuser').classList.remove('hidden'); } function closeEditConsumableTkuser() { document.getElementById('editConsumableModalTkuser').classList.add('hidden'); } function previewEditConsumablePhoto() { const file = document.getElementById('editConsumablePhotoTkuser').files[0]; const preview = document.getElementById('editConsumablePhotoPreviewTkuser'); if (!file) return; const reader = new FileReader(); reader.onload = e => { preview.innerHTML = ``; }; reader.readAsDataURL(file); } async function submitEditConsumableTkuser(e) { e.preventDefault(); const id = document.getElementById('editConsumableIdTkuser').value; const fd = new FormData(); fd.append('item_name', document.getElementById('editConsumableNameTkuser').value.trim()); fd.append('maker', document.getElementById('editConsumableMakerTkuser').value.trim()); fd.append('category', document.getElementById('editConsumableCategoryTkuser').value); fd.append('base_price', document.getElementById('editConsumablePriceTkuser').value || '0'); fd.append('unit', document.getElementById('editConsumableUnitTkuser').value.trim() || 'EA'); const photoFile = document.getElementById('editConsumablePhotoTkuser').files[0]; if (photoFile) fd.append('photo', photoFile); try { const token = getToken(); const res = await fetch(`/api/consumable-items/${id}`, { method: 'PUT', headers: { 'Authorization': `Bearer ${token}` }, body: fd }); const data = await res.json(); if (!res.ok) throw new Error(data.error || '수정 실패'); showToast('수정되었습니다'); closeEditConsumableTkuser(); await loadConsumablesList(); } catch (e) { showToast(e.message, 'error'); } } /* ===== 소모품 비활성화 ===== */ async function deactivateConsumableTkuser(id, name) { if (!confirm(`"${name}" 소모품을 비활성화하시겠습니까?`)) return; try { await api(`/consumable-items/${id}`, { method: 'DELETE' }); showToast('비활성화 완료'); await loadConsumablesList(); } catch (e) { showToast(e.message, 'error'); } } // 검색/필터 이벤트 + 모달 폼 이벤트 document.addEventListener('DOMContentLoaded', () => { let searchTimeout; const searchEl = document.getElementById('consumableSearchTkuser'); if (searchEl) searchEl.addEventListener('input', () => { clearTimeout(searchTimeout); searchTimeout = setTimeout(loadConsumablesList, 300); }); const filterCatEl = document.getElementById('consumableFilterCategoryTkuser'); if (filterCatEl) filterCatEl.addEventListener('change', loadConsumablesList); const filterActiveEl = document.getElementById('consumableFilterActiveTkuser'); if (filterActiveEl) filterActiveEl.addEventListener('change', loadConsumablesList); document.getElementById('addConsumableFormTkuser')?.addEventListener('submit', submitAddConsumableTkuser); document.getElementById('editConsumableFormTkuser')?.addEventListener('submit', submitEditConsumableTkuser); });