refactor(account): redesign settings index, drop card stack
The index page had ~12 distinct card surfaces with three places of nested cards-inside-cards, against PRODUCT.md's "elevation = lighter surface + border" + "nested cards are always wrong" rules. Branding appeared twice, Display Code lived in Identity but does invite work, and Preferences got a full card for one dropdown. Single column, max-w-3xl, no card chrome. Sections separated by border-t rules + mono-uppercase section labels (existing house style): - Header: inline-editable name + plan/status/owner/member-count info line. No card. - Plan & usage: renewal date right-aligned in section header, three thin progress rows replace the 4-card usage stat grid, upgrade CTAs right-aligned at bottom. - People (owner-only): invite form, unified members + pending invites list, display code as a quiet "share to invite during signup" line. Non-owners see a one-line "managed by your admin" instead of a card. - Settings: dense route list (icon + title + summary + status pill + chevron). Profile above a thin divider; team-admin rows below, owner-gated. Branding row carries the Included/Plan-gated pill. Support & Feedback as a dim link at the bottom. - Account actions: plain rows. Owner: Transfer + Delete. Non-owner: Leave. Destructive labels colored, no red box-of-doom. Drops: Access & Security card (filler), Preferences card, Settings Areas link grid, billing-card branding-status duplicate, SettingsLinkCard helper. Default export format moves to Profile Settings where it belongs (personal preference, not account). 856 -> 710 lines on the index. tsc, eslint, vite build clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ import { Link } from 'react-router-dom'
|
||||
import { User as UserIcon, Loader2, AlertCircle, Check } from 'lucide-react'
|
||||
import { authApi } from '@/api/auth'
|
||||
import { useAuthStore } from '@/store/authStore'
|
||||
import { useUserPreferencesStore } from '@/store/userPreferencesStore'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { toast } from '@/lib/toast'
|
||||
import type { UserUpdate } from '@/types'
|
||||
@@ -16,6 +17,7 @@ const inputClass = cn(
|
||||
export function ProfileSettingsPage() {
|
||||
const user = useAuthStore((s) => s.user)
|
||||
const fetchUser = useAuthStore((s) => s.fetchUser)
|
||||
const { defaultExportFormat, setDefaultExportFormat } = useUserPreferencesStore()
|
||||
|
||||
const [name, setName] = useState(user?.name ?? '')
|
||||
const [email, setEmail] = useState(user?.email ?? '')
|
||||
@@ -120,6 +122,27 @@ export function ProfileSettingsPage() {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Default export format — saved on change, not via Save Changes */}
|
||||
<div>
|
||||
<label htmlFor="profile-export-format" className="block text-sm font-medium text-foreground">
|
||||
Default export format
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground">Pre-selected when exporting sessions.</p>
|
||||
<select
|
||||
id="profile-export-format"
|
||||
value={defaultExportFormat}
|
||||
onChange={(e) => {
|
||||
setDefaultExportFormat(e.target.value as 'markdown' | 'text' | 'html')
|
||||
toast.success('Preference saved')
|
||||
}}
|
||||
className={inputClass}
|
||||
>
|
||||
<option value="markdown">Markdown (.md)</option>
|
||||
<option value="text">Plain text (.txt)</option>
|
||||
<option value="html">HTML (.html)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="flex items-center gap-2 text-sm text-rose-500">
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
|
||||
Reference in New Issue
Block a user