Planificateur « Suggérer » : 4 stratégies (smart / meilleurs d'abord / équilibré / juste ce qu'il faut), compétences+niveaux par tech (édition inline), niveau requis par compétence + par job, carte des tournées (1 couleur/tech, domicile→arrêts, sélecteur de jour), fenêtre de dispatch auj.+demain (dates sélectionnées), règle week-end + placeholder « en attente du quart », clustering + lasso + filtre-date sur la carte, accès rapide « À assigner » (badge). Boîte : liste /conversations allégée (45 Mo → ~1 Mo, 17×) + messages chargés à l'ouverture. Rapports : cache SWR sur revenue-explorer (22×). Session : keep-alive + timeout fetch global + authFetch durci → fin des rechargements manuels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.3 KiB
6.3 KiB
Cohérence de l'interface Communications — cap « feeling Missive »
Statut : PLAN (2026-06-18). Refactor d'AFFICHAGE + threading — ne change pas les données stockées. Objectif : que notre Boîte soit aussi cohérente et intuitive que Missive.
Diagnostic : une seule cause méta
Presque toutes les incohérences viennent du même défaut : le même concept est calculé à plusieurs endroits avec des logiques différentes (liste vs détail vs ingestion). Missive est cohérent parce que chaque concept a une source de vérité. → Principe directeur : un resolver unique par concept, importé partout.
Inventaire (causes racines, vérifiées dans le code)
1. « à moi » au lieu de « à support » (attribution du destinataire)
- Rendu :
ConversationPanel.vue:126(entête,convTo) +:414(msgTo→msgRecipientList, :1163-1186). - Cause :
meAddr = msg.toEmail || convTopuisif addr === meAddr → 'moi'. « moi » = la boîte qui a REÇU le courriel, PAS l'utilisateur connecté (meEmail, :1395, jamais consulté ici). Aucune table adresse-de-groupe → nom d'équipe. - Correctif : table
support@→Support,facturation@→Facturation… (depuisSENDER_IDENTITIES/queueLabels) ; « moi » seulement si l'adresse ==meEmail.
2. Nom d'expéditeur vide / incohérent
- Rendu : liste
MessageList.vue:104(finit par « Inconnu », OK) ; détailmsgWho(:1577-1582) + avatar (:1569-1570). - Cause :
fromName=''si le From n'a pas de display-name → retombe suractiveDiscussion.customerName(peut être un AUTRE expéditeur dans un fil multi-personnes) ; avatarstaffInitials('')→ vide. EtshortAgent(local part minuscule, ex. « gilles.drolet ») vsnoteAuthorName(Capitalisé) = même personne affichée de 2 façons. - Correctif : un resolver
identity()partagé, jamais vide, agents toujours capitalisés (réutilisernoteAuthorName).
3. Sur-regroupement (fils fusionnés à tort)
- Lieux : ingestion
conversation.js:722-734+ re-bucketing listelistConversations(:435-461). - Causes : (a) courriel sans sujet →
findConversationByEmail:681fusionne par email seul (garde sautée si un sujet est vide) ; (b) clé:active(:444) fusionne TOUS les fils actifs d'un mêmebaseKeysans borne de date ; (c) SMS/téléphone fusionne par numéro pour toujours (sans sujet ni fenêtre) ; (d)thread_idGmail pris tel quel (réutilisation de sujet → vieux fil) ; (e) In-Reply-To/References stockés mais PAS utilisés pour regrouper. - Correctif : exiger un signal de fil positif (thread_id OU match References) avant de fusionner ; sujet vide/auto = non-concordant ; fenêtre de récence sur la clé
:active.
4. Canal incohérent
conv.channel(conversation) vsmsg.via(message) — vocabulaires différents ; le courriel n'a AUCUN badge (MessageList.vue:102le masque) ; pas de badge par message.- Correctif :
msg.via= canal par message (vocabulaire normalisé) ; badge cohérent sur CHAQUE ligne/message, courriel inclus.
5. FILE + identité éclatées
- FILE : entête
activeConv.queue(chargé async) vs listed.queue→ décalage bref. Identité : titre/chip/message utilisent 3 champs différents (customerName/coordAccountName/email) → 3 noms pour le même fil. - Correctif : FILE = une source (réconcilier une fois
activeConvarrivé) ; « qui » = un seul computed.
6. Finitions incohérentes
- Deux
formatTime/formatDate: localConversationPanel:2070-2087(≠ canoniqueuseFormatters) → liste en « DD/MM » (relTime) vs entête de message « mois jour HH:MM » ; le local n'a pas le gardeisNaN→ « Invalid Date ». - Tri : message
emailDate||tsvs liste/lastTs=ts→ ordre divergent sur fils re-synchronisés. - Non-lu / « en train d'écrire » pris du
latestseul, pas du fil fusionné entier. - Couleurs d'avatar : clés de hash différentes liste vs détail → même personne, couleurs différentes.
Les resolvers (colonne vertébrale du correctif)
Un module partagé (ex. composables/useConversationDisplay.js) exporte un resolver par concept,
utilisé par la liste ET le détail (et côté hub, l'ingestion) :
| Concept | Resolver | Règle |
|---|---|---|
| Qui | identity(p) |
nom → local part → tél → « Inconnu » ; agents capitalisés ; avatar/couleur/initiales dérivés du MÊME id |
| Destinataire | recipientLabel(addr) |
adresse de groupe → nom d'équipe ; « moi » seulement si == utilisateur ; sinon nom/adresse |
| Canal | channelOf(msg) |
depuis via normalisé → badge |
| Fil | threadKey(msg) |
signal positif requis ; sujet vide = non-match ; fenêtre de récence |
| File | fileOf(conv) |
une seule source |
| Temps | useFormatters partout |
supprimer les copies locales |
Patterns Missive à adopter (extraits du tour vidéo)
- Ligne uniforme : avatar + badge de canal + nom + heure + sujet + extrait + chips.
- 1 fil = 1 sujet (titre visible → toute fausse fusion saute aux yeux).
- Fil unifié : messages client (blocs pleine largeur) + notes internes (bulles) + tâches inline, avec 2 composeurs (répondre au client / note d'équipe).
- Assign / Snooze / Close en actions de barre supérieure (dropdowns riches).
- Toasts Undo sur actions réversibles.
Plan par phases
| Phase | Contenu | Risque |
|---|---|---|
| P1 — Cohérence | resolvers identity + recipientLabel (« à support ») + temps unifié (tuer les formatTime locaux) + fileOf une source |
faible (affichage) |
| P2 — Threading strict | signal de fil positif, sujet vide = non-match, fenêtre de récence, In-Reply-To/References utilisés | moyen (logique de fil → tester sur fils réels) |
| P3 — Canal | channelOf + badge partout (Messenger/courriel/SMS) |
faible |
| P4 — UX Missive | fil unifié + 2 composeurs + assign/snooze + undo | moyen (UI) |
Garde-fous
- Refactor d'affichage d'abord — ne touche pas aux données stockées.
- Threading (P2) : préférer sur-séparer que sur-fusionner — un faux split est bénin ; une fuite de conversation entre deux clients ne l'est pas.
- Tester P2 sur de vrais fils : multi-expéditeur, SMS long, adresse de groupe, sujet réutilisé.
- Chaque resolver = testable isolément (entrées → libellé attendu) avant branchement UI.