Checkpoint: Ajout de la vue liste (tableau) en complément de la vue vignettes — toggle Vignettes/Liste dans la barre de filtres, tableau avec tri par colonne, barres de vétusté colorées, budget en orange/vert.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// Page principale — Budget Renouvellement SI 2027 - Itinova
|
||||
// Design: Corporate Modernism — Sidebar bleu marine + grille de cards
|
||||
// Design: Corporate Modernism — Sidebar bleu marine + grille de cards + vue liste
|
||||
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
@@ -15,14 +15,19 @@ import {
|
||||
AlertTriangle,
|
||||
CheckCircle,
|
||||
ChevronDown,
|
||||
LayoutGrid,
|
||||
List,
|
||||
} from 'lucide-react';
|
||||
import { useBudgetData } from '../hooks/useBudgetData';
|
||||
import { formatEuros, formatNumber } from '../lib/format';
|
||||
import { EtablissementCard } from '../components/EtablissementCard';
|
||||
import { EtablissementDetail } from '../components/EtablissementDetail';
|
||||
import { EtablissementListView } from '../components/EtablissementListView';
|
||||
import { KpiCard } from '../components/KpiCard';
|
||||
import type { Etablissement, SortField, FilterType } from '../types/budget';
|
||||
|
||||
type ViewMode = 'grid' | 'list';
|
||||
|
||||
export default function Home() {
|
||||
const {
|
||||
meta,
|
||||
@@ -39,6 +44,7 @@ export default function Home() {
|
||||
|
||||
const [selectedEtab, setSelectedEtab] = useState<Etablissement | null>(null);
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
const [viewMode, setViewMode] = useState<ViewMode>('grid');
|
||||
|
||||
const sortOptions: { value: SortField; label: string }[] = [
|
||||
{ value: 'budget_total', label: 'Budget total' },
|
||||
@@ -194,7 +200,7 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Barre de filtres */}
|
||||
{/* Barre de filtres + toggle vue */}
|
||||
<div className="px-6 py-3 border-b border-border bg-card flex-shrink-0">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
{/* Recherche */}
|
||||
@@ -225,38 +231,71 @@ export default function Home() {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Tri */}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground">Trier par :</span>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={sortField}
|
||||
onChange={(e) => toggleSort(e.target.value as SortField)}
|
||||
className="pl-3 pr-8 py-2 text-sm bg-muted/50 border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/30 appearance-none cursor-pointer"
|
||||
{/* Tri (masqué en vue liste car tri par colonne) */}
|
||||
{viewMode === 'grid' && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-muted-foreground">Trier par :</span>
|
||||
<div className="relative">
|
||||
<select
|
||||
value={sortField}
|
||||
onChange={(e) => toggleSort(e.target.value as SortField)}
|
||||
className="pl-3 pr-8 py-2 text-sm bg-muted/50 border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/30 appearance-none cursor-pointer"
|
||||
>
|
||||
{sortOptions.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => toggleSort(sortField)}
|
||||
className="p-2 rounded-lg border border-border bg-muted/50 hover:bg-muted transition-colors"
|
||||
title={sortOrder === 'asc' ? 'Croissant' : 'Décroissant'}
|
||||
>
|
||||
{sortOptions.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{sortOrder === 'asc' ? (
|
||||
<SortAsc className="w-4 h-4 text-muted-foreground" />
|
||||
) : (
|
||||
<SortDesc className="w-4 h-4 text-muted-foreground" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Spacer */}
|
||||
<div className="flex-1" />
|
||||
|
||||
{/* Toggle vue vignettes / liste */}
|
||||
<div className="flex items-center gap-1 bg-muted/50 border border-border rounded-lg p-1">
|
||||
<button
|
||||
onClick={() => toggleSort(sortField)}
|
||||
className="p-2 rounded-lg border border-border bg-muted/50 hover:bg-muted transition-colors"
|
||||
title={sortOrder === 'asc' ? 'Croissant' : 'Décroissant'}
|
||||
onClick={() => setViewMode('grid')}
|
||||
title="Vue vignettes"
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all ${
|
||||
viewMode === 'grid'
|
||||
? 'bg-white shadow-sm text-primary border border-border'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
{sortOrder === 'asc' ? (
|
||||
<SortAsc className="w-4 h-4 text-muted-foreground" />
|
||||
) : (
|
||||
<SortDesc className="w-4 h-4 text-muted-foreground" />
|
||||
)}
|
||||
<LayoutGrid className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Vignettes</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode('list')}
|
||||
title="Vue liste"
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all ${
|
||||
viewMode === 'list'
|
||||
? 'bg-white shadow-sm text-primary border border-border'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
<List className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Liste</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Grille des établissements */}
|
||||
{/* Contenu — vue vignettes ou liste */}
|
||||
<div className="flex-1 overflow-y-auto px-6 py-5">
|
||||
{etablissements.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-16 text-center">
|
||||
@@ -266,7 +305,7 @@ export default function Home() {
|
||||
Modifiez vos critères de recherche ou de filtre.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
) : viewMode === 'grid' ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
{etablissements.map((etab, index) => (
|
||||
<EtablissementCard
|
||||
@@ -277,6 +316,14 @@ export default function Home() {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<EtablissementListView
|
||||
etablissements={etablissements}
|
||||
onSelect={(etab) => setSelectedEtab(etab)}
|
||||
sortField={sortField}
|
||||
sortOrder={sortOrder}
|
||||
onSort={toggleSort}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user