|
|
|
|
|
import { CountUp } from 'countup.js';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function initCounter(element, endValue, options = {}) {
|
|
|
const defaultOptions = {
|
|
|
duration: 2,
|
|
|
useEasing: true,
|
|
|
useGrouping: true,
|
|
|
separator: ',',
|
|
|
decimal: '.',
|
|
|
prefix: '',
|
|
|
suffix: ''
|
|
|
};
|
|
|
|
|
|
const countUp = new CountUp(element, endValue, {
|
|
|
...defaultOptions,
|
|
|
...options
|
|
|
});
|
|
|
|
|
|
if (!countUp.error) {
|
|
|
countUp.start();
|
|
|
} else {
|
|
|
console.error('CountUp error:', countUp.error);
|
|
|
element.textContent = endValue;
|
|
|
}
|
|
|
|
|
|
return countUp;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function formatCurrency(value, currency = '฿') {
|
|
|
return `${currency}${value.toLocaleString()}`;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function formatPercentage(value, decimals = 2) {
|
|
|
return `${value.toFixed(decimals)}%`;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getTrendIndicator(percentage, showValue = true) {
|
|
|
const isPositive = percentage >= 0;
|
|
|
const icon = isPositive ? '↗' : '↘';
|
|
|
const colorClass = isPositive ? 'text-emerald-400' : 'text-red-400';
|
|
|
const bgClass = isPositive ? 'bg-emerald-400/10' : 'bg-red-400/10';
|
|
|
|
|
|
const valueText = showValue ? `${Math.abs(percentage).toFixed(1)}%` : '';
|
|
|
|
|
|
return `
|
|
|
<div class="flex items-center space-x-1 ${bgClass} px-2 py-1 rounded-full">
|
|
|
<span class="${colorClass} text-sm font-bold">${icon}</span>
|
|
|
${valueText ? `<span class="${colorClass} text-xs font-semibold">${valueText}</span>` : ''}
|
|
|
</div>
|
|
|
`;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function updateChartData(chart, newData, newLabels = null) {
|
|
|
if (newLabels) {
|
|
|
chart.data.labels = newLabels;
|
|
|
}
|
|
|
|
|
|
|
|
|
newData.forEach((dataset, index) => {
|
|
|
if (chart.data.datasets[index]) {
|
|
|
chart.data.datasets[index].data = dataset.data;
|
|
|
if (dataset.label) {
|
|
|
chart.data.datasets[index].label = dataset.label;
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
|
|
|
chart.update('active');
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function resizeChart(chart) {
|
|
|
if (chart && chart.canvas) {
|
|
|
chart.resize();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function destroyChart(chart) {
|
|
|
if (chart && typeof chart.destroy === 'function') {
|
|
|
chart.destroy();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function createChartSkeleton(container) {
|
|
|
container.innerHTML = `
|
|
|
<div class="animate-pulse">
|
|
|
<div class="h-4 bg-white/10 rounded mb-4 w-1/3"></div>
|
|
|
<div class="space-y-3">
|
|
|
<div class="h-3 bg-white/10 rounded w-full"></div>
|
|
|
<div class="h-3 bg-white/10 rounded w-5/6"></div>
|
|
|
<div class="h-3 bg-white/10 rounded w-4/6"></div>
|
|
|
<div class="h-3 bg-white/10 rounded w-3/6"></div>
|
|
|
<div class="h-3 bg-white/10 rounded w-2/6"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
`;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function showChartError(container, message = 'Failed to load chart data') {
|
|
|
container.innerHTML = `
|
|
|
<div class="flex flex-col items-center justify-center h-full text-center p-6">
|
|
|
<div class="w-12 h-12 bg-red-500/20 rounded-full flex items-center justify-center mb-4">
|
|
|
<svg class="w-6 h-6 text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
|
</svg>
|
|
|
</div>
|
|
|
<p class="text-white/60 text-sm mb-3">${message}</p>
|
|
|
<button onclick="location.reload()" class="px-4 py-2 bg-white/10 hover:bg-white/20 text-white text-xs rounded-lg transition-colors">
|
|
|
Retry
|
|
|
</button>
|
|
|
</div>
|
|
|
`;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function debounce(func, wait) {
|
|
|
let timeout;
|
|
|
return function executedFunction(...args) {
|
|
|
const later = () => {
|
|
|
clearTimeout(timeout);
|
|
|
func(...args);
|
|
|
};
|
|
|
clearTimeout(timeout);
|
|
|
timeout = setTimeout(later, wait);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function throttle(func, limit) {
|
|
|
let inThrottle;
|
|
|
return function() {
|
|
|
const args = arguments;
|
|
|
const context = this;
|
|
|
if (!inThrottle) {
|
|
|
func.apply(context, args);
|
|
|
inThrottle = true;
|
|
|
setTimeout(() => inThrottle = false, limit);
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function formatTimeLabel(value, period) {
|
|
|
switch (period) {
|
|
|
case 'daily':
|
|
|
return `${value}:00`;
|
|
|
case 'weekly':
|
|
|
return value;
|
|
|
case 'monthly':
|
|
|
return `Day ${value}`;
|
|
|
default:
|
|
|
return value;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function generateDemoData(length, min = 0, max = 1000) {
|
|
|
return Array.from({ length }, () => Math.floor(Math.random() * (max - min + 1)) + min);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function calculatePercentageChange(current, previous) {
|
|
|
if (previous === 0) return current > 0 ? 100 : 0;
|
|
|
return ((current - previous) / previous) * 100;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getResponsiveHeight() {
|
|
|
const width = window.innerWidth;
|
|
|
if (width < 640) return 250;
|
|
|
if (width < 1024) return 300;
|
|
|
return 350;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function initChartContainer(container) {
|
|
|
container.classList.add(
|
|
|
'bg-white/5',
|
|
|
'backdrop-blur-2xl',
|
|
|
'border',
|
|
|
'border-white/10',
|
|
|
'rounded-2xl',
|
|
|
'p-6',
|
|
|
'shadow-2xl'
|
|
|
);
|
|
|
|
|
|
|
|
|
container.style.boxShadow = `
|
|
|
0 25px 50px -12px rgba(0, 0, 0, 0.6),
|
|
|
0 0 0 1px rgba(255, 255, 255, 0.05),
|
|
|
inset 0 1px 0 rgba(255, 255, 255, 0.1)
|
|
|
`;
|
|
|
}
|
|
|
|
|
|
|
|
|
window.createChartSkeleton = createChartSkeleton;
|
|
|
window.showChartError = showChartError;
|
|
|
window.initChartContainer = initChartContainer;
|
|
|
window.formatCurrency = formatCurrency;
|
|
|
window.formatPercentage = formatPercentage;
|
|
|
window.getTrendIndicator = getTrendIndicator;
|
|
|
window.calculatePercentageChange = calculatePercentageChange;
|
|
|
window.updateChartData = updateChartData;
|
|
|
window.resizeChart = resizeChart;
|
|
|
window.destroyChart = destroyChart; |