Leçon 2 / 15

Organiser les composants du tableau de bord

20 min React — données, effets et formulaires Gratuit

Objectif : Découper l’interface du tableau de bord en composants fonctionnels et attribuer une responsabilité à chacun.

Une page de tableau de bord rassemble plusieurs zones : une introduction, une saisie, des filtres, une liste et un récapitulatif. Si vous écrivez toute l’interface dans un seul composant, il devient difficile de repérer où modifier une zone sans toucher aux autres. Vous allez répartir ces zones dans les composants prévus par le projet.

La leçon précédente a défini `Depense` et `CategorieDepense` dans `src/types.ts`. Vous n’avez pas besoin de modifier ces types ici : l’objectif est d’organiser l’interface, pas encore d’afficher une collection de dépenses ni de charger des données.

Attribuer une responsabilité à chaque composant

Un composant est une partie de l’interface que vous pouvez nommer et réutiliser dans l’arbre de rendu React. Donnez à chaque composant une responsabilité lisible : `Entete` présente le tableau de bord, `FormulaireDepense` porte la zone de saisie, `FiltresDepenses` présente les critères de consultation, `ListeDepenses` correspond à la zone de liste et `RecapitulatifMensuel` affiche la zone de synthèse.

Le composant racine `App` assemble ces parties et fournit la structure générale de la page. Il ne doit pas recopier le contenu de chaque zone : il exprime leur organisation. Ce découpage suit directement les fichiers imposés par le contrat ; ne créez pas de nouveau composant ou de classe CSS pour cette étape.

Créer les fichiers des zones de l’interface

Chaque composant ci-dessous est autonome et reste volontairement statique : il prépare la structure de l’interface sans anticiper les champs contrôlés, les filtres actifs ou le chargement des dépenses. Les balises et classes reprennent le squelette de référence. Le formulaire conserve notamment les identifiants et noms de champs, tandis que les options de catégorie seront raccordées plus tard aux données du projet.

src/composants/Entete.tsxTexte
export default function Entete() {
  return (
    <header className="depenses__entete">
      <h1>Tableau de bord des dépenses</h1>
      <p>Suivez vos dépenses personnelles, vos catégories et votre budget mensuel.</p>
    </header>
  );
}
src/composants/FormulaireDepense.tsxTexte
export default function FormulaireDepense() {
  return (
    <section className="depenses__panneau" aria-labelledby="titre-saisie">
      <h2 className="depenses__titre" id="titre-saisie">Ajouter une dépense</h2>
      <form className="depenses__formulaire">
        <label className="depenses__champ" htmlFor="libelle">Libellé</label>
        <input id="libelle" name="libelle" type="text" required />
        <label className="depenses__champ" htmlFor="montant">Montant</label>
        <input id="montant" name="montant" type="number" min="0.01" step="0.01" required />
        <label className="depenses__champ" htmlFor="categorie">Catégorie</label>
        <select id="categorie" name="categorie" required>
          <option value="">Choisir une catégorie</option>
        </select>
        <label className="depenses__champ" htmlFor="date">Date</label>
        <input id="date" name="date" type="date" required />
        <p className="depenses__erreur" role="alert"></p>
        <button type="submit">Ajouter</button>
      </form>
    </section>
  );
}
src/composants/FiltresDepenses.tsxTexte
export default function FiltresDepenses() {
  return (
    <section className="depenses__panneau" aria-labelledby="titre-filtres">
      <h2 className="depenses__titre" id="titre-filtres">Filtres</h2>
      <div className="depenses__filtres">
        <label htmlFor="filtre-mois">Mois</label>
        <input id="filtre-mois" name="mois" type="month" />
        <label htmlFor="filtre-categorie">Catégorie</label>
        <select id="filtre-categorie" name="categorie">
          <option value="">Toutes les catégories</option>
        </select>
      </div>
    </section>
  );
}
src/composants/ListeDepenses.tsxTexte
export default function ListeDepenses() {
  return (
    <section className="depenses__panneau" aria-labelledby="titre-liste">
      <h2 className="depenses__titre" id="titre-liste">Dépenses</h2>
      <div className="depenses__etat" role="status" aria-live="polite"></div>
      <ul className="depenses__liste">
        <li className="depenses__ligne">
          <span>Exemple de dépense</span>
          <span>Catégorie</span>
          <time dateTime="2025-01-01">1 janvier 2025</time>
          <span>12,50 €</span>
        </li>
      </ul>
    </section>
  );
}
src/composants/RecapitulatifMensuel.tsxTexte
export default function RecapitulatifMensuel() {
  return (
    <section className="depenses__panneau" aria-labelledby="titre-recapitulatif">
      <h2 className="depenses__titre" id="titre-recapitulatif">Récapitulatif mensuel</h2>
      <div className="depenses__recapitulatif">
        <p>Mois sélectionné : <span></span></p>
        <p>Total des dépenses : <strong>0,00 €</strong></p>
        <p>Nombre de dépenses : <strong>0</strong></p>
      </div>
    </section>
  );
}

Composer les composants dans App

`App.tsx` importe les composants puis les place dans l’ordre voulu sous le conteneur principal. Cette utilisation d’un composant dans un autre est la composition de composants : chaque élément conserve son propre contenu, tandis que `App` décrit l’agencement de la page. Les noms de classes restent ceux du contrat et les fichiers restent dans les répertoires indiqués.

src/App.tsxTexte
import Entete from "./composants/Entete";
import FormulaireDepense from "./composants/FormulaireDepense";
import FiltresDepenses from "./composants/FiltresDepenses";
import ListeDepenses from "./composants/ListeDepenses";
import RecapitulatifMensuel from "./composants/RecapitulatifMensuel";

export default function App() {
  return (
    <div className="depenses">
      <Entete />
      <main className="depenses__contenu">
        <FormulaireDepense />
        <FiltresDepenses />
        <ListeDepenses />
        <RecapitulatifMensuel />
      </main>
    </div>
  );
}

Après avoir créé ces fichiers, `App` rend les cinq zones dans le conteneur de page. Le résultat correspond à la structure du tableau de bord : les libellés de l’exemple sont encore statiques, et les listes déroulantes ne proposent pas encore de catégories. Ce comportement est attendu à cette étape ; les prochains composants de données donneront du contenu réel à la liste.

À retenir

  • Attribuez à chaque composant une responsabilité d’interface clairement identifiable.
  • Conservez un fichier par composant et respectez les chemins définis par le projet.
  • Utilisez `App` pour assembler les zones plutôt que d’y recopier tout leur contenu.
  • Gardez les classes et la structure conformes au contrat du tableau de bord.

Dans la prochaine leçon, vous remplacerez l’entrée statique par une liste de dépenses typée et affichée à partir de données.

Envie d'aller plus loin ?

Les cours Enovatic sont gratuits et le resteront. Si vous cherchez un accompagnement encadré, nos formations professionnelles sont animées par les mêmes formateurs.