ZeroToShip

Docs/Componenti

// componente

Button Account

Il menu account delle pagine private: un popover con due azioni, Fatturazione (apre il Customer Portal di Stripe) e Esci (logout Supabase + ritorno alla home). Nel boilerplate sta nell'header di app/dashboard/layout.tsx. Client component, costruito con Popover e Transition di Headless UI.

File

components/ButtonAccount.tsx

Uso base

app/dashboard/layout.tsx
tsx
import ButtonAccount from "@/components/ButtonAccount";

export default function LayoutPrivate({ children }: { children: ReactNode }) {
  return (
    <>
      <header>
        <ButtonAccount />
      </header>
      {children}
    </>
  );
}

Nessuna prop: legge tutto da solo la sessione Supabase con supabase.auth.getUser(). Il trigger mostra avatar o iniziale, poi il nome da user_metadata.name, altrimenti la parte di email prima della @, altrimenti “Account”.

Fatturazione: il giro completo

Il click su “Fatturazione” manda POST /api/stripe/create-portal con { returnUrl: window.location.href } — cioè l'utente torna esattamente dov'era quando chiude il portale. Durante l'attesa il chevron diventa uno spinner.

app/api/stripe/create-portal/route.ts fa tre controlli prima di chiamare Stripe:

  • utente non loggato → 401 (e libs/api.ts ti rispedisce su config.auth.loginUrl);
  • returnUrl mancante → 400;
  • nessun customer_id nella riga profiles → 400 con “You don't have a billing account yet”. È il caso di chi non ha mai comprato: il customer_id viene scritto dal webhook Stripe al primo acquisto.

Se passa tutto, createCustomerPortal di libs/stripe.ts restituisce l'URL e il componente fa il redirect.

Attenzione: il Customer Portal va attivato a mano una volta nel dashboard Stripe (Settings → Billing → Customer portal), altrimenti la chiamata fallisce con un errore di Stripe.

Logout

components/ButtonAccount.tsx
ts
const handleSignOut = async () => {
  await supabase.auth.signOut();
  window.location.href = "/";
};

È un window.location.href e non un router.push di proposito: forza un reload pieno, così nessun dato dell'utente precedente resta in cache lato client.

Personalizzare

  • Aggiungere voci: il pannello è un <div className="space-y-0.5"> con due <button>. Copia uno dei due per aggiungere “Impostazioni” o “Supporto”.
  • Destinazione dopo il logout: cambia "/" in handleSignOut.
  • Colori: le voci usano gli inline style con var(--ink), var(--cream-2) e var(--brick) (quest'ultimo per “Esci”).
  • Posizione del pannello: Popover.Panel è absolute left-0 con max-w-[16rem]. Se il bottone sta a destra nell'header, passa a right-0.