Aller au contenu
Design system

Composants · feedback

Retours & surimpressions

Toast après un succès ; confirmation seulement pour l’irréversible ; motif obligatoire au refus et à l’annulation ; dialogue explicite en cas de conflit de version. Les états vides, d’erreur et sans permission disent toujours quoi faire ensuite.

  • Modal
  • ConfirmDialog
  • ReasonDialog
  • ConflictDialog
  • Drawer
  • Popover
  • Tooltip
  • DropdownMenu
  • useToast
  • Toaster
  • Alert
  • Banner
  • OfflineBanner
  • EmptyState
  • Skeleton
  • ErrorState
  • PermissionDenied
  • ProgressBar
  • StatusDot

Fenêtres de dialogue

Modal

Quand l’utiliser : Tâche courte qui demande de l’attention sans quitter la page : saisie rapide, aperçu. Pour une tâche longue, préférer une page ; pour consulter en parallèle, un Drawer.

Échap ou clic extérieur ferment (sauf preventClose) ; le focus est piégé puis rendu au déclencheur. Tailles : sm, md, lg, xl, full.
Afficher le code
<Modal
  trigger={<Button>Modifier l’adresse de livraison</Button>}
  title="Adresse de livraison" description="CMD-2026-00421 · Grossiste Démo Kossodo"
  footer={<><ModalClose asChild><Button>Annuler</Button></ModalClose><Button variant="primary">Enregistrer</Button></>}
>
  <TextField label="Adresse" defaultValue="Zone industrielle de Kossodo, lot 14" />
</Modal>

ConfirmDialog

Quand l’utiliser : Uniquement avant une action irréversible ou lourde de conséquences (émettre une facture, valider une écriture, clôturer). Le contenu récapitule ce qui va se passer. Ne pas confirmer les actions réversibles.

Si onConfirm renvoie une promesse, le bouton passe en chargement et la fenêtre ne peut plus être fermée ; en cas d’échec elle reste ouverte avec un message. Le second exemple échoue une fois sur deux.
Afficher le code
<ConfirmDialog
  open={open} onOpenChange={setOpen}
  title="Émettre la facture ?" description="Une facture émise ne peut plus être modifiée, seulement annulée par avoir."
  confirmLabel="Émettre la facture"
  onConfirm={async () => { await api.issueInvoice(id); toast({ tone: 'success', title: 'Facture émise' }); }}
>
  <OrderSummary />
</ConfirmDialog>

ReasonDialog — refuser une commande

Quand l’utiliser : Refus, rejet, annulation : le motif est obligatoire et tracé dans l’historique. Motifs prédéfinis (boutons radio) plus précision écrite ; « Autre » exige un texte.

Afficher le code
<ReasonDialog
  open={open} onOpenChange={setOpen} tone="danger"
  title="Refuser la commande CMD-2026-00419"
  description="Le commercial sera notifié avec votre motif."
  reasons={[
    { value: 'credit', label: 'Dépassement du plafond de crédit' },
    { value: 'stock', label: 'Stock insuffisant' },
    { value: 'other', label: 'Autre', requiresDetails: true },
  ]}
  confirmLabel="Refuser la commande"
  onConfirm={async (reason) => api.reject(id, reason)}
/>

ConflictDialog

Quand l’utiliser : Réponse 409 à l’enregistrement : quelqu’un d’autre a modifié le document entre-temps. On propose de recharger (perte des modifications locales) ou de comparer.

Afficher le code
<ConflictDialog open={open} onOpenChange={setOpen}
  user="Paul Démo" time="2026-10-13T10:24:00Z" timeZone="Africa/Ouagadougou"
  onReload={reload} onCompare={openDiff} />

Tiroirs

Drawer — historique du document

Quand l’utiliser : Consulter ou éditer un contenu secondaire sans perdre le contexte : historique, détails d’une ligne, filtres sur mobile. modal={false} laisse la page utilisable en parallèle.

Afficher le code
<Drawer
  trigger={<Button leadingIcon={<History />}>Historique</Button>}
  title="Historique" description="CMD-2026-00421" size="md"
  footer={<DrawerClose asChild><Button>Fermer</Button></DrawerClose>}
>
  <Timeline events={events} />
</Drawer>

Notifications et messages

Toasts (useToast)

Quand l’utiliser : Confirmer le succès d’une action (principe AGB : toast après succès), signaler un problème non bloquant. Une action rapide possible (Annuler, Voir). Ne pas y mettre d’information à conserver.

Durées par défaut : 5 s, 7 s pour un avertissement, 10 s pour une erreur. F8 donne le focus à la zone de notifications. 3 toasts au maximum.
Afficher le code
const { toast } = useToast();
toast({ tone: 'success', title: 'Commande confirmée', description: 'CMD-2026-00421', action: { label: 'Voir', onClick: open } });
toast({ tone: 'error', title: 'Envoi impossible', description: 'Le serveur de messagerie ne répond pas.' });

Alert

