/** * Sales Bot - Chart.js Integration and Chart Utilities */ // Chart default configuration Chart.defaults.color = '#888'; Chart.defaults.borderColor = 'rgba(255, 255, 255, 0.1)'; Chart.defaults.font.family = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"; // Color palette const ChartColors = { primary: '#00d4ff', secondary: '#00ff88', warning: '#ffaa00', danger: '#ff4444', purple: '#aa00ff', gradient: (ctx, color1, color2) => { const gradient = ctx.createLinearGradient(0, 0, 0, 300); gradient.addColorStop(0, color1); gradient.addColorStop(1, color2); return gradient; } }; // Chart factory const ChartFactory = { // Line chart for trends createTrendChart(canvasId, data, options = {}) { const ctx = document.getElementById(canvasId); if (!ctx) return null; return new Chart(ctx.getContext('2d'), { type: 'line', data: { labels: data.labels || [], datasets: [{ label: data.label || 'Datos', data: data.values || [], borderColor: options.color || ChartColors.primary, backgroundColor: options.fill ? ChartColors.gradient(ctx.getContext('2d'), 'rgba(0, 212, 255, 0.3)', 'rgba(0, 212, 255, 0)') : 'transparent', fill: options.fill !== false, tension: 0.4, pointRadius: options.points ? 4 : 0, pointHoverRadius: 6 }] }, options: { responsive: true, maintainAspectRatio: false, interaction: { intersect: false, mode: 'index' }, plugins: { legend: { display: options.legend !== false, labels: { color: '#888' } }, tooltip: { backgroundColor: 'rgba(26, 26, 46, 0.9)', titleColor: '#fff', bodyColor: '#888', borderColor: ChartColors.primary, borderWidth: 1, padding: 12, displayColors: false, callbacks: { label: (ctx) => options.formatValue ? options.formatValue(ctx.parsed.y) : ctx.parsed.y } } }, scales: { x: { grid: { color: 'rgba(255,255,255,0.05)' }, ticks: { color: '#888', maxRotation: 45 } }, y: { grid: { color: 'rgba(255,255,255,0.05)' }, ticks: { color: '#888', callback: options.formatYAxis || ((value) => value) }, beginAtZero: options.beginAtZero !== false } } } }); }, // Bar chart for comparisons createBarChart(canvasId, data, options = {}) { const ctx = document.getElementById(canvasId); if (!ctx) return null; return new Chart(ctx.getContext('2d'), { type: options.horizontal ? 'bar' : 'bar', data: { labels: data.labels || [], datasets: [{ label: data.label || 'Datos', data: data.values || [], backgroundColor: data.colors || [ ChartColors.primary, ChartColors.secondary, ChartColors.warning, ChartColors.purple ], borderRadius: 8, barThickness: options.barThickness || 'flex' }] }, options: { responsive: true, maintainAspectRatio: false, indexAxis: options.horizontal ? 'y' : 'x', plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(26, 26, 46, 0.9)', titleColor: '#fff', bodyColor: '#888', borderColor: ChartColors.primary, borderWidth: 1, padding: 12, callbacks: { label: (ctx) => options.formatValue ? options.formatValue(ctx.parsed[options.horizontal ? 'x' : 'y']) : ctx.parsed[options.horizontal ? 'x' : 'y'] } } }, scales: { x: { grid: { display: !options.horizontal }, ticks: { color: '#888' } }, y: { grid: { color: options.horizontal ? 'transparent' : 'rgba(255,255,255,0.05)' }, ticks: { color: '#888' } } } } }); }, // Doughnut chart for distribution createDoughnutChart(canvasId, data, options = {}) { const ctx = document.getElementById(canvasId); if (!ctx) return null; return new Chart(ctx.getContext('2d'), { type: 'doughnut', data: { labels: data.labels || [], datasets: [{ data: data.values || [], backgroundColor: data.colors || [ ChartColors.primary, ChartColors.secondary, ChartColors.warning, ChartColors.purple, ChartColors.danger ], borderWidth: 0, cutout: options.cutout || '70%' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: options.legend !== false, position: options.legendPosition || 'bottom', labels: { color: '#888', padding: 15 } }, tooltip: { backgroundColor: 'rgba(26, 26, 46, 0.9)', titleColor: '#fff', bodyColor: '#888', borderColor: ChartColors.primary, borderWidth: 1, padding: 12 } } } }); }, // Multi-line chart for comparisons createMultiLineChart(canvasId, datasets, labels, options = {}) { const ctx = document.getElementById(canvasId); if (!ctx) return null; const colors = [ChartColors.primary, ChartColors.secondary, ChartColors.warning, ChartColors.purple]; return new Chart(ctx.getContext('2d'), { type: 'line', data: { labels: labels, datasets: datasets.map((ds, i) => ({ label: ds.label, data: ds.values, borderColor: ds.color || colors[i % colors.length], backgroundColor: 'transparent', borderDash: ds.dashed ? [5, 5] : [], tension: 0.4, pointRadius: 0, pointHoverRadius: 6 })) }, options: { responsive: true, maintainAspectRatio: false, interaction: { intersect: false, mode: 'index' }, plugins: { legend: { display: true, labels: { color: '#888' } }, tooltip: { backgroundColor: 'rgba(26, 26, 46, 0.9)', titleColor: '#fff', bodyColor: '#888', borderColor: ChartColors.primary, borderWidth: 1, padding: 12 } }, scales: { x: { grid: { color: 'rgba(255,255,255,0.05)' }, ticks: { color: '#888' } }, y: { grid: { color: 'rgba(255,255,255,0.05)' }, ticks: { color: '#888' }, beginAtZero: true } } } }); } }; // Helper to format currency in charts function formatCurrency(value) { return new Intl.NumberFormat('es-MX', { style: 'currency', currency: 'MXN', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(value); } // Export window.ChartFactory = ChartFactory; window.ChartColors = ChartColors; window.formatCurrency = formatCurrency;