/* ===== 소모품 신청 모바일 (장바구니 방식) ===== */
const TKUSER_BASE_URL = location.hostname.includes('technicalkorea.net')
? 'https://tkuser.technicalkorea.net'
: location.protocol + '//' + location.hostname + ':30180';
const CAT_LABELS = { consumable: '소모품', safety: '안전용품', repair: '수선비', equipment: '설비' };
const STATUS_LABELS = { pending: '대기', grouped: '구매진행중', purchased: '구매완료', received: '입고완료', cancelled: '취소', returned: '반품', hold: '보류' };
const STATUS_COLORS = { pending: 'badge-amber', grouped: 'badge-blue', purchased: 'badge-green', received: 'badge-teal', cancelled: 'badge-red', returned: 'badge-red', hold: 'badge-gray' };
const MATCH_LABELS = { exact: '정확', name: '이름', alias: '별칭', spec: '규격', chosung: '초성', chosung_alias: '초성' };
let currentPage = 1;
let currentStatus = '';
let totalPages = 1;
let isLoadingMore = false;
let requestsList = [];
let photoBase64 = null;
let searchTimer = null;
let lastSearchResults = [];
let cartItems = []; // [{item_id, item_name, spec, maker, category, quantity, notes, is_new, ...}]
/* ===== 상태 탭 필터 ===== */
function filterByStatus(btn) {
document.querySelectorAll('.pm-tab').forEach(t => t.classList.remove('active'));
btn.classList.add('active');
currentStatus = btn.dataset.status;
currentPage = 1;
requestsList = [];
loadRequests();
}
/* ===== 신청 목록 로드 ===== */
async function loadRequests(append = false) {
try {
if (!append) {
document.getElementById('requestCards').innerHTML = '
불러오는 중...
';
}
const params = new URLSearchParams({ page: currentPage, limit: 20 });
if (currentStatus) params.set('status', currentStatus);
const res = await api('/purchase-requests/my-requests?' + params.toString());
const items = res.data || [];
totalPages = res.pagination?.totalPages || 1;
if (append) { requestsList = requestsList.concat(items); } else { requestsList = items; }
renderCards();
} catch (e) {
document.getElementById('requestCards').innerHTML = `${escapeHtml(e.message)}
`;
}
}
function renderCards() {
const container = document.getElementById('requestCards');
if (!requestsList.length) {
container.innerHTML = '신청 내역이 없습니다.
';
return;
}
container.innerHTML = requestsList.map(r => {
const itemName = r.item_name || r.custom_item_name || '-';
const category = r.category || r.custom_category;
const catLabel = CAT_LABELS[category] || category || '';
const statusLabel = STATUS_LABELS[r.status] || r.status;
const statusColor = STATUS_COLORS[r.status] || 'badge-gray';
const isCustom = !r.item_id && r.custom_item_name;
return `
수량: ${r.quantity}
${formatDate(r.request_date)}
${r.batch_name ? ` ${escapeHtml(r.batch_name)}` : ''}
`;
}).join('');
}
/* ===== 무한 스크롤 ===== */
window.addEventListener('scroll', () => {
if (isLoadingMore || currentPage >= totalPages) return;
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 200) {
isLoadingMore = true;
currentPage++;
document.getElementById('loadingMore').classList.remove('hidden');
loadRequests(true).finally(() => {
isLoadingMore = false;
document.getElementById('loadingMore').classList.add('hidden');
});
}
});
/* ===== 상세 바텀시트 ===== */
function openDetail(requestId) {
const r = requestsList.find(x => x.request_id === requestId);
if (!r) return;
const itemName = r.item_name || r.custom_item_name || '-';
const category = r.category || r.custom_category;
const catLabel = CAT_LABELS[category] || category || '-';
const statusLabel = STATUS_LABELS[r.status] || r.status;
const statusColor = STATUS_COLORS[r.status] || 'badge-gray';
let html = `
${escapeHtml(itemName)}
${statusLabel}
분류${catLabel}
수량${r.quantity}
신청일${formatDate(r.request_date)}
`;
if (r.notes) html += `메모${escapeHtml(r.notes)}
`;
if (r.batch_name) html += `구매 그룹${escapeHtml(r.batch_name)}
`;
if (r.hold_reason) html += `보류 사유${escapeHtml(r.hold_reason)}
`;
if (r.status === 'received') {
html += `입고 완료
`;
if (r.received_location) html += `
보관위치: ${escapeHtml(r.received_location)}
`;
if (r.received_at) html += `
${formatDateTime(r.received_at)}${r.received_by_name ? ' · ' + escapeHtml(r.received_by_name) : ''}
`;
if (r.received_photo_path) html += `

`;
html += `
`;
}
if (r.pr_photo_path) html += `첨부 사진

`;
document.getElementById('detailContent').innerHTML = html;
document.getElementById('detailOverlay').classList.add('open');
document.getElementById('detailSheet').classList.add('open');
}
function closeDetailSheet() {
document.getElementById('detailOverlay').classList.remove('open');
document.getElementById('detailSheet').classList.remove('open');
}
/* ===== 신청 바텀시트 ===== */
function openRequestSheet() {
document.getElementById('requestOverlay').classList.add('open');
document.getElementById('requestSheet').classList.add('open');
document.getElementById('searchInput').focus();
}
function closeRequestSheet() {
document.getElementById('requestOverlay').classList.remove('open');
document.getElementById('requestSheet').classList.remove('open');
resetRequestForm();
}
function resetRequestForm() {
document.getElementById('searchInput').value = '';
document.getElementById('searchResults').classList.remove('open');
cartItems = [];
renderCart();
document.getElementById('reqPhotoInput').value = '';
document.getElementById('reqPhotoPreview').classList.add('hidden');
document.getElementById('photoLabel').textContent = '사진 촬영/선택';
photoBase64 = null;
updateSubmitBtn();
}
/* ===== 서버 스마트 검색 ===== */
document.addEventListener('DOMContentLoaded', () => {
const input = document.getElementById('searchInput');
input.addEventListener('input', () => {
clearTimeout(searchTimer);
const q = input.value.trim();
if (q.length === 0) {
document.getElementById('searchResults').classList.remove('open');
document.getElementById('searchSpinner').classList.remove('show');
return;
}
document.getElementById('searchSpinner').classList.add('show');
searchTimer = setTimeout(async () => {
try {
const res = await api('/purchase-requests/search?q=' + encodeURIComponent(q));
renderSearchResults(res.data || [], q);
} catch (e) { console.error('검색 오류:', e); }
finally { document.getElementById('searchSpinner').classList.remove('show'); }
}, 300);
});
});
function renderSearchResults(items, query) {
lastSearchResults = items;
const container = document.getElementById('searchResults');
let html = '';
items.forEach(item => {
const catLabel = CAT_LABELS[item.category] || '';
const matchLabel = MATCH_LABELS[item._matchType] || '';
const spec = item.spec ? ' [' + escapeHtml(item.spec) + ']' : '';
const maker = item.maker ? ' (' + escapeHtml(item.maker) + ')' : '';
html += `
${escapeHtml(item.item_name)}${spec}${maker}
${catLabel ? `
${catLabel}
` : ''}
${matchLabel ? `
${matchLabel}` : ''}
`;
});
html += `
"${escapeHtml(query)}" 새 품목으로 추가
`;
container.innerHTML = html;
container.classList.add('open');
}
/* ===== 장바구니 ===== */
function addToCart(itemId) {
const item = lastSearchResults.find(i => i.item_id === itemId);
if (!item) return;
// 동일 품목이면 수량 +1
const existing = cartItems.find(c => c.item_id === item.item_id && !c.is_new);
if (existing) {
existing.quantity++;
showToast(`${item.item_name} 수량이 ${existing.quantity}개로 추가되었습니다.`);
} else {
cartItems.push({
item_id: item.item_id,
item_name: item.item_name,
spec: item.spec,
maker: item.maker,
category: item.category,
quantity: 1,
notes: '',
is_new: false
});
}
document.getElementById('searchInput').value = '';
document.getElementById('searchResults').classList.remove('open');
renderCart();
updateSubmitBtn();
}
function addNewToCart() {
const query = document.getElementById('searchInput').value.trim();
if (!query) return;
cartItems.push({
item_id: null,
item_name: query,
spec: '',
maker: '',
category: '',
quantity: 1,
notes: '',
is_new: true
});
document.getElementById('searchInput').value = '';
document.getElementById('searchResults').classList.remove('open');
renderCart();
updateSubmitBtn();
}
function removeFromCart(idx) {
cartItems.splice(idx, 1);
renderCart();
updateSubmitBtn();
}
function updateCartQty(idx, val) {
const qty = parseInt(val) || 1;
cartItems[idx].quantity = Math.max(1, qty);
}
function updateCartNotes(idx, val) {
cartItems[idx].notes = val;
}
function updateCartNewField(idx, field, val) {
cartItems[idx][field] = val;
}
function renderCart() {
const wrap = document.getElementById('cartWrap');
const list = document.getElementById('cartList');
const count = document.getElementById('cartCount');
if (cartItems.length === 0) {
wrap.classList.add('hidden');
return;
}
wrap.classList.remove('hidden');
count.textContent = cartItems.length + '건';
list.innerHTML = cartItems.map((c, idx) => {
const spec = c.spec ? ' [' + escapeHtml(c.spec) + ']' : '';
const maker = c.maker ? ' (' + escapeHtml(c.maker) + ')' : '';
const catLabel = CAT_LABELS[c.category] || '';
let newFields = '';
if (c.is_new) {
newFields = `
`;
}
return ``;
}).join('');
}
function updateSubmitBtn() {
const btn = document.getElementById('submitBtn');
if (cartItems.length > 0) {
btn.disabled = false;
btn.textContent = cartItems.length + '건 신청하기';
} else {
btn.disabled = true;
btn.textContent = '품목을 추가해주세요';
}
}
/* ===== 사진 ===== */
async function onMobilePhotoSelected(inputEl) {
const file = inputEl.files[0];
if (!file) return;
let processFile = file;
const isHeic = file.type === 'image/heic' || file.type === 'image/heif' ||
file.name.toLowerCase().endsWith('.heic') || file.name.toLowerCase().endsWith('.heif');
if (isHeic && typeof heic2any !== 'undefined') {
document.getElementById('photoLabel').textContent = '변환 중...';
try {
const blob = await heic2any({ blob: file, toType: 'image/jpeg', quality: 0.85 });
processFile = new File([blob], file.name.replace(/\.hei[cf]$/i, '.jpg'), { type: 'image/jpeg' });
} catch (e) { console.warn('HEIC 변환 실패, 원본 사용:', e); }
}
if (processFile.size > 10 * 1024 * 1024) {
showToast('파일 크기는 10MB 이하만 가능합니다.', 'error');
document.getElementById('photoLabel').textContent = '사진 촬영/선택';
return;
}
const reader = new FileReader();
reader.onload = (e) => {
photoBase64 = e.target.result;
document.getElementById('reqPhotoPreview').src = photoBase64;
document.getElementById('reqPhotoPreview').classList.remove('hidden');
document.getElementById('photoLabel').textContent = '사진 변경';
};
reader.readAsDataURL(processFile);
}
/* ===== 일괄 신청 제출 ===== */
async function submitRequest() {
if (cartItems.length === 0) { showToast('품목을 추가해주세요.', 'error'); return; }
const btn = document.getElementById('submitBtn');
btn.disabled = true;
btn.textContent = '처리 중...';
try {
const items = cartItems.map(c => {
if (c.is_new) {
return { item_name: c.item_name, spec: c.spec || null, maker: c.maker || null, category: c.category || null, quantity: c.quantity, notes: c.notes || null, is_new: true };
}
return { item_id: c.item_id, quantity: c.quantity, notes: c.notes || null };
});
const body = { items };
if (photoBase64) body.photo = photoBase64;
await api('/purchase-requests/bulk', { method: 'POST', body: JSON.stringify(body) });
showToast(`${cartItems.length}건 소모품 신청이 등록되었습니다.`);
closeRequestSheet();
currentPage = 1;
requestsList = [];
await loadRequests();
} catch (e) {
showToast(e.message, 'error');
} finally {
btn.disabled = false;
updateSubmitBtn();
}
}
/* ===== URL 파라미터로 상세 열기 ===== */
function checkViewParam() {
const urlParams = new URLSearchParams(location.search);
const viewId = urlParams.get('view');
if (viewId) setTimeout(() => openDetail(parseInt(viewId)), 500);
}
/* ===== Init ===== */
(async function() {
if (!await initAuth()) return;
await loadRequests();
checkViewParam();
})();