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
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(elibs/api.tsti rispedisce suconfig.auth.loginUrl); returnUrlmancante →400;- nessun
customer_idnella rigaprofiles→400con “You don't have a billing account yet”. È il caso di chi non ha mai comprato: ilcustomer_idviene 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
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
"/"inhandleSignOut. - Colori: le voci usano gli inline style con
var(--ink),var(--cream-2)evar(--brick)(quest'ultimo per “Esci”). - Posizione del pannello:
Popover.Panelèabsolute left-0conmax-w-[16rem]. Se il bottone sta a destra nell'header, passa aright-0.