Initial commit from remix
This commit is contained in:
141
src/components/dashboard/ApexChart.tsx
Normal file
141
src/components/dashboard/ApexChart.tsx
Normal file
@@ -0,0 +1,141 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import type { ApexOptions } from 'apexcharts';
|
||||
|
||||
// Chart module is imported dynamically inside the component to avoid SSR/hot-reload issues.
|
||||
|
||||
interface ApexChartProps {
|
||||
type?: 'area' | 'line' | 'bar' | 'pie' | 'donut';
|
||||
data?: number[];
|
||||
labels?: string[];
|
||||
title?: string;
|
||||
height?: number;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
const ApexChart: React.FC<ApexChartProps> = ({
|
||||
type = 'area',
|
||||
data = [],
|
||||
labels = [],
|
||||
title = 'Chart',
|
||||
height = 350,
|
||||
color = '#F84525',
|
||||
}) => {
|
||||
const isClient = typeof window !== 'undefined';
|
||||
const [Chart, setChart] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isClient) {
|
||||
import('react-apexcharts')
|
||||
.then((m) => setChart(m.default))
|
||||
.catch((e) => console.error('Failed to load react-apexcharts', e));
|
||||
}
|
||||
}, [isClient]);
|
||||
|
||||
const safeData = Array.isArray(data) ? data.map((n) => (Number.isFinite(Number(n)) ? Number(n) : 0)) : [];
|
||||
const isDarkMode = typeof document !== 'undefined' && document.documentElement.classList.contains('dark');
|
||||
|
||||
// Show loading/no data state if chart not ready or no data
|
||||
if (!isClient || !Chart || safeData.length === 0) {
|
||||
return (
|
||||
<div className="card-enhanced">
|
||||
<div className="card-body p-4">
|
||||
<h5 className="card-title mb-3">{title || 'Chart'}</h5>
|
||||
<div className="text-sm text-muted">
|
||||
{!Chart ? 'Cargando gráfico...' : 'No hay datos disponibles.'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure options are valid and chart configuration is safe
|
||||
const options: ApexOptions = {
|
||||
chart: {
|
||||
type: type || 'area',
|
||||
height: height || 350,
|
||||
zoom: { enabled: false },
|
||||
toolbar: { show: false },
|
||||
foreColor: isDarkMode ? '#91989e' : '#433c3a',
|
||||
animations: { enabled: false } // Disable animations for stability
|
||||
},
|
||||
colors: [color || '#F84525'],
|
||||
dataLabels: { enabled: false },
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
width: 3,
|
||||
},
|
||||
fill: type === 'area'
|
||||
? {
|
||||
type: 'gradient',
|
||||
gradient: {
|
||||
shadeIntensity: 1,
|
||||
type: 'vertical',
|
||||
opacityFrom: 0.4,
|
||||
opacityTo: 0,
|
||||
stops: [0, 70, 97],
|
||||
gradientToColors: ['#f7b733'],
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
markers: {
|
||||
size: type === 'area' ? 3 : 0,
|
||||
strokeWidth: 0,
|
||||
hover: { sizeOffset: 2 },
|
||||
colors: type === 'area' ? ['#FFFFFF'] : [color || '#F84525'],
|
||||
},
|
||||
xaxis: {
|
||||
categories: labels && labels.length > 0 ? labels : ['Sin datos'],
|
||||
axisBorder: { show: false },
|
||||
axisTicks: { show: false },
|
||||
labels: { style: { colors: isDarkMode ? '#91989e' : '#aaa' } },
|
||||
},
|
||||
yaxis: {
|
||||
labels: { style: { colors: isDarkMode ? '#91989e' : '#aaa' } },
|
||||
min: 0
|
||||
},
|
||||
grid: { borderColor: isDarkMode ? '#26292d' : '#eff2f7' },
|
||||
legend: {
|
||||
horizontalAlign: 'left',
|
||||
labels: { colors: isDarkMode ? '#ffffff' : '#433c3a' },
|
||||
},
|
||||
title: {
|
||||
text: title || 'Chart',
|
||||
style: { color: isDarkMode ? '#ffffff' : '#433c3a' }
|
||||
},
|
||||
theme: { mode: isDarkMode ? 'dark' : 'light' },
|
||||
noData: {
|
||||
text: 'No hay datos disponibles',
|
||||
align: 'center',
|
||||
verticalAlign: 'middle',
|
||||
style: {
|
||||
color: isDarkMode ? '#ffffff' : '#433c3a',
|
||||
fontSize: '14px'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Ensure series data is valid
|
||||
const series = [{
|
||||
name: title || 'Data',
|
||||
data: safeData && safeData.length > 0 ? safeData : [0]
|
||||
}];
|
||||
|
||||
return (
|
||||
<div className="card-enhanced">
|
||||
<div className="card-body p-4">
|
||||
<h5 className="card-title mb-3">{title || 'Chart'}</h5>
|
||||
{Chart && (
|
||||
<Chart
|
||||
options={options}
|
||||
series={series}
|
||||
type={type || 'area'}
|
||||
height={height || 350}
|
||||
key={`chart-${title}-${safeData.length}`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApexChart;
|
||||
Reference in New Issue
Block a user