"use client" import { dmSans125ClassName } from "@/lib/fonts" import { cn } from "@lib/utils" import { useAuth } from "@lib/auth-context" import { useAccountMemberships, useDeleteUserAccount, } from "@/hooks/use-account-settings" import { Avatar, AvatarFallback, AvatarImage } from "@ui/components/avatar" import { useTokenUsage } from "@/hooks/use-token-usage" import { Dialog, DialogContent, DialogTrigger, DialogClose, } from "@ui/components/dialog" import { authClient } from "@lib/auth" import { Popover, PopoverContent, PopoverTrigger } from "@ui/components/popover" import { useCustomer } from "autumn-js/react" import { Check, X, Trash2, LoaderIcon, Settings, ChevronDown, Building2, } from "lucide-react" import { useMemo, useState } from "react" import { toast } from "sonner" function SectionTitle({ children }: { children: React.ReactNode }) { return (

{children}

) } function SettingsCard({ children }: { children: React.ReactNode }) { return (
{children}
) } function PlanComparisonCard({ name, price, period, description, credits, features, highlight, }: { name: string price: string period: string description: string credits: string features: string[] highlight: boolean }) { return (

{name}

{highlight && ( RECOMMENDED )}
{price} {period && ( {period} )}

{description}

{credits}

of usage included

) } function formatOrgRole(role: string): string { const r = role.toLowerCase() if (r === "owner") return "Owner" if (r === "admin") return "Admin" if (r === "member") return "Member" return role ? role.charAt(0).toUpperCase() + role.slice(1).toLowerCase() : "Member" } export default function Account() { const { user, org, setActiveOrg, clearActiveOrg } = useAuth() const autumn = useCustomer() const [isUpgrading, setIsUpgrading] = useState(false) const [emailConfirm, setEmailConfirm] = useState("") const [notifyWhenDeleted, setNotifyWhenDeleted] = useState(false) const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false) const [isClosingAccount, setIsClosingAccount] = useState(false) const [switchingOrgId, setSwitchingOrgId] = useState(null) const { data: allOrgs } = authClient.useListOrganizations() const { data: memberships, isPending: membershipsPending } = useAccountMemberships() const sortedMemberships = useMemo(() => { if (!memberships?.length) return [] return [...memberships].sort((a, b) => a.name.localeCompare(b.name)) }, [memberships]) const ownedOrgs = useMemo( () => memberships?.filter((m) => m.role === "owner") ?? [], [memberships], ) const hasOwnedOrgWithTeammates = useMemo( () => ownedOrgs.some((m) => m.memberCount > 1), [ownedOrgs], ) const showMembershipsOverview = !membershipsPending && (sortedMemberships.length > 1 || hasOwnedOrgWithTeammates) const deleteUserAccount = useDeleteUserAccount() const emailMatches = user?.email ? emailConfirm.trim().toLowerCase() === user.email.trim().toLowerCase() : false const handleOrgSwitch = async (orgSlug: string, orgId: string) => { if (orgId === org?.id) return setSwitchingOrgId(orgId) try { await setActiveOrg(orgSlug) window.location.reload() } catch (error) { console.error("Failed to switch organization:", error) setSwitchingOrgId(null) } } const { usdIncluded, usdSpent, planUsagePct, currentPlan, hasPaidPlan, isLoading: isCheckingStatus, daysRemaining, } = useTokenUsage(autumn) const formatUsd = (n: number) => n.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2, }) const planDisplayNames: Record = { free: "Free", pro: "Pro", scale: "Scale", enterprise: "Enterprise", } // Handlers const handleUpgrade = async () => { setIsUpgrading(true) try { const result = await autumn.attach({ planId: "api_pro", successUrl: `${window.location.origin}/settings#account`, }) if (result?.paymentUrl) { window.open(result.paymentUrl, "_self") return } autumn.refetch?.() } catch (error) { console.error(error) toast.error("Failed to start checkout. Please try again.") } finally { setIsUpgrading(false) } } const handleDeleteAccount = async () => { if (!user?.email || !emailMatches || membershipsPending) return setIsClosingAccount(true) try { await deleteUserAccount.mutateAsync({ confirmation: user.email, notifyOnComplete: notifyWhenDeleted, }) clearActiveOrg() try { await authClient.signOut() } catch { window.location.assign("/login/new") return } setIsDeleteDialogOpen(false) setEmailConfirm("") setNotifyWhenDeleted(false) window.location.assign("/login/new") } catch (e) { const msg = e instanceof Error ? e.message : "Something went wrong" toast.error(msg) } finally { setIsClosingAccount(false) } } // Format member since date const memberSince = user?.createdAt ? new Date(user.createdAt).toLocaleDateString("en-US", { month: "short", year: "numeric", }) : "—" return (
Profile Details
{/* Avatar + Name/Email */}
{user?.name?.charAt(0) ?? "U"}

