refactor: major architecture improvements
Security: - Remove exposed NocoDB token from client code - Add .env.example for environment variables Shared Components: - Carousel: touch/keyboard navigation, accessibility - ChartCard: reusable chart container - EmptyState: for no-data scenarios - FilterControls: collapsible filter panel with reset button - StatCard: metric display with change indicator - ToggleSwitch: accessible radio-style toggle Architecture: - Create src/config/chartConfig.js for shared chart options - Extract ChartJS registration to single location - Reduce code duplication in Dashboard and Comparison UX Improvements: - Add empty state when filters return no data - Add Reset Filters button to filter controls - Add skeleton loader CSS utilities - Improve focus states for accessibility - Use shared components in Dashboard and Comparison
This commit is contained in:
97
src/config/chartConfig.js
Normal file
97
src/config/chartConfig.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
BarElement,
|
||||
ArcElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
Filler
|
||||
} from 'chart.js';
|
||||
import ChartDataLabels from 'chartjs-plugin-datalabels';
|
||||
|
||||
// Register ChartJS components once
|
||||
ChartJS.register(
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
BarElement,
|
||||
ArcElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
Filler,
|
||||
ChartDataLabels
|
||||
);
|
||||
|
||||
export const chartColors = {
|
||||
primary: '#2563eb',
|
||||
secondary: '#7c3aed',
|
||||
tertiary: '#0891b2',
|
||||
success: '#059669',
|
||||
danger: '#dc2626',
|
||||
muted: '#94a3b8',
|
||||
grid: '#f1f5f9'
|
||||
};
|
||||
|
||||
export const createDataLabelConfig = (showDataLabels) => ({
|
||||
display: showDataLabels,
|
||||
color: '#1e293b',
|
||||
font: { size: 10, weight: 600 },
|
||||
anchor: 'end',
|
||||
align: 'end',
|
||||
offset: 4,
|
||||
padding: 4,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.85)',
|
||||
borderRadius: 3,
|
||||
formatter: (value) => {
|
||||
if (value == null) return '';
|
||||
if (value >= 1000000) return (value / 1000000).toFixed(2) + 'M';
|
||||
if (value >= 1000) return (value / 1000).toFixed(2) + 'K';
|
||||
if (value < 100 && value > 0) return value.toFixed(2);
|
||||
return Math.round(value).toLocaleString();
|
||||
}
|
||||
});
|
||||
|
||||
export const createBaseOptions = (showDataLabels) => ({
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
backgroundColor: '#1e293b',
|
||||
padding: 12,
|
||||
cornerRadius: 8,
|
||||
titleFont: { size: 12 },
|
||||
bodyFont: { size: 11 }
|
||||
},
|
||||
datalabels: createDataLabelConfig(showDataLabels)
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: { display: false },
|
||||
ticks: { font: { size: 10 }, color: '#94a3b8' }
|
||||
},
|
||||
y: {
|
||||
grid: { color: chartColors.grid },
|
||||
ticks: { font: { size: 10 }, color: '#94a3b8' },
|
||||
border: { display: false }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export const lineDatasetDefaults = {
|
||||
borderWidth: 2,
|
||||
tension: 0.4,
|
||||
fill: true,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 4
|
||||
};
|
||||
|
||||
export const barDatasetDefaults = {
|
||||
borderRadius: 4
|
||||
};
|
||||
Reference in New Issue
Block a user