/* ===== tkuser 소모품 마스터 CRUD ===== */
let consumablesLoaded = false;
let consumablesList = [];
// 카테고리 — system1-factory API에서 동적 로드 (fallback: 기본 4개)
const TKFB_API = location.hostname.includes('technicalkorea.net')
? 'https://tkfb.technicalkorea.net/api'
: location.protocol + '//' + location.hostname + ':30005/api';
let _consumableCategories = null;
const DEFAULT_CATEGORIES = [
{ category_code: 'consumable', category_name: '소모품', icon: 'fa-box', color_bg: '#dbeafe', color_fg: '#1e40af' },
{ category_code: 'safety', category_name: '안전용품', icon: 'fa-hard-hat', color_bg: '#dcfce7', color_fg: '#166534' },
{ category_code: 'repair', category_name: '수선비', icon: 'fa-wrench', color_bg: '#fef3c7', color_fg: '#92400e' },
{ category_code: 'equipment', category_name: '설비', icon: 'fa-cogs', color_bg: '#f3e8ff', color_fg: '#7e22ce' }
];
async function loadConsumableCategories() {
if (_consumableCategories) return _consumableCategories;
try {
const token = getToken();
const res = await fetch(TKFB_API + '/consumable-categories', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (res.ok) {
const data = await res.json();
_consumableCategories = data.data || DEFAULT_CATEGORIES;
} else {
_consumableCategories = DEFAULT_CATEGORIES;
}
} catch (e) {
_consumableCategories = DEFAULT_CATEGORIES;
}
return _consumableCategories;
}
function getCatLabel(code) { return (_consumableCategories || DEFAULT_CATEGORIES).find(c => c.category_code === code)?.category_name || code; }
function getCatColorClass(code) {
const c = (_consumableCategories || DEFAULT_CATEGORIES).find(x => x.category_code === code);
if (!c) return 'bg-gray-50 text-gray-600';
// 인라인 스타일 반환
return '';
}
function getCatStyle(code) {
const c = (_consumableCategories || DEFAULT_CATEGORIES).find(x => x.category_code === code);
return c ? `background:${c.color_bg};color:${c.color_fg}` : 'background:#f3f4f6;color:#6b7280';
}
async function loadConsumablesTab() {
if (consumablesLoaded) return;
consumablesLoaded = true;
await loadConsumableCategories();
if (currentUser && ['admin', 'system'].includes(currentUser.role)) {
document.getElementById('btnAddConsumableTkuser')?.classList.remove('hidden');
document.getElementById('categoryManageSection')?.classList.remove('hidden');
}
populateCategorySelects();
await loadConsumablesList();
loadCategoryManagement();
}
// 카테고리 select 옵션 동적 생성
function populateCategorySelects() {
const cats = _consumableCategories || DEFAULT_CATEGORIES;
const optionsHtml = cats.map(c => ``).join('');
['consumableFilterCategoryTkuser', 'newConsumableCategoryTkuser', 'editConsumableCategoryTkuser'].forEach(id => {
const el = document.getElementById(id);
if (!el) return;
const firstOpt = el.querySelector('option:first-child');
el.innerHTML = (firstOpt ? firstOpt.outerHTML : '') + optionsHtml;
});
}
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 = `
`;
}
}
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 = getCatLabel(item.category);
const catStyle = getCatStyle(item.category);
const price = item.base_price ? Number(item.base_price).toLocaleString() + '원' : '-';
return `
${item.photo_path
? `

`
: `
`}
${escHtml(item.item_name)}${item.spec ? ' [' + escHtml(item.spec) + ']' : ''}
${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('spec', document.getElementById('newConsumableSpecTkuser').value.trim());
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('editConsumableSpecTkuser').value = item.spec || '';
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('spec', document.getElementById('editConsumableSpecTkuser').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'); }
}
/* ===== 카테고리 관리 ===== */
async function loadCategoryManagement() {
const container = document.getElementById('categoryList');
if (!container) return;
const cats = _consumableCategories || DEFAULT_CATEGORIES;
container.innerHTML = cats.map(c => `
${escHtml(c.category_name)}
(${escHtml(c.category_code)})
${c.category_id ? `
` : ''}
`).join('');
}
async function addCategory() {
const code = prompt('카테고리 코드 (영문):');
if (!code) return;
const name = prompt('표시 이름:');
if (!name) return;
try {
const token = getToken();
const res = await fetch(TKFB_API + '/consumable-categories', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ category_code: code, category_name: name })
});
const data = await res.json();
if (!res.ok) throw new Error(data.message || '추가 실패');
showToast('카테고리가 추가되었습니다');
_consumableCategories = null;
await loadConsumableCategories();
populateCategorySelects();
loadCategoryManagement();
} 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);
});