diff --git a/frontend/src/pages/AccountSettingsPage.tsx b/frontend/src/pages/AccountSettingsPage.tsx
index f19e169e..b04c502e 100644
--- a/frontend/src/pages/AccountSettingsPage.tsx
+++ b/frontend/src/pages/AccountSettingsPage.tsx
@@ -2,9 +2,7 @@ import { useEffect, useMemo, useState } from 'react'
import { Link } from 'react-router-dom'
import {
AlertCircle,
- AlertTriangle,
ArrowRight,
- Building2,
Check,
Clock,
Copy,
@@ -14,13 +12,11 @@ import {
Mail,
MessageSquareText,
Palette,
+ Pencil,
Plug,
RefreshCw,
Server,
- Settings,
- ShieldCheck,
UserCog,
- Users,
X,
} from 'lucide-react'
import { PageMeta } from '@/components/common/PageMeta'
@@ -36,46 +32,20 @@ import { cn } from '@/lib/utils'
import { usePermissions } from '@/hooks/usePermissions'
import { useSubscription } from '@/hooks/useSubscription'
import { useAuthStore } from '@/store/authStore'
-import { useUserPreferencesStore } from '@/store/userPreferencesStore'
import { CheckoutButton } from '@/components/subscription/CheckoutButton'
import { toast } from '@/lib/toast'
-interface SettingsLinkCardProps {
- to: string
- icon: React.ReactNode
- title: string
- description: string
- badge?: string
-}
+/* ── Building blocks ─────────────────────────────────────────────────────── */
-function SettingsLinkCard({ to, icon, title, description, badge }: SettingsLinkCardProps) {
+function SectionLabel({ children }: { children: React.ReactNode }) {
return (
-
-
-
-
{icon}
-
-
-
{title}
- {badge && (
-
- {badge}
-
- )}
-
-
{description}
-
-
-
-
-
+
+ {children}
+
)
}
-function UsageStat({
+function UsageRow({
label,
current,
max,
@@ -86,47 +56,85 @@ function UsageStat({
}) {
const isUnlimited = max === null
const percentage = isUnlimited ? 0 : Math.min((current / max) * 100, 100)
- const isNearLimit = !isUnlimited && percentage >= 80
const isAtLimit = !isUnlimited && current >= max
+ const isNearLimit = !isUnlimited && percentage >= 80
+
+ const numeralColor = isAtLimit
+ ? 'text-danger'
+ : isNearLimit
+ ? 'text-warning'
+ : 'text-foreground'
+ const barColor = isAtLimit ? 'bg-danger' : isNearLimit ? 'bg-warning' : 'bg-primary'
return (
-
-
{label}
-
+ {label}
+
+ {!isUnlimited && (
+
)}
- >
- {current}
-
- / {isUnlimited ? 'Unlimited' : max}
-
-
- {!isUnlimited && (
-
- )}
+
+
+ {current}
+ / {isUnlimited ? '∞' : max}
+
)
}
+interface SettingsRowProps {
+ to: string
+ icon: React.ReactNode
+ title: string
+ description: string
+ status?: { label: string; tone: 'positive' | 'neutral' | 'warning' }
+}
+
+function SettingsRow({ to, icon, title, description, status }: SettingsRowProps) {
+ return (
+
+ {icon}
+
+
{title}
+
{description}
+
+ {status && (
+
+ {status.label}
+
+ )}
+
+
+ )
+}
+
function formatShortDate(value: string | null | undefined) {
if (!value) return 'Never'
return new Date(value).toLocaleDateString()
}
+function planLabel(plan: string) {
+ return plan.charAt(0).toUpperCase() + plan.slice(1)
+}
+
+/* ── Page ────────────────────────────────────────────────────────────────── */
+
export function AccountSettingsPage() {
const { isAccountOwner } = usePermissions()
const { plan, limits, usage } = useSubscription()
- const { defaultExportFormat, setDefaultExportFormat } = useUserPreferencesStore()
const subscription = useAuthStore((s) => s.subscription)
const user = useAuthStore((s) => s.user)
const refreshUser = useAuthStore((s) => s.fetchUser)
@@ -180,8 +188,11 @@ export function AccountSettingsPage() {
}
const pendingInvites = useMemo(() => invites.filter((invite) => !invite.used_at), [invites])
- const ownerMember = useMemo(() => members.find((member) => member.account_role === 'owner') ?? null, [members])
-
+ const ownerMember = useMemo(
+ () => members.find((member) => member.account_role === 'owner') ?? null,
+ [members]
+ )
+ const memberCount = members.length || (isAccountOwner ? 1 : undefined)
const handleCopyDisplayCode = async () => {
if (!account?.display_code) return
@@ -276,562 +287,405 @@ export function AccountSettingsPage() {
}
const sub = subscription?.subscription
+ const ownerName = ownerMember?.name ?? (user?.account_role === 'owner' ? user.name : null)
+ const inputClass = cn(
+ 'rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground',
+ 'placeholder:text-muted-foreground',
+ 'focus:border-primary/40 focus:outline-hidden focus:ring-1 focus:ring-primary/20'
+ )
return (
<>
-
-
-
Account Management
-
- Manage your account identity, billing, access, and workspace settings.
-
-
-
-
-
-
+
+ {/* ── Header ─────────────────────────────────────────────────────── */}
+
+
+ {isEditingName ? (
-
-
Account Identity
+ setEditedName(e.target.value)}
+ className={cn(inputClass, 'text-2xl font-bold font-heading py-1')}
+ autoFocus
+ onKeyDown={(e) => {
+ if (e.key === 'Enter') handleSaveName()
+ if (e.key === 'Escape') {
+ setEditedName(account?.name ?? '')
+ setIsEditingName(false)
+ }
+ }}
+ />
+
+
-
-
-
-
- {isEditingName ? (
-
- setEditedName(e.target.value)}
- className={cn(
- 'flex-1 rounded-md border border-border bg-card px-3 py-2',
- 'text-foreground placeholder:text-muted-foreground',
- 'focus:border-primary focus:outline-hidden focus:ring-1 focus:ring-primary/20'
- )}
- autoFocus
- onKeyDown={(e) => {
- if (e.key === 'Enter') handleSaveName()
- if (e.key === 'Escape') {
- setEditedName(account?.name ?? '')
- setIsEditingName(false)
- }
- }}
- />
-
-
-
- ) : (
-
- {account?.name}
- {isAccountOwner && (
-
- )}
-
- )}
-
-
-
-
-
-
-
- {account?.display_code}
-
-
-
-
- Share this code with teammates so they can join your account during registration.
-
-
-
-
-
-
- {ownerMember ? ownerMember.name : user?.account_role === 'owner' ? user.email : 'Owner unavailable'}
-
-
- {ownerMember?.email ?? 'The owner manages billing, branding, integrations, and membership changes.'}
-
-
-
-
-
-
-
-
{formatShortDate(account?.created_at)}
-
-
-
-
{formatShortDate(account?.updated_at)}
-
-
-
-
-
-
-
-
-
-
-
Billing & Usage
-
-
- Monitor plan status, renewal timing, and current account limits.
-
-
-
-
-
+ ) : (
+ <>
+
{account?.name}
+ {isAccountOwner && (
+
+ )}
+ >
+ )}
+
+
+
+
+ {planLabel(plan)} plan
+
+ {sub && (
+ <>
+ ·
-
- {plan.charAt(0).toUpperCase() + plan.slice(1)} Plan
+ {sub.status.charAt(0).toUpperCase() + sub.status.slice(1).replace('_', ' ')}
- {sub && (
-
- {sub.status.charAt(0).toUpperCase() + sub.status.slice(1).replace('_', ' ')}
-
- )}
-
+ >
+ )}
+ {ownerName && (
+ <>
+
·
+
Owned by {ownerName}
+ >
+ )}
+ {memberCount !== undefined && (
+ <>
+
·
+
+ {memberCount} {memberCount === 1 ? 'member' : 'members'}
+
+ >
+ )}
+ <>
+
·
+
Created {formatShortDate(account?.created_at)}
+ >
+
+
-
-
-
Renewal date
-
- {sub?.current_period_end ? new Date(sub.current_period_end).toLocaleDateString() : 'Not scheduled'}
-
-
-
-
Branding access
-
- {limits?.custom_branding ? 'Included in your plan' : 'Upgrade required'}
-
-
-
+ {/* ── Plan & Usage ───────────────────────────────────────────────── */}
+
+
+ Plan & usage
+
+ {sub?.current_period_end
+ ? `Renews ${new Date(sub.current_period_end).toLocaleDateString()}`
+ : 'No renewal scheduled'}
+
+
- {limits && usage && (
-
-
-
-
-
- )}
-
- {plan === 'free' && (
-
-
-
-
- )}
- {plan === 'pro' && (
-
-
-
- )}
+ {limits && usage ? (
+
+
+
+
+ ) : (
+ Plan limits unavailable.
+ )}
-
-
-
-
Access & Security
-
+ {plan !== 'team' && (
+
+ {plan === 'free' && }
+
+
+ )}
+
-
-
-
Authentication
-
- {plan === 'team' ? 'Password auth available, SSO can be enabled.' : 'Password-based authentication is active.'}
-
-
- Use profile settings to update your personal details and sign-in information.
-
-
+ {/* ── People ─────────────────────────────────────────────────────── */}
+ {isAccountOwner ? (
+
+ People
-
-
Single Sign-On
-
- {plan === 'team' ? 'Enterprise-ready setup available.' : 'Available on higher-tier account setups.'}
-
-
- Contact support to configure SAML or OIDC for your organization.
-
-
-
+
- {isAccountOwner && (
-
-
-
-
Need enterprise security controls?
-
- We can help enable SSO and align account security for larger teams.
-
+ {(members.length > 0 || pendingInvites.length > 0) && (
+
+ {members.map((member) => (
+ -
+
+
+
+ {member.name}
+
+ {!member.is_active && (
+
+ Inactive
+
+ )}
+
+
+ {member.email}
+ {member.last_login && (
+ · Last seen {formatShortDate(member.last_login)}
+ )}
+
-
- Contact Us
-
-
-
- )}
-
-
-
-