Quand l’utiliser : Message contextuel dans le contenu d’une page ou d’un formulaire : blocage de crédit, avertissement de stock, information réglementaire. Le titre dit le problème, la description comment avancer.

Nouvelle grille tarifaire

La grille « Grossistes 2026 » s’applique aux commandes créées à partir du 1er octobre.

Lot RB25-261013-01 libéré

Contrôle qualité conforme : humidité 13,2 %, brisures 4 %.

Astuce

Appuyez sur ⌘K pour ouvrir n’importe quel document par son numéro.
Afficher le code
<Alert tone="warning" title="Plafond de crédit dépassé"
  description="La commande dépasse de 1 625 000 F CFA le crédit disponible : elle sera soumise à validation."
  actions={<Button size="sm">Demander une validation</Button>} />

Banner et OfflineBanner

Quand l’utiliser : Bandeau pleine largeur pour un état global de la page ou de l’application : période clôturée, accès support actif, mode hors ligne. sticky le garde visible au défilement.

Accès support actifL’équipe SYNAPSE consulte votre espace jusqu’à 18:00 (ticket SUP-2291).
Maintenance planifiéeDimanche 18 octobre de 22:00 à 23:00 (heure de Ouagadougou).

OfflineBanner ne s’affiche que hors connexion : coupez le réseau dans les outils du navigateur pour le voir ci-dessous.

Afficher le code
<Banner tone="warning" title="Période clôturée" description="Septembre 2026 est clôturée : le document est en lecture seule." />
<Banner tone="special" title="Accès support actif" action={<Button size="sm">Terminer l’accès</Button>} />
<OfflineBanner />   // s’affiche automatiquement quand navigator.onLine passe à false

États vides, chargement, erreurs, permissions

EmptyState — quatre variantes

Quand l’utiliser : default : rien à afficher ; first-use : module jamais utilisé (inviter à créer) ; no-results : filtres trop restrictifs (proposer de réinitialiser) ; no-permission : contenu masqué par les droits. size sm dans un tableau ou un panneau.

Aucune livraison prévue aujourd’hui

Les bons de livraison du jour apparaîtront ici.

Aucune commande pour l’instant

Créez votre première commande client ou importez votre carnet existant.

Aucune commande ne correspond

Statut « Payé » et client « Comptoir Démo du Centre » : aucun résultat.

Marges masquées

Votre rôle ne permet pas de voir les marges par commande.

Afficher le code
<EmptyState variant="first-use" title="Aucune commande pour l’instant" primaryAction={<Button variant="primary">Créer une commande</Button>} />
<EmptyState variant="no-results" onResetFilters={reset} />

Skeleton

Quand l’utiliser : Pendant le premier chargement d’une zone dont on connaît la forme (tableau, carte, texte) : évite les sauts de mise en page. Un Spinner pour une action ponctuelle.

Chargement…
Chargement…
Chargement…
Afficher le code
<SkeletonTable rows={4} columns={5} />
<SkeletonCard avatar lines={2} />
<SkeletonText lines={3} />
<Skeleton shape="circle" className="size-10" />  <Skeleton shape="rect" className="h-24 w-full" />

ErrorState et PermissionDenied

Quand l’utiliser : ErrorState quand une zone ne peut pas se charger : message compréhensible, bouton Réessayer, identifiant de trace à copier pour le support. PermissionDenied quand l’utilisateur ouvre un lien sans droit : dire quelle permission manque et proposer de demander l’accès.

Vous n’avez pas accès à cette page

Votre rôle ne comporte pas la permission nécessaire. Demandez l’accès à un administrateur de votre société.

Permission requise : Valider les commandes (VEN.commande.valider)

Afficher le code
<ErrorState message="Le serveur n’a pas répondu à temps." traceId="7f3a9c2e-4b1d" onRetry={retry} retrying={retrying} />
<PermissionDenied permission="Valider les commandes (VEN.commande.valider)"
  onRequestAccess={request} requestState={state} />

ProgressBar et StatusDot

Quand l’utiliser : ProgressBar : avancement mesurable (préparation d’une commande, import, ordre de fabrication) ; indéterminée si la durée est inconnue. StatusDot : état compact dans une liste (machine, synchronisation), toujours accompagné d’un libellé.

Préparation CMD-2026-00421620 / 950 sacs
OF-2026-0142 — usinage paddy TS278 %
Capacité silo n° 392 %
Budget maintenance consommé104 %
Import du relevé bancaire
  • Décortiqueuse 1 — en marche
  • Blanchisseuse 2 — maintenance préventive à 14:00
  • Trieur optique — arrêt (bourrage)
  • Balance pont-bascule — synchronisation
  • Ensacheuse 3 — hors service
  • Terminal atelier — connecté
  • Contrôle qualité en cours libellé masqué (lu par les lecteurs d’écran)
Afficher le code
<ProgressBar label="Préparation CMD-2026-00421" value={620} max={950} showValue valueText="620 / 950 sacs" />
<ProgressBar label="Import du relevé" />            // indéterminée
<StatusDot tone="success" label="Décortiqueuse 1 — en marche" pulse />