Initial commit
This commit is contained in:
507
COMPONENTS.md
Normal file
507
COMPONENTS.md
Normal file
@@ -0,0 +1,507 @@
|
||||
# 🧩 COMPOSANTS - BTPXPRESS FRONTEND
|
||||
|
||||
## 📋 Table des matières
|
||||
|
||||
- [Vue d'ensemble](#vue-densemble)
|
||||
- [Composants Layout](#composants-layout)
|
||||
- [Composants Formulaires](#composants-formulaires)
|
||||
- [Composants Tableaux](#composants-tableaux)
|
||||
- [Composants Graphiques](#composants-graphiques)
|
||||
- [Composants Communs](#composants-communs)
|
||||
- [PrimeReact](#primereact)
|
||||
- [Création de composants](#création-de-composants)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Vue d'ensemble
|
||||
|
||||
L'application utilise **PrimeReact 10.2.1** comme bibliothèque de composants UI principale, complétée par des composants personnalisés.
|
||||
|
||||
### **Organisation**
|
||||
|
||||
```
|
||||
components/
|
||||
├── layout/ # Composants de mise en page
|
||||
├── forms/ # Formulaires
|
||||
├── tables/ # Tableaux de données
|
||||
├── charts/ # Graphiques
|
||||
└── common/ # Composants réutilisables
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Composants Layout
|
||||
|
||||
### **Header**
|
||||
|
||||
En-tête de l'application avec navigation et profil utilisateur.
|
||||
|
||||
**Fichier** : `components/layout/Header.tsx`
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
|
||||
import { Menubar } from 'primereact/menubar';
|
||||
import { Avatar } from 'primereact/avatar';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
|
||||
export const Header = () => {
|
||||
const { user, logout } = useAuth();
|
||||
|
||||
const items = [
|
||||
{ label: 'Dashboard', icon: 'pi pi-home', url: '/dashboard' },
|
||||
{ label: 'Chantiers', icon: 'pi pi-building', url: '/chantiers' },
|
||||
{ label: 'Clients', icon: 'pi pi-users', url: '/clients' },
|
||||
{ label: 'Matériel', icon: 'pi pi-wrench', url: '/materiels' },
|
||||
];
|
||||
|
||||
const end = (
|
||||
<div className="flex align-items-center gap-2">
|
||||
<Avatar label={user?.initials} shape="circle" />
|
||||
<span>{user?.name}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
return <Menubar model={items} end={end} />;
|
||||
};
|
||||
```
|
||||
|
||||
**Props** : Aucune
|
||||
|
||||
**Utilisation** :
|
||||
```typescript
|
||||
import { Header } from '@/components/layout/Header';
|
||||
|
||||
<Header />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Sidebar**
|
||||
|
||||
Menu latéral de navigation.
|
||||
|
||||
**Fichier** : `components/layout/Sidebar.tsx`
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
|
||||
import { PanelMenu } from 'primereact/panelmenu';
|
||||
import { MenuItem } from 'primereact/menuitem';
|
||||
|
||||
export const Sidebar = () => {
|
||||
const items: MenuItem[] = [
|
||||
{
|
||||
label: 'Dashboard',
|
||||
icon: 'pi pi-home',
|
||||
url: '/dashboard',
|
||||
},
|
||||
{
|
||||
label: 'Chantiers',
|
||||
icon: 'pi pi-building',
|
||||
items: [
|
||||
{ label: 'Liste', url: '/chantiers' },
|
||||
{ label: 'Nouveau', url: '/chantiers/nouveau' },
|
||||
{ label: 'Planning', url: '/chantiers/planning' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Clients',
|
||||
icon: 'pi pi-users',
|
||||
items: [
|
||||
{ label: 'Liste', url: '/clients' },
|
||||
{ label: 'Nouveau', url: '/clients/nouveau' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="sidebar">
|
||||
<PanelMenu model={items} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **Breadcrumb**
|
||||
|
||||
Fil d'Ariane pour la navigation.
|
||||
|
||||
**Fichier** : `components/layout/Breadcrumb.tsx`
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
|
||||
import { BreadCrumb } from 'primereact/breadcrumb';
|
||||
import { MenuItem } from 'primereact/menuitem';
|
||||
|
||||
interface BreadcrumbProps {
|
||||
items: MenuItem[];
|
||||
}
|
||||
|
||||
export const AppBreadcrumb: React.FC<BreadcrumbProps> = ({ items }) => {
|
||||
const home = { icon: 'pi pi-home', url: '/dashboard' };
|
||||
|
||||
return <BreadCrumb model={items} home={home} />;
|
||||
};
|
||||
```
|
||||
|
||||
**Utilisation** :
|
||||
```typescript
|
||||
const items = [
|
||||
{ label: 'Chantiers', url: '/chantiers' },
|
||||
{ label: 'Villa Moderne' },
|
||||
];
|
||||
|
||||
<AppBreadcrumb items={items} />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Composants Formulaires
|
||||
|
||||
### **ChantierForm**
|
||||
|
||||
Formulaire de création/modification de chantier.
|
||||
|
||||
**Fichier** : `components/forms/ChantierForm.tsx`
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
import { Calendar } from 'primereact/calendar';
|
||||
import { Button } from 'primereact/button';
|
||||
import type { ChantierDTO } from '@/types/models/Chantier';
|
||||
|
||||
interface ChantierFormProps {
|
||||
chantier?: ChantierDTO;
|
||||
onSubmit: (data: ChantierDTO) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export const ChantierForm: React.FC<ChantierFormProps> = ({
|
||||
chantier,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { control, handleSubmit, formState: { errors } } = useForm<ChantierDTO>({
|
||||
defaultValues: chantier || {},
|
||||
});
|
||||
|
||||
const statutOptions = [
|
||||
{ label: 'Planifié', value: 'PLANIFIE' },
|
||||
{ label: 'En cours', value: 'EN_COURS' },
|
||||
{ label: 'Terminé', value: 'TERMINE' },
|
||||
];
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="p-fluid">
|
||||
<div className="field">
|
||||
<label htmlFor="nom">Nom *</label>
|
||||
<Controller
|
||||
name="nom"
|
||||
control={control}
|
||||
rules={{ required: 'Le nom est obligatoire' }}
|
||||
render={({ field }) => (
|
||||
<InputText
|
||||
id="nom"
|
||||
{...field}
|
||||
className={errors.nom ? 'p-invalid' : ''}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.nom && <small className="p-error">{errors.nom.message}</small>}
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label htmlFor="statut">Statut</label>
|
||||
<Controller
|
||||
name="statut"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Dropdown
|
||||
id="statut"
|
||||
{...field}
|
||||
options={statutOptions}
|
||||
placeholder="Sélectionner un statut"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label htmlFor="dateDebut">Date de début</label>
|
||||
<Controller
|
||||
name="dateDebut"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Calendar
|
||||
id="dateDebut"
|
||||
{...field}
|
||||
dateFormat="dd/mm/yy"
|
||||
showIcon
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 justify-content-end">
|
||||
<Button
|
||||
label="Annuler"
|
||||
icon="pi pi-times"
|
||||
className="p-button-text"
|
||||
onClick={onCancel}
|
||||
type="button"
|
||||
/>
|
||||
<Button
|
||||
label="Enregistrer"
|
||||
icon="pi pi-check"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Composants Tableaux
|
||||
|
||||
### **DataTableWrapper**
|
||||
|
||||
Wrapper pour DataTable de PrimeReact avec fonctionnalités communes.
|
||||
|
||||
**Fichier** : `components/tables/DataTableWrapper.tsx`
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
|
||||
import { DataTable } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
import { Button } from 'primereact/button';
|
||||
|
||||
interface DataTableWrapperProps<T> {
|
||||
data: T[];
|
||||
columns: ColumnConfig[];
|
||||
onEdit?: (item: T) => void;
|
||||
onDelete?: (item: T) => void;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
interface ColumnConfig {
|
||||
field: string;
|
||||
header: string;
|
||||
sortable?: boolean;
|
||||
body?: (rowData: any) => React.ReactNode;
|
||||
}
|
||||
|
||||
export function DataTableWrapper<T>({
|
||||
data,
|
||||
columns,
|
||||
onEdit,
|
||||
onDelete,
|
||||
loading = false,
|
||||
}: DataTableWrapperProps<T>) {
|
||||
const actionBodyTemplate = (rowData: T) => {
|
||||
return (
|
||||
<div className="flex gap-2">
|
||||
{onEdit && (
|
||||
<Button
|
||||
icon="pi pi-pencil"
|
||||
className="p-button-rounded p-button-text"
|
||||
onClick={() => onEdit(rowData)}
|
||||
/>
|
||||
)}
|
||||
{onDelete && (
|
||||
<Button
|
||||
icon="pi pi-trash"
|
||||
className="p-button-rounded p-button-text p-button-danger"
|
||||
onClick={() => onDelete(rowData)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
value={data}
|
||||
paginator
|
||||
rows={10}
|
||||
loading={loading}
|
||||
emptyMessage="Aucune donnée disponible"
|
||||
>
|
||||
{columns.map((col) => (
|
||||
<Column
|
||||
key={col.field}
|
||||
field={col.field}
|
||||
header={col.header}
|
||||
sortable={col.sortable}
|
||||
body={col.body}
|
||||
/>
|
||||
))}
|
||||
{(onEdit || onDelete) && (
|
||||
<Column
|
||||
body={actionBodyTemplate}
|
||||
header="Actions"
|
||||
style={{ width: '10rem' }}
|
||||
/>
|
||||
)}
|
||||
</DataTable>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Utilisation** :
|
||||
```typescript
|
||||
const columns = [
|
||||
{ field: 'nom', header: 'Nom', sortable: true },
|
||||
{ field: 'code', header: 'Code', sortable: true },
|
||||
{ field: 'statut', header: 'Statut' },
|
||||
];
|
||||
|
||||
<DataTableWrapper
|
||||
data={chantiers}
|
||||
columns={columns}
|
||||
onEdit={handleEdit}
|
||||
onDelete={handleDelete}
|
||||
loading={loading}
|
||||
/>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Composants Graphiques
|
||||
|
||||
### **ChartBar**
|
||||
|
||||
Graphique en barres avec Chart.js.
|
||||
|
||||
**Fichier** : `components/charts/ChartBar.tsx`
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
|
||||
import { Chart } from 'primereact/chart';
|
||||
|
||||
interface ChartBarProps {
|
||||
data: {
|
||||
labels: string[];
|
||||
datasets: {
|
||||
label: string;
|
||||
data: number[];
|
||||
backgroundColor?: string;
|
||||
}[];
|
||||
};
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export const ChartBar: React.FC<ChartBarProps> = ({ data, title }) => {
|
||||
const options = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top' as const,
|
||||
},
|
||||
title: {
|
||||
display: !!title,
|
||||
text: title,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return <Chart type="bar" data={data} options={options} />;
|
||||
};
|
||||
```
|
||||
|
||||
**Utilisation** :
|
||||
```typescript
|
||||
const data = {
|
||||
labels: ['Jan', 'Fév', 'Mar', 'Avr'],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Chantiers',
|
||||
data: [12, 19, 8, 15],
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.5)',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
<ChartBar data={data} title="Chantiers par mois" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Composants Communs
|
||||
|
||||
### **LoadingSpinner**
|
||||
|
||||
Indicateur de chargement.
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
|
||||
import { ProgressSpinner } from 'primereact/progressspinner';
|
||||
|
||||
export const LoadingSpinner = () => {
|
||||
return (
|
||||
<div className="flex justify-content-center align-items-center" style={{ height: '400px' }}>
|
||||
<ProgressSpinner />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### **ErrorMessage**
|
||||
|
||||
Message d'erreur.
|
||||
|
||||
```typescript
|
||||
'use client';
|
||||
|
||||
import { Message } from 'primereact/message';
|
||||
|
||||
interface ErrorMessageProps {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export const ErrorMessage: React.FC<ErrorMessageProps> = ({ message }) => {
|
||||
return <Message severity="error" text={message} />;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 PrimeReact
|
||||
|
||||
### **Composants utilisés**
|
||||
|
||||
| Composant | Usage |
|
||||
|-----------|-------|
|
||||
| **DataTable** | Tableaux de données |
|
||||
| **Button** | Boutons |
|
||||
| **InputText** | Champs texte |
|
||||
| **Dropdown** | Listes déroulantes |
|
||||
| **Calendar** | Sélecteur de date |
|
||||
| **Dialog** | Modales |
|
||||
| **Toast** | Notifications |
|
||||
| **Chart** | Graphiques |
|
||||
| **Menubar** | Menu principal |
|
||||
| **PanelMenu** | Menu latéral |
|
||||
|
||||
### **Documentation**
|
||||
|
||||
https://primereact.org/
|
||||
|
||||
---
|
||||
|
||||
**Dernière mise à jour** : 2025-09-30
|
||||
**Version** : 1.0
|
||||
**Auteur** : Équipe BTPXpress
|
||||
|
||||
Reference in New Issue
Block a user