{user?.name ?? "—"}

{user?.email ?? "—"}

Organization

{org?.name ?? "Personal"} {allOrgs && allOrgs.length > 1 && ( {allOrgs.map((organization) => { const isCurrent = organization.id === org?.id const isSwitching = switchingOrgId === organization.id return ( ) })} )}

Member since

{memberSince}

Billing & Subscription
{hasPaidPlan ? ( <>

{planDisplayNames[currentPlan]} plan

ACTIVE

Expanded memory with connections and more

{/* Plan usage (unified) */}

Plan usage

{planUsagePct < 1 && planUsagePct > 0 ? "< 1" : Math.round(planUsagePct)} % used
80 ? "#ef4444" : "linear-gradient(to right, #4BA0FA 80%, #002757 100%)", }} title={`$${formatUsd(usdSpent)} of $${formatUsd(usdIncluded)} used`} />

{daysRemaining !== null ? `Resets in ${daysRemaining} day${daysRemaining !== 1 ? "s" : ""}` : ""}

) : ( <>

Free Plan

You are on basic plan

{/* Plan usage (unified) */}

Plan usage

{planUsagePct < 1 && planUsagePct > 0 ? "< 1" : Math.round(planUsagePct)} % used

80 ? "#ef4444" : "#0054AD", }} title={`$${formatUsd(usdSpent)} of $${formatUsd(usdIncluded)} used`} />

{daysRemaining !== null ? `Resets in ${daysRemaining} day${daysRemaining !== 1 ? "s" : ""}` : ""}

)}
Delete Account

Permanently delete all your data and cancel any active subscriptions

{ setIsDeleteDialogOpen(open) if (!open) { setEmailConfirm("") setNotifyWhenDeleted(false) } }} >
{/* Header */}

Delete account?

This cannot be undone.

{hasOwnedOrgWithTeammates && (

You own at least one organization that still has other members. Those organizations will be deleted for everyone when you confirm.

)}
What happens next?

Your account is locked immediately; data removal runs in the background.

  • Removes memories, conversations, and settings; cancels active subscriptions.
  • Orgs where you're only a member: you're removed; the org continues.
  • Orgs you own: deleted for all members.
{showMembershipsOverview && (

Your organizations

{sortedMemberships.map((m) => (

{m.name}

{m.slug ? (

{m.slug}

) : null}
{formatOrgRole(m.role)} {m.memberCount} member {m.memberCount === 1 ? "" : "s"}
))}
)} {/* Confirmation input */}

Type your account email to confirm:

setEmailConfirm(e.target.value)} placeholder={user?.email ?? "you@example.com"} className={cn( "w-full px-4 py-3 bg-transparent", "text-[#FAFAFA] placeholder:text-[#737373]", "text-[14px] tracking-[-0.14px]", "outline-none", dmSans125ClassName(), )} />
{/* Footer */}
{/* Modal inset highlight */}
) }