feat(conversazioni): scrivere per primo, taggare il cliente, notificarlo via mail
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>
This commit is contained in:
@@ -98,15 +98,31 @@ export async function generateMetadata({
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Il canale chiesto con `?chat=<key>`, se appartiene a QUESTO progetto.
|
||||
*
|
||||
* La validazione non e' cosmetica: senza, un id qualsiasi nella query aprirebbe
|
||||
* la chat su un canale vuoto — e con piu' progetti a tab, la fase di un progetto
|
||||
* spalancherebbe il pannello anche negli altri.
|
||||
*/
|
||||
function resolveChatChannel(
|
||||
requested: string | undefined,
|
||||
view: ProjectView
|
||||
): string | null {
|
||||
if (!requested) return null;
|
||||
if (requested === view.project.client_id) return requested;
|
||||
return view.phases.some((p) => p.id === requested) ? requested : null;
|
||||
}
|
||||
|
||||
export default async function ClientPage({
|
||||
params,
|
||||
searchParams,
|
||||
}: {
|
||||
params: Promise<{ token: string }>;
|
||||
searchParams: Promise<{ preview?: string }>;
|
||||
searchParams: Promise<{ preview?: string; chat?: string }>;
|
||||
}) {
|
||||
const { token } = await params;
|
||||
const { preview: previewParam } = await searchParams;
|
||||
const { preview: previewParam, chat: chatParam } = await searchParams;
|
||||
|
||||
// ⚠️ Il gate va PRIMA di ogni query sui dati del progetto: se si interroga il
|
||||
// DB e poi si decide di mostrare il form, i dati sono già nel payload RSC
|
||||
@@ -161,6 +177,7 @@ export default async function ClientPage({
|
||||
adminAvatarUrl: admin.avatarUrl,
|
||||
}}
|
||||
preview={preview}
|
||||
initialChatChannel={resolveChatChannel(chatParam, view)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
@@ -214,6 +231,7 @@ export default async function ClientPage({
|
||||
}}
|
||||
embedded
|
||||
preview={preview}
|
||||
initialChatChannel={resolveChatChannel(chatParam, view)}
|
||||
/>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">Progetto non disponibile.</p>
|
||||
|
||||
Reference in New Issue
Block a user