diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/client/index.html b/client/index.html index 48faf7a..0326373 100644 --- a/client/index.html +++ b/client/index.html @@ -1,17 +1,15 @@ - + - Budget Renouvellement SI 2027 - Itinova - + diff --git a/client/src/App.tsx b/client/src/App.tsx index 0828668..9d83cf5 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,3 +1,6 @@ +// App.tsx — Budget Renouvellement SI 2027 - Itinova +// Design: Corporate Modernism — Thème clair institutionnel + import { Toaster } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; import NotFound from "@/pages/NotFound"; @@ -6,30 +9,20 @@ import ErrorBoundary from "./components/ErrorBoundary"; import { ThemeProvider } from "./contexts/ThemeContext"; import Home from "./pages/Home"; - function Router() { return ( - {/* Final fallback route */} ); } -// NOTE: About Theme -// - First choose a default theme according to your design style (dark or light bg), than change color palette in index.css -// to keep consistent foreground/background color across components -// - If you want to make theme switchable, pass `switchable` ThemeProvider and use `useTheme` hook - function App() { return ( - + diff --git a/client/src/components/EtablissementCard.tsx b/client/src/components/EtablissementCard.tsx new file mode 100644 index 0000000..b7ea45b --- /dev/null +++ b/client/src/components/EtablissementCard.tsx @@ -0,0 +1,101 @@ +// Composant EtablissementCard — Design Corporate Modernism Itinova +// Card d'établissement avec indicateurs de vétusté et budget + +import { Monitor, Laptop, Euro, ChevronRight } from 'lucide-react'; +import type { Etablissement } from '../types/budget'; +import { formatEuros, getVetustePercent, getBarColor } from '../lib/format'; + +interface EtablissementCardProps { + etablissement: Etablissement; + onClick: () => void; + index: number; +} + +export function EtablissementCard({ etablissement, onClick, index }: EtablissementCardProps) { + const { stats, nom, code } = etablissement; + const percentFixes = getVetustePercent(stats.nb_fixes_renouveler, stats.nb_fixes_total); + const percentPortables = getVetustePercent(stats.nb_portables_renouveler, stats.nb_portables_total); + const hasBudget = stats.budget_total > 0; + + return ( +
e.key === 'Enter' && onClick()} + aria-label={`Voir le détail de ${nom}`} + > + {/* Liseré de couleur en haut selon le budget */} +
20000 ? 'bg-red-500' : 'bg-orange-400') : 'bg-green-500'}`} + /> + +
+ {/* En-tête */} +
+
+

{code}

+

+ {nom} +

+
+ +
+ + {/* Budget total */} +
+
+ + Budget renouvellement 2027 +
+

+ {formatEuros(stats.budget_total)} +

+
+ + {/* Indicateurs fixes */} +
+
+
+
+ + PC Fixes +
+ + {stats.nb_fixes_renouveler}/{stats.nb_fixes_total} + à renouveler + +
+
+
+
+
+ +
+
+
+ + PC Portables +
+ + {stats.nb_portables_renouveler}/{stats.nb_portables_total} + à renouveler + +
+
+
+
+
+
+
+
+ ); +} diff --git a/client/src/components/EtablissementDetail.tsx b/client/src/components/EtablissementDetail.tsx new file mode 100644 index 0000000..3f817e5 --- /dev/null +++ b/client/src/components/EtablissementDetail.tsx @@ -0,0 +1,327 @@ +// Composant EtablissementDetail — Design Corporate Modernism Itinova +// Panneau de détail d'un établissement avec budget détaillé et liste des équipements + +import { useState } from 'react'; +import { X, Monitor, Laptop, Euro, AlertTriangle, CheckCircle, Printer } from 'lucide-react'; +import type { Etablissement } from '../types/budget'; +import { formatEuros, getVetustePercent, getBarColor } from '../lib/format'; +import { MaterielTable } from './MaterielTable'; + +interface EtablissementDetailProps { + etablissement: Etablissement | null; + onClose: () => void; + coutFixe: number; + coutPortable: number; + seuilFixesAns: number; + seuilPortablesAns: number; +} + +type TabType = 'resume' | 'fixes' | 'portables'; + +export function EtablissementDetail({ + etablissement, + onClose, + coutFixe, + coutPortable, + seuilFixesAns, + seuilPortablesAns, +}: EtablissementDetailProps) { + const [activeTab, setActiveTab] = useState('resume'); + const [showOnlyToRenew, setShowOnlyToRenew] = useState(false); + + if (!etablissement) return null; + + const { stats, nom, code, fixes, portables } = etablissement; + const percentFixes = getVetustePercent(stats.nb_fixes_renouveler, stats.nb_fixes_total); + const percentPortables = getVetustePercent(stats.nb_portables_renouveler, stats.nb_portables_total); + + const handlePrint = () => { + window.print(); + }; + + return ( + <> + {/* Overlay */} +