194 lines
4.9 KiB
JavaScript
194 lines
4.9 KiB
JavaScript
// public/dashboard/js/charts.js
|
|
// Chart.js helpers: create & update charts without recreating canvases.
|
|
|
|
let dailyLineChart = null;
|
|
let categoryChart = null;
|
|
|
|
// Export chart instances untuk akses dari dashboard.js
|
|
export function getDailyChart() {
|
|
return dailyLineChart;
|
|
}
|
|
|
|
export function getCategoryChart() {
|
|
return categoryChart;
|
|
}
|
|
|
|
export function initDailyChart(ctx) {
|
|
if (dailyLineChart) return dailyLineChart;
|
|
|
|
dailyLineChart = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: [],
|
|
datasets: [
|
|
{
|
|
label: 'Jumlah',
|
|
data: [],
|
|
borderColor: '#111827',
|
|
backgroundColor: 'rgba(17, 24, 39, 0.06)',
|
|
borderWidth: 2,
|
|
tension: 0.35,
|
|
fill: true,
|
|
pointRadius: 2.5,
|
|
pointBackgroundColor: '#111827'
|
|
},
|
|
{
|
|
label: 'Pendapatan',
|
|
data: [],
|
|
borderColor: '#6b7280',
|
|
backgroundColor: 'rgba(156, 163, 175, 0.08)',
|
|
borderWidth: 2,
|
|
tension: 0.35,
|
|
fill: true,
|
|
pointRadius: 2.5,
|
|
pointBackgroundColor: '#6b7280',
|
|
yAxisID: 'y1'
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
interaction: {
|
|
mode: 'index',
|
|
intersect: false
|
|
},
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true,
|
|
grid: { color: '#e5e7eb' },
|
|
ticks: { font: { size: 11 } }
|
|
},
|
|
y1: {
|
|
beginAtZero: true,
|
|
position: 'right',
|
|
grid: { drawOnChartArea: false },
|
|
ticks: {
|
|
font: { size: 11 },
|
|
callback: value => 'Rp ' + new Intl.NumberFormat('id-ID').format(value)
|
|
}
|
|
},
|
|
x: {
|
|
grid: { display: false },
|
|
ticks: { font: { size: 11 } }
|
|
}
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
position: 'bottom',
|
|
labels: {
|
|
boxWidth: 14,
|
|
boxHeight: 4
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return dailyLineChart;
|
|
}
|
|
|
|
export function updateDailyChart({ labels, counts, amounts }) {
|
|
if (!dailyLineChart) {
|
|
console.warn('[Charts] Daily chart belum di-init, skip update');
|
|
// Try to init if canvas exists
|
|
const canvas = document.getElementById('daily-chart');
|
|
if (canvas) {
|
|
console.log('[Charts] Attempting to init daily chart from update function...');
|
|
initDailyChart(canvas.getContext('2d'));
|
|
} else {
|
|
console.error('[Charts] Daily chart canvas not found!');
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!dailyLineChart) {
|
|
console.error('[Charts] Failed to init daily chart');
|
|
return;
|
|
}
|
|
|
|
dailyLineChart.data.labels = labels || [];
|
|
dailyLineChart.data.datasets[0].data = counts || [];
|
|
dailyLineChart.data.datasets[1].data = amounts || [];
|
|
dailyLineChart.update();
|
|
console.log('[Charts] Daily chart updated:', { labelsCount: labels?.length, countsCount: counts?.length, amountsCount: amounts?.length });
|
|
}
|
|
|
|
export function initCategoryChart(ctx) {
|
|
if (categoryChart) return categoryChart;
|
|
|
|
categoryChart = new Chart(ctx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: [],
|
|
datasets: [
|
|
{
|
|
label: 'Per Kategori',
|
|
data: [],
|
|
backgroundColor: ['#111827', '#4b5563', '#9ca3af'],
|
|
borderWidth: 0
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
cutout: '60%',
|
|
plugins: {
|
|
legend: {
|
|
position: 'bottom',
|
|
labels: {
|
|
boxWidth: 14,
|
|
boxHeight: 6
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return categoryChart;
|
|
}
|
|
|
|
export function updateCategoryChart({ labels, values }) {
|
|
if (!categoryChart) {
|
|
console.warn('[Charts] Category chart belum di-init, skip update');
|
|
// Try to init if canvas exists
|
|
const canvas = document.getElementById('category-chart');
|
|
if (canvas) {
|
|
console.log('[Charts] Attempting to init category chart from update function...');
|
|
initCategoryChart(canvas.getContext('2d'));
|
|
} else {
|
|
console.error('[Charts] Category chart canvas not found!');
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!categoryChart) {
|
|
console.error('[Charts] Failed to init category chart');
|
|
return;
|
|
}
|
|
|
|
// Pastikan labels dan values tidak kosong
|
|
const finalLabels = labels && labels.length > 0 ? labels : ['Orang', 'Motor', 'Mobil'];
|
|
const finalValues = values && values.length > 0 ? values : [0, 0, 0];
|
|
|
|
// Pastikan length sama
|
|
const minLength = Math.min(finalLabels.length, finalValues.length);
|
|
const safeLabels = finalLabels.slice(0, minLength);
|
|
const safeValues = finalValues.slice(0, minLength);
|
|
|
|
categoryChart.data.labels = safeLabels;
|
|
categoryChart.data.datasets[0].data = safeValues;
|
|
|
|
// Update chart dengan mode 'none' untuk animasi halus
|
|
categoryChart.update('none');
|
|
|
|
console.log('[Charts] Category chart updated:', {
|
|
labels: safeLabels,
|
|
values: safeValues,
|
|
total: safeValues.reduce((a, b) => a + b, 0)
|
|
});
|
|
}
|
|
|
|
|