refactor: improve Alpine chart instance management to prevent maximum call stack size errors and streamline updates.
This commit is contained in:
parent
ab01645ccd
commit
754b65259e
@ -263,11 +263,22 @@
|
|||||||
|
|
||||||
@script
|
@script
|
||||||
<script>
|
<script>
|
||||||
Alpine.data('analysisCharts', () => ({
|
Alpine.data('analysisCharts', () => {
|
||||||
charts: {},
|
// Local storage for chart instances to avoid Alpine reactivity proxying
|
||||||
|
// This prevents "Maximum call stack size exceeded" errors
|
||||||
|
const charts = {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
unwrap(data) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.stringify(data));
|
||||||
|
} catch (e) {
|
||||||
|
// Fallback for safety
|
||||||
|
return { labels: [], datasets: [] };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// Memastikan DOM siap dan library tersedia sebelum inisialisasi awal
|
|
||||||
const start = () => {
|
const start = () => {
|
||||||
if (typeof Chart !== 'undefined') {
|
if (typeof Chart !== 'undefined') {
|
||||||
this.initAll();
|
this.initAll();
|
||||||
@ -278,21 +289,54 @@
|
|||||||
|
|
||||||
this.$nextTick(() => start());
|
this.$nextTick(() => start());
|
||||||
|
|
||||||
// Watchers untuk pembaruan data reaktif
|
// Watchers
|
||||||
this.$watch('$wire.revenueTrendChart', () => this.updateRevenueTrend());
|
this.$watch('$wire.revenueTrendChart', () => this.updateChart('revenue', this.getRevenueData));
|
||||||
this.$watch('$wire.memberGrowthChart', () => this.updateMemberGrowth());
|
this.$watch('$wire.memberGrowthChart', () => this.updateChart('memberGrowth', this.getMemberGrowthData));
|
||||||
this.$watch('$wire.hourlySalesChart', () => this.updateHourlySales());
|
this.$watch('$wire.hourlySalesChart', () => this.updateChart('hourlySales', this.getHourlySalesData));
|
||||||
this.$watch('$wire.outletPerformanceChart', () => this.updateOutletPerformance());
|
this.$watch('$wire.outletPerformanceChart', () => this.updateChart('outletPerformance', this.getOutletPerformanceData));
|
||||||
this.$watch('$wire.categoryDistributionChart', () => this.updateCategoryDistribution());
|
this.$watch('$wire.categoryDistributionChart', () => this.updateChart('categoryDist', this.getCategoryDistributionData));
|
||||||
this.$watch('$wire.paymentMethodChart', () => this.updatePaymentMethod());
|
this.$watch('$wire.paymentMethodChart', () => this.updateChart('paymentMethod', this.getPaymentMethodData));
|
||||||
this.$watch('$wire.dayOfWeekSalesChart', () => this.updateDayOfWeekSales());
|
this.$watch('$wire.dayOfWeekSalesChart', () => this.updateChart('dayOfWeekSales', this.getDayOfWeekData));
|
||||||
this.$watch('$wire.customerTypeChart', () => this.updateCustomerType());
|
this.$watch('$wire.customerTypeChart', () => this.updateChart('customerType', this.getCustomerTypeData));
|
||||||
this.$watch('$wire.transactionTrendChart', () => this.updateTransactionTrend());
|
this.$watch('$wire.transactionTrendChart', () => this.updateChart('transactionTrend', this.getTransactionTrendData));
|
||||||
this.$watch('$wire.aovTrendChart', () => this.updateAovTrend());
|
this.$watch('$wire.aovTrendChart', () => this.updateChart('aovTrend', this.getAovTrendData));
|
||||||
this.$watch('$wire.expenseBreakdownChart', () => this.updateExpenseBreakdown());
|
this.$watch('$wire.expenseBreakdownChart', () => this.updateChart('expenseBreakdown', this.getExpenseBreakdownData));
|
||||||
this.$watch('$wire.revenueVsCostChart', () => this.updateRevenueVsCost());
|
this.$watch('$wire.revenueVsCostChart', () => this.updateChart('revenueVsCost', this.getRevenueVsCostData));
|
||||||
this.$watch('$wire.customerRetentionChart', () => this.updateCustomerRetention());
|
this.$watch('$wire.customerRetentionChart', () => this.updateChart('customerRetention', this.getCustomerRetentionData));
|
||||||
this.$watch('$wire.voucherUsageTrendChart', () => this.updateVoucherUsage());
|
this.$watch('$wire.voucherUsageTrendChart', () => this.updateChart('voucherUsage', this.getVoucherUsageData));
|
||||||
|
},
|
||||||
|
|
||||||
|
updateChart(key, getDataFn) {
|
||||||
|
if (charts[key]) {
|
||||||
|
try {
|
||||||
|
charts[key].data = getDataFn.call(this);
|
||||||
|
charts[key].update();
|
||||||
|
} catch(e) {
|
||||||
|
console.error(`Error updating chart ${key}`, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// init map for manual mapping if needed, or rely on naming convention
|
||||||
|
const initMap = {
|
||||||
|
'revenue': 'initRevenueTrend',
|
||||||
|
'memberGrowth': 'initMemberGrowth',
|
||||||
|
'hourlySales': 'initHourlySales',
|
||||||
|
'outletPerformance': 'initOutletPerformance',
|
||||||
|
'categoryDist': 'initCategoryDistribution',
|
||||||
|
'paymentMethod': 'initPaymentMethod',
|
||||||
|
'dayOfWeekSales': 'initDayOfWeekSales',
|
||||||
|
'customerType': 'initCustomerType',
|
||||||
|
'transactionTrend': 'initTransactionTrend',
|
||||||
|
'aovTrend': 'initAovTrend',
|
||||||
|
'expenseBreakdown': 'initExpenseBreakdown',
|
||||||
|
'revenueVsCost': 'initRevenueVsCost',
|
||||||
|
'customerRetention': 'initCustomerRetention',
|
||||||
|
'voucherUsage': 'initVoucherUsage',
|
||||||
|
};
|
||||||
|
const fnName = initMap[key];
|
||||||
|
if (fnName && typeof this[fnName] === 'function') {
|
||||||
|
this[fnName]();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
initAll() {
|
initAll() {
|
||||||
@ -311,41 +355,21 @@
|
|||||||
this.initRevenueVsCost();
|
this.initRevenueVsCost();
|
||||||
this.initCustomerRetention();
|
this.initCustomerRetention();
|
||||||
this.initVoucherUsage();
|
this.initVoucherUsage();
|
||||||
|
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
initRevenueTrend() {
|
initRevenueTrend() {
|
||||||
const ctx = document.getElementById('revenueTrendChart');
|
this.createChart('revenueTrendChart', 'revenue', 'line', this.getRevenueData(), {
|
||||||
if (!ctx) return;
|
plugins: { legend: { position: 'top' } },
|
||||||
|
scales: { y: { beginAtZero: true } }
|
||||||
const data = $wire.revenueTrendChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.revenue = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: this.getRevenueData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
position: 'top'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getRevenueData() {
|
getRevenueData() {
|
||||||
const data = $wire.revenueTrendChart;
|
const data = this.unwrap($wire.revenueTrendChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [
|
||||||
|
{
|
||||||
label: 'Pendapatan',
|
label: 'Pendapatan',
|
||||||
data: data.revenue || [],
|
data: data.revenue || [],
|
||||||
borderColor: '#10b981',
|
borderColor: '#10b981',
|
||||||
@ -364,37 +388,14 @@
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateRevenueTrend() {
|
|
||||||
if (this.charts.revenue) {
|
|
||||||
this.charts.revenue.data = this.getRevenueData();
|
|
||||||
this.charts.revenue.update();
|
|
||||||
} else {
|
|
||||||
this.initRevenueTrend();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initMemberGrowth() {
|
initMemberGrowth() {
|
||||||
const ctx = document.getElementById('memberGrowthChart');
|
this.createChart('memberGrowthChart', 'memberGrowth', 'line', this.getMemberGrowthData(), {
|
||||||
if (!ctx) return;
|
scales: { y: { beginAtZero: true } }
|
||||||
const data = $wire.memberGrowthChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.memberGrowth = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: this.getMemberGrowthData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getMemberGrowthData() {
|
getMemberGrowthData() {
|
||||||
const data = $wire.memberGrowthChart;
|
const data = this.unwrap($wire.memberGrowthChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -407,32 +408,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateMemberGrowth() {
|
|
||||||
if (this.charts.memberGrowth) {
|
|
||||||
this.charts.memberGrowth.data = this.getMemberGrowthData();
|
|
||||||
this.charts.memberGrowth.update();
|
|
||||||
} else {
|
|
||||||
this.initMemberGrowth();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initHourlySales() {
|
initHourlySales() {
|
||||||
const ctx = document.getElementById('hourlySalesChart');
|
this.createChart('hourlySalesChart', 'hourlySales', 'bar', this.getHourlySalesData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.hourlySalesChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.hourlySales = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: this.getHourlySalesData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getHourlySalesData() {
|
getHourlySalesData() {
|
||||||
const data = $wire.hourlySalesChart;
|
const data = this.unwrap($wire.hourlySalesChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -442,33 +423,14 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateHourlySales() {
|
|
||||||
if (this.charts.hourlySales) {
|
|
||||||
this.charts.hourlySales.data = this.getHourlySalesData();
|
|
||||||
this.charts.hourlySales.update();
|
|
||||||
} else {
|
|
||||||
this.initHourlySales();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initOutletPerformance() {
|
initOutletPerformance() {
|
||||||
const ctx = document.getElementById('outletPerformanceChart');
|
this.createChart('outletPerformanceChart', 'outletPerformance', 'bar', this.getOutletPerformanceData(), {
|
||||||
if (!ctx) return;
|
indexAxis: 'y'
|
||||||
const data = $wire.outletPerformanceChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.outletPerformance = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: this.getOutletPerformanceData(),
|
|
||||||
options: {
|
|
||||||
indexAxis: 'y',
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getOutletPerformanceData() {
|
getOutletPerformanceData() {
|
||||||
const data = $wire.outletPerformanceChart;
|
const data = this.unwrap($wire.outletPerformanceChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -478,32 +440,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateOutletPerformance() {
|
|
||||||
if (this.charts.outletPerformance) {
|
|
||||||
this.charts.outletPerformance.data = this.getOutletPerformanceData();
|
|
||||||
this.charts.outletPerformance.update();
|
|
||||||
} else {
|
|
||||||
this.initOutletPerformance();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initCategoryDistribution() {
|
initCategoryDistribution() {
|
||||||
const ctx = document.getElementById('categoryDistributionChart');
|
this.createChart('categoryDistributionChart', 'categoryDist', 'doughnut', this.getCategoryDistributionData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.categoryDistributionChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.categoryDist = new Chart(ctx, {
|
|
||||||
type: 'doughnut',
|
|
||||||
data: this.getCategoryDistributionData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getCategoryDistributionData() {
|
getCategoryDistributionData() {
|
||||||
const data = $wire.categoryDistributionChart;
|
const data = this.unwrap($wire.categoryDistributionChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -512,32 +454,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateCategoryDistribution() {
|
|
||||||
if (this.charts.categoryDist) {
|
|
||||||
this.charts.categoryDist.data = this.getCategoryDistributionData();
|
|
||||||
this.charts.categoryDist.update();
|
|
||||||
} else {
|
|
||||||
this.initCategoryDistribution();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initPaymentMethod() {
|
initPaymentMethod() {
|
||||||
const ctx = document.getElementById('paymentMethodChart');
|
this.createChart('paymentMethodChart', 'paymentMethod', 'pie', this.getPaymentMethodData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.paymentMethodChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.paymentMethod = new Chart(ctx, {
|
|
||||||
type: 'pie',
|
|
||||||
data: this.getPaymentMethodData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getPaymentMethodData() {
|
getPaymentMethodData() {
|
||||||
const data = $wire.paymentMethodChart;
|
const data = this.unwrap($wire.paymentMethodChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -546,32 +468,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updatePaymentMethod() {
|
|
||||||
if (this.charts.paymentMethod) {
|
|
||||||
this.charts.paymentMethod.data = this.getPaymentMethodData();
|
|
||||||
this.charts.paymentMethod.update();
|
|
||||||
} else {
|
|
||||||
this.initPaymentMethod();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initDayOfWeekSales() {
|
initDayOfWeekSales() {
|
||||||
const ctx = document.getElementById('dayOfWeekSalesChart');
|
this.createChart('dayOfWeekSalesChart', 'dayOfWeekSales', 'bar', this.getDayOfWeekData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.dayOfWeekSalesChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.dayOfWeek = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: this.getDayOfWeekData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getDayOfWeekData() {
|
getDayOfWeekData() {
|
||||||
const data = $wire.dayOfWeekSalesChart;
|
const data = this.unwrap($wire.dayOfWeekSalesChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -581,32 +483,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateDayOfWeekSales() {
|
|
||||||
if (this.charts.dayOfWeek) {
|
|
||||||
this.charts.dayOfWeek.data = this.getDayOfWeekData();
|
|
||||||
this.charts.dayOfWeek.update();
|
|
||||||
} else {
|
|
||||||
this.initDayOfWeekSales();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initCustomerType() {
|
initCustomerType() {
|
||||||
const ctx = document.getElementById('customerTypeChart');
|
this.createChart('customerTypeChart', 'customerType', 'doughnut', this.getCustomerTypeData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.customerTypeChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.customerType = new Chart(ctx, {
|
|
||||||
type: 'doughnut',
|
|
||||||
data: this.getCustomerTypeData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getCustomerTypeData() {
|
getCustomerTypeData() {
|
||||||
const data = $wire.customerTypeChart;
|
const data = this.unwrap($wire.customerTypeChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -615,32 +497,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateCustomerType() {
|
|
||||||
if (this.charts.customerType) {
|
|
||||||
this.charts.customerType.data = this.getCustomerTypeData();
|
|
||||||
this.charts.customerType.update();
|
|
||||||
} else {
|
|
||||||
this.initCustomerType();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initTransactionTrend() {
|
initTransactionTrend() {
|
||||||
const ctx = document.getElementById('transactionTrendChart');
|
this.createChart('transactionTrendChart', 'transactionTrend', 'line', this.getTransactionTrendData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.transactionTrendChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.transactionTrend = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: this.getTransactionTrendData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getTransactionTrendData() {
|
getTransactionTrendData() {
|
||||||
const data = $wire.transactionTrendChart;
|
const data = this.unwrap($wire.transactionTrendChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -653,32 +515,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateTransactionTrend() {
|
|
||||||
if (this.charts.transactionTrend) {
|
|
||||||
this.charts.transactionTrend.data = this.getTransactionTrendData();
|
|
||||||
this.charts.transactionTrend.update();
|
|
||||||
} else {
|
|
||||||
this.initTransactionTrend();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initAovTrend() {
|
initAovTrend() {
|
||||||
const ctx = document.getElementById('aovTrendChart');
|
this.createChart('aovTrendChart', 'aovTrend', 'line', this.getAovTrendData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.aovTrendChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.aovTrend = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: this.getAovTrendData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getAovTrendData() {
|
getAovTrendData() {
|
||||||
const data = $wire.aovTrendChart;
|
const data = this.unwrap($wire.aovTrendChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -691,32 +533,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateAovTrend() {
|
|
||||||
if (this.charts.aovTrend) {
|
|
||||||
this.charts.aovTrend.data = this.getAovTrendData();
|
|
||||||
this.charts.aovTrend.update();
|
|
||||||
} else {
|
|
||||||
this.initAovTrend();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initExpenseBreakdown() {
|
initExpenseBreakdown() {
|
||||||
const ctx = document.getElementById('expenseBreakdownChart');
|
this.createChart('expenseBreakdownChart', 'expenseBreakdown', 'doughnut', this.getExpenseBreakdownData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.expenseBreakdownChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.expenseBreakdown = new Chart(ctx, {
|
|
||||||
type: 'doughnut',
|
|
||||||
data: this.getExpenseBreakdownData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getExpenseBreakdownData() {
|
getExpenseBreakdownData() {
|
||||||
const data = $wire.expenseBreakdownChart;
|
const data = this.unwrap($wire.expenseBreakdownChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -725,32 +547,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateExpenseBreakdown() {
|
|
||||||
if (this.charts.expenseBreakdown) {
|
|
||||||
this.charts.expenseBreakdown.data = this.getExpenseBreakdownData();
|
|
||||||
this.charts.expenseBreakdown.update();
|
|
||||||
} else {
|
|
||||||
this.initExpenseBreakdown();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initRevenueVsCost() {
|
initRevenueVsCost() {
|
||||||
const ctx = document.getElementById('revenueVsCostChart');
|
this.createChart('revenueVsCostChart', 'revenueVsCost', 'bar', this.getRevenueVsCostData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.revenueVsCostChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.revenueVsCost = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: this.getRevenueVsCostData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getRevenueVsCostData() {
|
getRevenueVsCostData() {
|
||||||
const data = $wire.revenueVsCostChart;
|
const data = this.unwrap($wire.revenueVsCostChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -760,32 +562,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateRevenueVsCost() {
|
|
||||||
if (this.charts.revenueVsCost) {
|
|
||||||
this.charts.revenueVsCost.data = this.getRevenueVsCostData();
|
|
||||||
this.charts.revenueVsCost.update();
|
|
||||||
} else {
|
|
||||||
this.initRevenueVsCost();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initCustomerRetention() {
|
initCustomerRetention() {
|
||||||
const ctx = document.getElementById('customerRetentionChart');
|
this.createChart('customerRetentionChart', 'customerRetention', 'bar', this.getCustomerRetentionData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.customerRetentionChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.customerRetention = new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
data: this.getCustomerRetentionData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getCustomerRetentionData() {
|
getCustomerRetentionData() {
|
||||||
const data = $wire.customerRetentionChart;
|
const data = this.unwrap($wire.customerRetentionChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -795,32 +577,12 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateCustomerRetention() {
|
|
||||||
if (this.charts.customerRetention) {
|
|
||||||
this.charts.customerRetention.data = this.getCustomerRetentionData();
|
|
||||||
this.charts.customerRetention.update();
|
|
||||||
} else {
|
|
||||||
this.initCustomerRetention();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initVoucherUsage() {
|
initVoucherUsage() {
|
||||||
const ctx = document.getElementById('voucherUsageTrendChart');
|
this.createChart('voucherUsageTrendChart', 'voucherUsage', 'line', this.getVoucherUsageData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.voucherUsageTrendChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.voucherUsage = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: this.getVoucherUsageData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getVoucherUsageData() {
|
getVoucherUsageData() {
|
||||||
const data = $wire.voucherUsageTrendChart;
|
const data = this.unwrap($wire.voucherUsageTrendChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -833,14 +595,29 @@
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateVoucherUsage() {
|
|
||||||
if (this.charts.voucherUsage) {
|
// Helper to create chart efficiently
|
||||||
this.charts.voucherUsage.data = this.getVoucherUsageData();
|
createChart(elementId, chartKey, type, data, extraOptions = {}) {
|
||||||
this.charts.voucherUsage.update();
|
if (!data || !data.labels) return;
|
||||||
} else {
|
|
||||||
this.initVoucherUsage();
|
const ctx = document.getElementById(elementId);
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
if (charts[chartKey]) {
|
||||||
|
charts[chartKey].destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
charts[chartKey] = new Chart(ctx, {
|
||||||
|
type: type,
|
||||||
|
data: data,
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
...extraOptions
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
@endscript
|
@endscript
|
||||||
|
|||||||
@ -99,10 +99,25 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
|
|
||||||
@script
|
@script
|
||||||
<script>
|
<script>
|
||||||
Alpine.data('overviewCharts', () => ({
|
Alpine.data('overviewCharts', () => {
|
||||||
charts: {},
|
// Local storage for chart instances to avoid Alpine reactivity proxying
|
||||||
|
// This prevents "Maximum call stack size exceeded" errors
|
||||||
|
const charts = {};
|
||||||
|
|
||||||
|
return {
|
||||||
isInitialized: false,
|
isInitialized: false,
|
||||||
|
|
||||||
|
unwrap(data) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.stringify(data));
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
labels: [],
|
||||||
|
datasets: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
const start = () => {
|
const start = () => {
|
||||||
if (typeof Chart !== 'undefined') {
|
if (typeof Chart !== 'undefined') {
|
||||||
@ -114,13 +129,45 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
|
|
||||||
this.$nextTick(() => start());
|
this.$nextTick(() => start());
|
||||||
|
|
||||||
this.$watch('$wire.hourlyComparisonChart', () => this.updateHourlyComparison());
|
this.$watch('$wire.hourlyComparisonChart', () => this.updateChart('hourly', this
|
||||||
this.$watch('$wire.todayCategoryChart', () => this.updateCategoryDist());
|
.getHourlyData));
|
||||||
this.$watch('$wire.todayPaymentChart', () => this.updatePaymentMethod());
|
this.$watch('$wire.todayCategoryChart', () => this.updateChart('category', this
|
||||||
this.$watch('$wire.todayCustomerTypeChart', () => this.updateCustomerType());
|
.getCategoryData));
|
||||||
this.$watch('$wire.todayTopSellingChart', () => this.updateTopSelling());
|
this.$watch('$wire.todayPaymentChart', () => this.updateChart('payment', this.getPaymentData));
|
||||||
this.$watch('$wire.todayTopCustomersChart', () => this.updateTopCustomers());
|
this.$watch('$wire.todayCustomerTypeChart', () => this.updateChart('customerType', this
|
||||||
this.$watch('$wire.todayDiscountChart', () => this.updateDiscountDist());
|
.getCustomerTypeData));
|
||||||
|
this.$watch('$wire.todayTopSellingChart', () => this.updateChart('topSelling', this
|
||||||
|
.getTopSellingData));
|
||||||
|
this.$watch('$wire.todayTopCustomersChart', () => this.updateChart('topCustomers', this
|
||||||
|
.getTopCustomersData));
|
||||||
|
this.$watch('$wire.todayDiscountChart', () => this.updateChart('discountDist', this
|
||||||
|
.getDiscountData));
|
||||||
|
},
|
||||||
|
|
||||||
|
updateChart(key, getDataFn) {
|
||||||
|
if (charts[key]) {
|
||||||
|
try {
|
||||||
|
charts[key].data = getDataFn.call(this);
|
||||||
|
charts[key].update();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error updating chart ${key}`, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Map key to init function if chart doesn't exist
|
||||||
|
const initMap = {
|
||||||
|
'hourly': 'initHourlyComparison',
|
||||||
|
'category': 'initCategoryDist',
|
||||||
|
'payment': 'initPaymentMethod',
|
||||||
|
'customerType': 'initCustomerType',
|
||||||
|
'topSelling': 'initTopSelling',
|
||||||
|
'topCustomers': 'initTopCustomers',
|
||||||
|
'discountDist': 'initDiscountDist',
|
||||||
|
};
|
||||||
|
const fnName = initMap[key];
|
||||||
|
if (fnName && typeof this[fnName] === 'function') {
|
||||||
|
this[fnName]();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
initAll() {
|
initAll() {
|
||||||
@ -134,18 +181,30 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
initHourlyComparison() {
|
// Helper to create charts consistently
|
||||||
const ctx = document.getElementById('hourlyComparisonChart');
|
createChart(elementId, key, type, data, options = {}) {
|
||||||
|
const ctx = document.getElementById(elementId);
|
||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
const data = $wire.hourlyComparisonChart;
|
|
||||||
if (!data || !data.labels) return;
|
if (!data || !data.labels) return;
|
||||||
|
|
||||||
this.charts.hourly = new Chart(ctx, {
|
if (charts[key]) {
|
||||||
type: 'line',
|
charts[key].destroy();
|
||||||
data: this.getHourlyData(),
|
}
|
||||||
|
|
||||||
|
charts[key] = new Chart(ctx, {
|
||||||
|
type: type,
|
||||||
|
data: data,
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
|
...options
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
initHourlyComparison() {
|
||||||
|
this.createChart('hourlyComparisonChart', 'hourly', 'line', this.getHourlyData(), {
|
||||||
plugins: {
|
plugins: {
|
||||||
legend: {
|
legend: {
|
||||||
position: 'top'
|
position: 'top'
|
||||||
@ -159,11 +218,10 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getHourlyData() {
|
getHourlyData() {
|
||||||
const data = $wire.hourlyComparisonChart;
|
const data = this.unwrap($wire.hourlyComparisonChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -186,32 +244,12 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateHourlyComparison() {
|
|
||||||
if (this.charts.hourly) {
|
|
||||||
this.charts.hourly.data = this.getHourlyData();
|
|
||||||
this.charts.hourly.update();
|
|
||||||
} else {
|
|
||||||
this.initHourlyComparison();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initCategoryDist() {
|
initCategoryDist() {
|
||||||
const ctx = document.getElementById('todayCategoryChart');
|
this.createChart('todayCategoryChart', 'category', 'doughnut', this.getCategoryData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.todayCategoryChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.category = new Chart(ctx, {
|
|
||||||
type: 'doughnut',
|
|
||||||
data: this.getCategoryData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getCategoryData() {
|
getCategoryData() {
|
||||||
const data = $wire.todayCategoryChart;
|
const data = this.unwrap($wire.todayCategoryChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -220,32 +258,12 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateCategoryDist() {
|
|
||||||
if (this.charts.category) {
|
|
||||||
this.charts.category.data = this.getCategoryData();
|
|
||||||
this.charts.category.update();
|
|
||||||
} else {
|
|
||||||
this.initCategoryDist();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initPaymentMethod() {
|
initPaymentMethod() {
|
||||||
const ctx = document.getElementById('todayPaymentChart');
|
this.createChart('todayPaymentChart', 'payment', 'pie', this.getPaymentData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.todayPaymentChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.payment = new Chart(ctx, {
|
|
||||||
type: 'pie',
|
|
||||||
data: this.getPaymentData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getPaymentData() {
|
getPaymentData() {
|
||||||
const data = $wire.todayPaymentChart;
|
const data = this.unwrap($wire.todayPaymentChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -254,32 +272,13 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updatePaymentMethod() {
|
|
||||||
if (this.charts.payment) {
|
|
||||||
this.charts.payment.data = this.getPaymentData();
|
|
||||||
this.charts.payment.update();
|
|
||||||
} else {
|
|
||||||
this.initPaymentMethod();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initCustomerType() {
|
initCustomerType() {
|
||||||
const ctx = document.getElementById('todayCustomerTypeChart');
|
this.createChart('todayCustomerTypeChart', 'customerType', 'doughnut', this
|
||||||
if (!ctx) return;
|
.getCustomerTypeData());
|
||||||
const data = $wire.todayCustomerTypeChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.customerType = new Chart(ctx, {
|
|
||||||
type: 'doughnut',
|
|
||||||
data: this.getCustomerTypeData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getCustomerTypeData() {
|
getCustomerTypeData() {
|
||||||
const data = $wire.todayCustomerTypeChart;
|
const data = this.unwrap($wire.todayCustomerTypeChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -288,32 +287,12 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateCustomerType() {
|
|
||||||
if (this.charts.customerType) {
|
|
||||||
this.charts.customerType.data = this.getCustomerTypeData();
|
|
||||||
this.charts.customerType.update();
|
|
||||||
} else {
|
|
||||||
this.initCustomerType();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initTopSelling() {
|
initTopSelling() {
|
||||||
const ctx = document.getElementById('todayTopSellingChart');
|
this.createChart('todayTopSellingChart', 'topSelling', 'doughnut', this.getTopSellingData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.todayTopSellingChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.topSelling = new Chart(ctx, {
|
|
||||||
type: 'doughnut',
|
|
||||||
data: this.getTopSellingData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getTopSellingData() {
|
getTopSellingData() {
|
||||||
const data = $wire.todayTopSellingChart;
|
const data = this.unwrap($wire.todayTopSellingChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -322,32 +301,12 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateTopSelling() {
|
|
||||||
if (this.charts.topSelling) {
|
|
||||||
this.charts.topSelling.data = this.getTopSellingData();
|
|
||||||
this.charts.topSelling.update();
|
|
||||||
} else {
|
|
||||||
this.initTopSelling();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initTopCustomers() {
|
initTopCustomers() {
|
||||||
const ctx = document.getElementById('todayTopCustomersChart');
|
this.createChart('todayTopCustomersChart', 'topCustomers', 'pie', this.getTopCustomersData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.todayTopCustomersChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.topCustomers = new Chart(ctx, {
|
|
||||||
type: 'pie',
|
|
||||||
data: this.getTopCustomersData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getTopCustomersData() {
|
getTopCustomersData() {
|
||||||
const data = $wire.todayTopCustomersChart;
|
const data = this.unwrap($wire.todayTopCustomersChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -356,32 +315,12 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
updateTopCustomers() {
|
|
||||||
if (this.charts.topCustomers) {
|
|
||||||
this.charts.topCustomers.data = this.getTopCustomersData();
|
|
||||||
this.charts.topCustomers.update();
|
|
||||||
} else {
|
|
||||||
this.initTopCustomers();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
initDiscountDist() {
|
initDiscountDist() {
|
||||||
const ctx = document.getElementById('todayDiscountChart');
|
this.createChart('todayDiscountChart', 'discountDist', 'doughnut', this.getDiscountData());
|
||||||
if (!ctx) return;
|
|
||||||
const data = $wire.todayDiscountChart;
|
|
||||||
if (!data || !data.labels) return;
|
|
||||||
|
|
||||||
this.charts.discountDist = new Chart(ctx, {
|
|
||||||
type: 'doughnut',
|
|
||||||
data: this.getDiscountData(),
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
getDiscountData() {
|
getDiscountData() {
|
||||||
const data = $wire.todayDiscountChart;
|
const data = this.unwrap($wire.todayDiscountChart);
|
||||||
return {
|
return {
|
||||||
labels: data.labels || [],
|
labels: data.labels || [],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
@ -389,15 +328,8 @@ class="flex items-center gap-1 font-medium text-sm @if ($stat['trendUp']) text-g
|
|||||||
backgroundColor: ['#f87171', '#4ade80']
|
backgroundColor: ['#f87171', '#4ade80']
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
},
|
|
||||||
updateDiscountDist() {
|
|
||||||
if (this.charts.discountDist) {
|
|
||||||
this.charts.discountDist.data = this.getDiscountData();
|
|
||||||
this.charts.discountDist.update();
|
|
||||||
} else {
|
|
||||||
this.initDiscountDist();
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}));
|
});
|
||||||
</script>
|
</script>
|
||||||
@endscript
|
@endscript
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user