PFEE/dashboard-sqdc/src/components/RangeChartModal.tsx
Alexis Bruneteau 770c41d5e0 feat: Add Shadcn UI, dark theme, and complete Docker/K8s deployment setup
Major Changes:
- Migrate UI to Shadcn components with Tailwind CSS v3
- Implement dark theme as default with improved color scheme
- Optimize homepage layout to fit single screen without scrolling
- Fix chart visibility with explicit colors for dark mode

Deployment Infrastructure:
- Add Docker multi-stage build with Nginx + Node.js
- Create Kubernetes manifests (deployment, service, ingress, PVC)
- Configure Gitea CI/CD workflow with registry integration
- Add deployment scripts with registry support

CI/CD Configuration:
- Registry: gitea.vidoks.fr/sortifal/pfee
- Automatic build and push on commits
- Kubernetes deployment with image pull secrets
- Three-stage pipeline: build, deploy, notify

Documentation:
- Add DEPLOYMENT.md with comprehensive deployment guide
- Add SETUP-REGISTRY.md with step-by-step registry setup
- Add workflow README with troubleshooting guide
- Include configuration examples and best practices

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-22 00:42:32 +02:00

148 lines
3.6 KiB
TypeScript

import React, { useState } from 'react';
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from 'chart.js';
import { Line } from 'react-chartjs-2';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from './ui/dialog';
import { Button } from './ui/button';
import { LineChart } from 'lucide-react';
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);
interface RangeChartModalProps {
isOpen: boolean;
kpi: any;
measurements: any[];
getMeasurementsForRange: (days: number) => any[];
onClose: () => void;
}
export const RangeChartModal: React.FC<RangeChartModalProps> = ({
isOpen,
kpi,
measurements,
getMeasurementsForRange,
onClose,
}) => {
const [selectedRange, setSelectedRange] = useState<number>(30);
if (!kpi) return null;
const filteredMeasurements = getMeasurementsForRange(selectedRange);
const labels = filteredMeasurements
.map(m => new Date(m.measurement_date).toLocaleDateString('fr-FR'))
.reverse();
const values = filteredMeasurements
.map(m => m.value)
.reverse();
const chartData = {
labels,
datasets: [
{
label: kpi.name,
data: values,
borderColor: '#60a5fa',
backgroundColor: 'rgba(96, 165, 250, 0.1)',
tension: 0.4,
fill: true,
pointRadius: 4,
pointBackgroundColor: '#60a5fa',
pointBorderColor: '#1e293b',
pointBorderWidth: 2,
},
],
};
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'top' as const,
labels: {
color: '#cbd5e1',
},
},
title: {
display: false,
},
},
scales: {
y: {
beginAtZero: true,
title: {
display: true,
text: kpi.unit,
color: '#cbd5e1',
},
ticks: {
color: '#94a3b8',
},
grid: {
color: 'rgba(148, 163, 184, 0.1)',
},
},
x: {
display: true,
ticks: {
color: '#94a3b8',
},
grid: {
color: 'rgba(148, 163, 184, 0.1)',
},
},
},
};
const ranges = [
{ value: 7, label: 'Semaine' },
{ value: 30, label: 'Mois' },
{ value: 90, label: 'Trimestre' },
{ value: 365, label: 'Année' },
{ value: -1, label: 'Tout' },
];
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="sm:max-w-[900px] max-h-[90vh]">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<LineChart className="h-5 w-5" />
{kpi.name}
</DialogTitle>
<DialogDescription>
Mesures: {filteredMeasurements.length} | Période: {labels[0]} à {labels[labels.length - 1]}
</DialogDescription>
</DialogHeader>
<div className="flex gap-2 flex-wrap py-2">
{ranges.map((range) => (
<Button
key={range.value}
variant={selectedRange === range.value ? 'default' : 'outline'}
size="sm"
onClick={() => setSelectedRange(range.value)}
>
{range.label}
</Button>
))}
</div>
<div className="py-4">
<div style={{ height: '500px', width: '100%' }}>
<Line data={chartData} options={chartOptions} />
</div>
</div>
</DialogContent>
</Dialog>
);
};