Tre cose che mancavano all'inbox admin, tutte senza migration.
1. Dall'inbox era impossibile aprire una conversazione: getConversations()
costruiva la lista dai commenti, quindi un cliente compariva solo dopo aver
scritto lui. Ora la lista parte da `clients` e i commenti la arricchiscono.
Ordine: prima chi ha scritto (per recenza), in coda i clienti muti in
alfabetico, cosi' l'inbox resta un inbox.
2. Menzioni «@Nome», rinviate dalla chat a canali. Modello senza schema: il tag
si riconosce confrontando il testo con i nomi noti del cliente (nome intero,
nome di battesimo, brand), insensibile ad accenti e maiuscole. Il body resta
quello che l'admin ha scritto, quindi la menzione sopravvive alla modifica di
un messaggio e resta leggibile ovunque finisca, mail compresa.
Confini di parola su ENTRAMBI i lati: senza quello a sinistra,
«scrivimi a mario@teckell.it» conteneva un tag «@Teckell».
3. Un tag manda una mail. E' l'unico messaggio che esce dal portale: per il
resto il cliente entra quando gli pare, ma il tag e' la dichiarazione che
quel messaggio non puo' aspettare il prossimo accesso. Nessuno scheduler --
parte dalla stessa azione che scrive il messaggio, fuori transazione: se
Resend e' giu' il messaggio in chat resta comunque scritto.
Destinatari: whitelist OTP + email della scheda, deduplicati. Con zero
indirizzi il compositore lo dice PRIMA, invece di lasciar credere che sia
partita una mail che non partira'.
Il pulsante della mail punta a `?chat=<canale>`, validato lato server e passato
come prop: leggerlo nel browser vorrebbe dire renderizzare il pannello chiuso e
riaprirlo dopo l'idratazione.
La casella di risposta diventa controllata (ReplyComposer): il suggerimento del
tag deve leggere il testo mentre lo scrivi e reinserirlo al caret giusto.
Invio manda, Shift+Invio va a capo -- come nel pannello del cliente.
Verificato: `npm run build` e `eslint` puliti, parser delle menzioni provato su
9 casi. NON verificato a schermo ne' contro il DB.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Il widget dei messaggi esisteva gia', ma era il terzo riquadro della colonna
stretta: sotto la piega, cioe' invisibile. Un messaggio non letto e' la cosa
piu' urgente della giornata e ora sta in testa alla dashboard, a piena
larghezza, due colonne.
Due dati erano gia' in ConversationSummary e non venivano mostrati: da quanto
aspetta il messaggio e su cosa e' stato scritto (fase, task, deliverable o
generale). Sono esattamente i due che dicono con che fretta rispondere. Il
tempo relativo si ferma alla settimana e poi passa alla data: oltre, "23 giorni
fa" non aiuta piu' nessuno.
La fascia sparisce del tutto quando non c'e' niente da leggere, invece di
lasciare a video un riquadro vuoto che si impara a saltare — e con lui si
imparerebbe a saltare anche quello pieno.
relativeTime va in src/lib/dates.ts perche' serve anche altrove. FollowUpWidget
ha ancora la sua copia locale con il prefisso "Contattato": la si unifica
quando la si tocca, non oggi.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>