Opérations longues et journaux
Le problème
Un déploiement, un import, une synchronisation, un envoi en masse : une opération qui dure, qui échoue parfois et qu'on relance. L'écran qui la suit répond toujours aux mêmes questions :
- où en est-elle ? En file d'attente, en cours, terminée, en échec ;
- qu'a-t-elle fait ? Le journal ligne à ligne, les essais successifs ;
- qu'a répondu le serveur ? Une charge utile, une erreur détaillée ;
- sur quel service ? Le logo du fournisseur concerné.
Quatre composants y répondent, et se composent :
| Composant | Répond à |
|---|---|
JobProgress | le statut, les étapes, la progression, les essais, la durée, l'erreur, la relance |
LogStream | le journal : niveaux, heure, groupes par essai, filtres, recherche, copie, suivi de la fin |
JsonView | une réponse ou une configuration JSON, en lecture seule, repliable et copiable |
BrandIcon | le logo d'un service tiers, sans dépendance à un paquet d'icônes |
Comment ils s'assemblent
JobProgress reçoit le journal par sa prop logs : il le place sous le statut, sans en connaître la forme. Une ligne de LogStream accepte un ReactNode : un JsonView replié y tient quand le message est une charge utile.
<JobProgress
title={<><BrandIcon icon={siVercel} decorative /> Déploiement</>}
status={job.status}
steps={job.steps}
currentStep={job.currentStep}
attempts={{ current: job.attempt, max: 3 }}
startedAt={job.startedAt}
error={job.error}
onRetry={() => relancer(job.id)}
logs={<LogStream lines={job.lines} streaming={job.status === "running"} height={320} />}
/>Le temps réel
Ces composants ne s'abonnent à rien. Les données arrivent par le cache de requêtes de l'application, que createRealtimeBinding met à jour à chaque événement du serveur. Voir Module API. Le composant se redessine comme pour n'importe quelle donnée, et LogStream suit la fin du journal tant que la personne n'a pas remonté pour lire.
Les choix
LogStreamlimite ce qu'il garde (maxLines) : un journal qui défile pendant une heure ne doit pas faire grossir la page sans fin.JsonViewne rend pas une branche repliée : un document de plusieurs milliers de nœuds s'affiche sans attendre. Un texte invalide s'affiche tel quel sous une alerte, car une réponse tronquée reste utile à lire.BrandIconne livre aucun logo : le projet importe l'objet desimple-iconsqu'il veut et reste maître de sa version. Une marque quasi noire ou quasi blanche prend la couleur du texte là où elle serait illisible.- Les textes viennent de la locale : statuts, niveaux (
INFO,ERREUR), « Suivre la fin », « {count} clés ». Voir Langues, libellés et réglages.
Chacun expose les props de ses sous-composants (statusBadgeProps, progressProps, copyButtonProps, alertProps…), transmises telles quelles.