ZeroToShip

Docs/Feature

// feature

Customer support

Il support è cablato con due strade, e il repo sceglie da solo quale usare: chat Crisp se hai messo un website ID in config, altrimenti apre il client email dell'utente sul tuo indirizzo di supporto. Un solo componente, ButtonSupport, copre entrambi i casi.

Com'è messo adesso

crisp-sdk-web è già tra le dipendenze del package.json, ma in config.ts il campo crisp.id è una stringa vuota. Tradotto: la chat è spenta e in questo momento il support gira sul fallback email, che punta a resend.supportEmail.

config.ts
ts
crisp: {
  // Vuoto = chat disattivata. Incolla qui il Website ID per accenderla.
  id: "",
  // Il widget resta visibile SOLO su queste route
  onlyShowOnRoutes: ["/"],
},
resend: {
  // Usato dal fallback mailto quando crisp.id è vuoto
  supportEmail: "tuaemail@example.com",
},

ButtonSupport

Il componente è in components/ButtonSupport.tsx. Al click controlla il config nell'ordine: prima Crisp, poi email.

components/ButtonSupport.tsx
tsx
const handleClick = () => {
  if (config.crisp?.id) {
    Crisp.chat.show();
    Crisp.chat.open();
  } else if (config.resend?.supportEmail) {
    window.open(
      `mailto:${config.resend.supportEmail}?subject=Serve aiuto con ${config.appName}`,
      "_blank"
    );
  }
};

Se svuoti entrambi i campi il bottone diventa decorativo: click, e non succede niente. Tienine sempre almeno uno pieno.

Nel repo il bottone è già usato in due posti: app/error.tsx e app/not-found.tsx. Cioè esattamente dove l'utente è bloccato e ha bisogno di parlarti. Aggiungilo dove vuoi con un import:

components/Footer.tsx
tsx
import ButtonSupport from "@/components/ButtonSupport";

<ButtonSupport />

Accendere Crisp

  1. Crea un account su crisp.chat e un website.
  2. Settings → Website Settings → Setup instructions: copia il Website ID (un UUID).
  3. Incollalo in config.ts sotto crisp.id. Fine — non c'è nessuno script da mettere in layout.tsx, ci pensa il boot del client layout.

Il website ID non è un segreto (finisce comunque nel bundle browser), quindi sta bene in config e non in .env.

Il boot in LayoutClient

components/LayoutClient.tsx contiene un componente CrispChat che non renderizza niente e fa tre cose: avvia Crisp, applica la regola di visibilità, e identifica l'utente loggato.

components/LayoutClient.tsx
tsx
useEffect(() => {
  if (config?.crisp?.id) {
    Crisp.configure(config.crisp.id);

    if (
      config.crisp.onlyShowOnRoutes &&
      !config.crisp.onlyShowOnRoutes?.includes(pathname)
    ) {
      Crisp.chat.hide();
      Crisp.chat.onChatClosed(() => {
        Crisp.chat.hide();
      });
    }
  }
}, [pathname]);

Tutto è avvolto in if (config?.crisp?.id): con l'id vuoto lo script Crisp non parte proprio, zero JavaScript di terze parti caricato. È il motivo per cui puoi lasciare la dipendenza installata senza pagare nulla in performance.

Come funziona onlyShowOnRoutes

È un array di path dove la bolla di chat resta visibile. Su ogni altra route il codice chiama Crisp.chat.hide(), e con onChatClosed la rinasconde appena l'utente chiude la finestra. Il default [“/”] significa: bolla solo in home, sparita ovunque altro.

  • onlyShowOnRoutes: [“/”] — bolla solo sulla landing. Nelle altre pagine il support si apre con <ButtonSupport />, che chiama Crisp.chat.show() e open().
  • onlyShowOnRoutes: [“/”, “/pricing”] — bolla anche sulla pagina prezzi, dove le domande pre-acquisto valgono soldi veri.
  • Togli del tutto la chiave onlyShowOnRoutes dal config e la bolla resta visibile su tutte le route.

Il confronto è una includes() esatta sul pathname: /blog nell'array non copre /blog/mio-post. Se ti serve il match per prefisso, sostituisci l'includes con uno some((r) => pathname.startsWith(r)).

Riconoscere chi ti scrive

Un terzo effect legge l'utente Supabase e passa il suo id a Crisp, così nella tua inbox vedi chi ha aperto la conversazione invece di un visitatore anonimo:

components/LayoutClient.tsx
tsx
useEffect(() => {
  if (data?.user && config?.crisp?.id) {
    Crisp.session.setData({ userId: data.user?.id });
  }
}, [data]);

Puoi aggiungere altri campi allo stesso oggetto (piano acquistato, data di iscrizione) e ritrovarteli nel pannello Crisp accanto alla chat.

Se preferisci solo email

Lascia crisp.id vuoto e metti un indirizzo vero in resend.supportEmail. Il mailto: apre il client dell'utente con oggetto già compilato — meno immediato di una chat, ma zero costi e zero script di terze parti. Puoi anche rimuovere crisp-sdk-web dalle dipendenze e cancellare il componente CrispChat da LayoutClient.