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>
198 lines
6.7 KiB
TypeScript
198 lines
6.7 KiB
TypeScript
'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>
|
|
);
|
|
}
|