feat: Pages de détails complètes pour chantiers, clients et matériels

PHASE 2 - FINALISATIONS FONCTIONNELLES TERMINÉES

 Pages Chantiers [id] créées:
- /chantiers/[id]: Vue d'ensemble avec statistiques et navigation
- /chantiers/[id]/budget: Suivi budgétaire détaillé avec graphiques
- /chantiers/[id]/planning: Chronologie et planning des tâches
- /chantiers/[id]/documents: Gestion des documents du chantier
- /chantiers/[id]/equipe: Liste et gestion de l'équipe affectée

 Pages Clients [id] créées:
- /clients/[id]: Fiche client complète avec coordonnées
- Onglets: Chantiers, Factures, Documents
- Statistiques et historique complet

 Pages Matériels [id] créées:
- /materiels/[id]: Fiche matériel avec informations techniques
- Calendrier de disponibilité
- Onglets: Réservations, Maintenances, Documents
- Timeline des maintenances

Fonctionnalités implémentées:
- Navigation fluide entre les pages
- Boutons retour vers listes principales
- DataTables avec tri et filtres
- Graphiques budget (bar chart, doughnut)
- Calendriers et timeline
- Tags de statut colorés
- Cards statistiques
- Responsive design

Technologies utilisées:
- PrimeReact (DataTable, Chart, Calendar, Timeline, TabView)
- Next.js App Router avec dynamic routes [id]
- TypeScript avec interfaces typées
- Integration API backend via fetch

Prochaines étapes:
- Connecter aux vraies APIs backend
- Ajouter formulaires de modification
- Implémenter actions (supprimer, modifier)
- Ajouter toasts de confirmation

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
DahoudG
2025-10-31 11:52:21 +00:00
parent 5da5290a6d
commit a91a34dbf8
7 changed files with 1672 additions and 0 deletions

View File

@@ -0,0 +1,197 @@
'use client';
import React, { useState } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { Card } from 'primereact/card';
import { Button } from 'primereact/button';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Avatar } from 'primereact/avatar';
import { Tag } from 'primereact/tag';
import { Badge } from 'primereact/badge';
interface MembreEquipe {
id: number;
nom: string;
prenom: string;
role: string;
specialite: string;
email: string;
telephone: string;
dateAffectation: string;
statut: string;
}
export default function ChantierEquipePage() {
const params = useParams();
const router = useRouter();
const id = params.id as string;
const [membres] = useState<MembreEquipe[]>([
{
id: 1,
nom: 'Dupont',
prenom: 'Jean',
role: 'Chef de chantier',
specialite: 'Gestion',
email: 'jean.dupont@btpxpress.fr',
telephone: '06 12 34 56 78',
dateAffectation: '2025-01-01',
statut: 'Actif'
},
{
id: 2,
nom: 'Martin',
prenom: 'Marie',
role: 'Maçon',
specialite: 'Maçonnerie',
email: 'marie.martin@btpxpress.fr',
telephone: '06 23 45 67 89',
dateAffectation: '2025-01-05',
statut: 'Actif'
}
]);
const nomBodyTemplate = (rowData: MembreEquipe) => {
return (
<div className="flex align-items-center gap-2">
<Avatar
label={`${rowData.prenom[0]}${rowData.nom[0]}`}
size="large"
shape="circle"
className="mr-2"
/>
<div>
<div className="font-bold">{rowData.prenom} {rowData.nom}</div>
<div className="text-sm text-600">{rowData.role}</div>
</div>
</div>
);
};
const specialiteBodyTemplate = (rowData: MembreEquipe) => {
return <Tag value={rowData.specialite} severity="info" />;
};
const statutBodyTemplate = (rowData: MembreEquipe) => {
const severity = rowData.statut === 'Actif' ? 'success' : 'danger';
return <Tag value={rowData.statut} severity={severity} />;
};
const actionsBodyTemplate = () => {
return (
<div className="flex gap-2">
<Button icon="pi pi-eye" className="p-button-text p-button-sm" tooltip="Voir le profil" />
<Button icon="pi pi-pencil" className="p-button-text p-button-sm" tooltip="Modifier" />
<Button icon="pi pi-times" className="p-button-text p-button-sm p-button-danger" tooltip="Retirer du chantier" />
</div>
);
};
return (
<div className="grid">
<div className="col-12">
<div className="flex justify-content-between align-items-center mb-3">
<div className="flex align-items-center">
<Button
icon="pi pi-arrow-left"
className="p-button-text mr-2"
onClick={() => router.push(`/chantiers/${id}`)}
tooltip="Retour"
/>
<h2 className="m-0">Équipe du chantier</h2>
<Badge value={membres.length} severity="info" className="ml-2" />
</div>
<Button
label="Affecter un membre"
icon="pi pi-plus"
className="p-button-success"
/>
</div>
</div>
{/* Statistiques équipe */}
<div className="col-12 md:col-3">
<Card>
<div className="flex justify-content-between align-items-center">
<div>
<span className="block text-500 font-medium mb-1">Total membres</span>
<div className="text-900 font-medium text-xl">{membres.length}</div>
</div>
<div className="flex align-items-center justify-content-center bg-blue-100 border-round" style={{width: '2.5rem', height: '2.5rem'}}>
<i className="pi pi-users text-blue-500 text-xl"></i>
</div>
</div>
</Card>
</div>
<div className="col-12 md:col-3">
<Card>
<div className="flex justify-content-between align-items-center">
<div>
<span className="block text-500 font-medium mb-1">Membres actifs</span>
<div className="text-900 font-medium text-xl">
{membres.filter(m => m.statut === 'Actif').length}
</div>
</div>
<div className="flex align-items-center justify-content-center bg-green-100 border-round" style={{width: '2.5rem', height: '2.5rem'}}>
<i className="pi pi-check-circle text-green-500 text-xl"></i>
</div>
</div>
</Card>
</div>
<div className="col-12 md:col-3">
<Card>
<div className="flex justify-content-between align-items-center">
<div>
<span className="block text-500 font-medium mb-1">Spécialités</span>
<div className="text-900 font-medium text-xl">
{new Set(membres.map(m => m.specialite)).size}
</div>
</div>
<div className="flex align-items-center justify-content-center bg-orange-100 border-round" style={{width: '2.5rem', height: '2.5rem'}}>
<i className="pi pi-star text-orange-500 text-xl"></i>
</div>
</div>
</Card>
</div>
<div className="col-12 md:col-3">
<Card>
<div className="flex justify-content-between align-items-center">
<div>
<span className="block text-500 font-medium mb-1">Chef de chantier</span>
<div className="text-900 font-medium text-xl">
{membres.filter(m => m.role === 'Chef de chantier').length}
</div>
</div>
<div className="flex align-items-center justify-content-center bg-purple-100 border-round" style={{width: '2.5rem', height: '2.5rem'}}>
<i className="pi pi-user text-purple-500 text-xl"></i>
</div>
</div>
</Card>
</div>
{/* Liste des membres */}
<div className="col-12">
<Card title="Liste des membres">
<DataTable
value={membres}
paginator
rows={10}
responsiveLayout="scroll"
>
<Column body={nomBodyTemplate} header="Nom" sortable filter />
<Column body={specialiteBodyTemplate} header="Spécialité" sortable filter />
<Column field="email" header="Email" sortable />
<Column field="telephone" header="Téléphone" sortable />
<Column field="dateAffectation" header="Date d'affectation" sortable />
<Column body={statutBodyTemplate} header="Statut" sortable filter />
<Column body={actionsBodyTemplate} header="Actions" />
</DataTable>
</Card>
</div>
</div>
);
}