41530b556a
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>
45 lines
1.1 KiB
TypeScript
45 lines
1.1 KiB
TypeScript
import { Fragment } from "react";
|
|
import { cn } from "@/lib/utils";
|
|
import { splitMentions } from "@/lib/mentions";
|
|
|
|
/**
|
|
* Corpo di un messaggio di chat con le menzioni evidenziate.
|
|
*
|
|
* Sostituisce `{m.body}` dentro il paragrafo: mantiene `whitespace-pre-wrap` sul
|
|
* genitore, quindi gli a capo continuano a funzionare senza dover spezzare il
|
|
* testo in righe qui dentro.
|
|
*/
|
|
export function MentionText({
|
|
body,
|
|
candidates,
|
|
mentionClassName,
|
|
}: {
|
|
body: string;
|
|
candidates: string[];
|
|
/** Il contrasto cambia col fondo della bolla: lo decide chi la disegna. */
|
|
mentionClassName?: string;
|
|
}) {
|
|
const parts = splitMentions(body, candidates);
|
|
|
|
return (
|
|
<>
|
|
{parts.map((part, i) =>
|
|
part.type === "mention" ? (
|
|
<span
|
|
key={i}
|
|
className={cn(
|
|
"rounded px-1 font-semibold",
|
|
mentionClassName ??
|
|
"bg-primary/10 text-primary dark:bg-primary/20"
|
|
)}
|
|
>
|
|
{part.value}
|
|
</span>
|
|
) : (
|
|
<Fragment key={i}>{part.value}</Fragment>
|
|
)
|
|
)}
|
|
</>
|
|
);
|
|
}
|