TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1'use client';23import { useActionState } from 'react';4import { cancelChangeEmailAction, confirmChangeEmailAction, startChangeEmailAction, updateProfileAction } from '@/lib/auth/account-actions';5import { idle } from '@/lib/auth/state';6import { CodeInput, Field, FormMessage, Select, SubmitButton, TextArea, TextInput } from '@/components/account/form';78export function ProfileForm({ user }: { user: { name: string | null; handle: string | null; bio: string | null; displayCurrency: string } }) {9 const [state, action] = useActionState(updateProfileAction, idle);10 return (11 <form action={action} className="space-y-4" noValidate>12 <FormMessage state={state} />13 <div className="grid gap-4 sm:grid-cols-2">14 <Field label="Name" name="name" error={state.fieldErrors?.name}>15 <TextInput name="name" defaultValue={user.name ?? ''} maxLength={80} autoComplete="name" />16 </Field>17 <Field label="Public handle" name="handle" error={state.fieldErrors?.handle} hint="3–24 chars, letters/numbers/hyphens. Unlocks /u/handle.">18 <div className="flex items-center">19 <span className="rounded-l-md border border-r-0 border-border bg-sunken px-2.5 py-2 text-sm text-subtle">/u/</span>20 <TextInput name="handle" id="handle" defaultValue={user.handle ?? ''} className="rounded-l-none" placeholder="yourname" autoCapitalize="off" />21 </div>22 </Field>23 </div>24 <Field label="Bio" name="bio" error={state.fieldErrors?.bio} hint="Up to 280 characters.">25 <TextArea name="bio" defaultValue={user.bio ?? ''} maxLength={280} placeholder="What do you collect?" />26 </Field>27 <Field label="Display currency" name="displayCurrency" hint="Historical sales keep their native currency; this only changes the display conversion.">28 <Select name="displayCurrency" defaultValue={user.displayCurrency} className="sm:max-w-[200px]">29 {['USD', 'CAD', 'EUR', 'GBP', 'JPY'].map((c) => (30 <option key={c} value={c}>31 {c}32 </option>33 ))}34 </Select>35 </Field>36 <SubmitButton pendingText="Saving…">Save profile</SubmitButton>37 </form>38 );39}4041export function ChangeEmailForm({ pendingEmail }: { pendingEmail: string | null }) {42 const [start, startAction] = useActionState(startChangeEmailAction, idle);43 const [confirm, confirmAction] = useActionState(confirmChangeEmailAction, idle);44 const pending = (start.data?.pendingEmail as string | undefined) ?? pendingEmail;45 if (pending && !confirm.ok) {46 return (47 <div className="space-y-4">48 <p className="text-sm text-muted">49 Enter the code we sent to <span className="font-medium text-fg">{pending}</span>.50 </p>51 <form action={confirmAction} className="space-y-3" noValidate>52 <FormMessage state={confirm} />53 <CodeInput name="code" autoSubmit={false} label="Confirmation code" />54 <div className="flex gap-2">55 <SubmitButton pendingText="Confirming…">Confirm new e-mail</SubmitButton>56 </div>57 </form>58 <form action={cancelChangeEmailAction}>59 <SubmitButton variant="ghost" className="h-8 text-xs">60 Cancel change61 </SubmitButton>62 </form>63 </div>64 );65 }66 return (67 <form action={startAction} className="space-y-4" noValidate>68 <FormMessage state={confirm.ok ? confirm : start} />69 <div className="grid gap-4 sm:grid-cols-2">70 <Field label="New e-mail" name="newEmail" error={start.fieldErrors?.newEmail}>71 <TextInput name="newEmail" type="email" autoComplete="email" required />72 </Field>73 <Field label="Current password" name="password" error={start.fieldErrors?.password}>74 <TextInput name="password" type="password" autoComplete="current-password" required />75 </Field>76 </div>77 <SubmitButton variant="secondary" pendingText="Sending code…">78 Send confirmation code79 </SubmitButton>80 </form>81 );82}83