Files
btpxpress-frontend/COMPONENTS.md
2025-10-13 05:29:32 +02:00

10 KiB

🧩 COMPOSANTS - BTPXPRESS FRONTEND

📋 Table des matières


🎯 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

'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 :

import { Header } from '@/components/layout/Header';

<Header />

Sidebar

Menu latéral de navigation.

Fichier : components/layout/Sidebar.tsx

'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

'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 :

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

'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

'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 :

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

'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 :

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.

'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.

'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