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();