Initial commit: Retribusi frontend dengan dashboard, event logs, dan settings
This commit is contained in:
193
public/dashboard/js/charts.js
Normal file
193
public/dashboard/js/charts.js
Normal file
@@ -0,0 +1,193 @@
|
||||
// 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)
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user