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>
30 lines
1.3 KiB
Bash
30 lines
1.3 KiB
Bash
# Database — Postgres su Coolify
|
|
DATABASE_URL=postgresql://user:password@host:5432/database
|
|
|
|
# NextAuth
|
|
NEXTAUTH_URL=https://hub.iamcavalli.net
|
|
NEXTAUTH_SECRET=generate-with-openssl-rand-base64-32
|
|
ADMIN_EMAIL=admin@example.com
|
|
ADMIN_PASSWORD=use-a-strong-password-min-20-chars
|
|
|
|
# Internal API secret — shared between proxy.ts and /api/internal/* routes
|
|
# Generate with: openssl rand -base64 32
|
|
INTERNAL_SECRET=generate-with-openssl-rand-base64-32
|
|
|
|
# Resend — invio del codice OTP per l'accesso al portale cliente
|
|
# RESEND_FROM deve usare un dominio verificato su Resend
|
|
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxx
|
|
RESEND_FROM=Nome Mittente <no-reply@iamcavalli.net>
|
|
|
|
# Base pubblica dei link nelle email (pulsante "Apri la conversazione" della
|
|
# notifica di tag). Opzionale: se manca si usa NEXTAUTH_URL, che in ogni
|
|
# ambiente e' gia' l'origine giusta. Serve solo se le due devono divergere.
|
|
# APP_BASE_URL=https://hub.iamcavalli.net
|
|
|
|
# Ingresso lead da fuori (form del sito, bridge Zapier/Make) su
|
|
# POST /api/webhooks/lead, header x-webhook-secret.
|
|
# A differenza di INTERNAL_SECRET questa route e' esposta a internet: se la
|
|
# variabile manca, la route risponde 403 invece di lasciar passare.
|
|
# Generate with: openssl rand -base64 32
|
|
LEAD_WEBHOOK_SECRET=generate-with-openssl-rand-base64-32
|