let currentItems = []; function refreshAll() { fetchInventory(); fetchSalesStats(); } async function fetchInventory() { try { const res = await fetch('/items'); if (res.ok) { currentItems = await res.json(); renderTable('expiry'); } } catch (e) { console.error(e); } } function renderTable(sortKey) { const tbody = document.getElementById('inventoryBody'); if (sortKey === 'expiry') { currentItems.sort((a, b) => new Date(a.expiry_date) - new Date(b.expiry_date)); } else if (sortKey === 'stock') { currentItems.sort((a, b) => a.stock - b.stock); } const today = new Date(); today.setHours(0,0,0,0); let html = currentItems.map(item => { const expiryDate = new Date(item.expiry_date); const diffTime = expiryDate - today; const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); let dateClass = 'expiry-good'; let dateText = `D-${diffDays}`; if (diffDays < 0) { dateClass = 'expiry-expired'; dateText = '만료됨'; } else if (diffDays === 0) { dateClass = 'expiry-warning'; dateText = '오늘 만료'; } else if (diffDays <= 3) { dateClass = 'expiry-warning'; dateText = `${diffDays}일 남음`; } let stockHtml = ''; if (item.stock === 0) { stockHtml = `부족 (${item.stock})`; } else { stockHtml = `${item.stock}개`; } return ` #${item.id} ${item.name} ${item.price.toLocaleString()}원 ${item.expiry_date} (${dateText}) ${stockHtml} `; }).join(''); tbody.innerHTML = html || '등록된 상품이 없습니다.'; } let myChart = null; // ----- 결제 정보 ----- async function fetchSalesStats() { try { const res = await fetch('/payments'); if (!res.ok) return; const payments = await res.json(); const today = new Date(); const thirtyDaysAgo = new Date(); thirtyDaysAgo.setDate(today.getDate() - 30); let monthTotal = 0; let monthCount = 0; const dailySales = {}; for (let i = 0; i < 30; i++) { const d = new Date(); d.setDate(today.getDate() - i); dailySales[d.toISOString().split('T')[0]] = 0; } payments.forEach(pay => { const payDate = new Date(pay.created_at); if (payDate >= thirtyDaysAgo) { monthTotal += pay.total_price; monthCount += 1; const dateStr = pay.created_at.split('T')[0]; if (dailySales[dateStr] !== undefined) dailySales[dateStr] += pay.total_price; } }); document.getElementById('monthTotal').innerText = monthTotal.toLocaleString() + '원'; document.getElementById('monthCount').innerText = monthCount + '건'; updateChart(dailySales); } catch (e) { console.error(e); } } function updateChart(dailyData) { const sortedDates = Object.keys(dailyData).sort(); const dataValues = sortedDates.map(date => dailyData[date]); const labels = sortedDates.map(date => date.slice(5)); const ctx = document.getElementById('salesChart').getContext('2d'); if (myChart) myChart.destroy(); myChart = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: '매출 (원)', data: dataValues, borderColor: '#8b5cf6', backgroundColor: 'rgba(139, 92, 246, 0.2)', borderWidth: 2, fill: true, tension: 0.3 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { y: { beginAtZero: true, grid: { borderDash: [5, 5], color: '#334155' }, ticks: { color: '#94a3b8' } }, x: { grid: { display: false }, ticks: { color: '#94a3b8' } } } } }); } refreshAll();