File
Blob: worker/src/client/ui/login.tsx
| 1 | import { useActionState } from 'react'; |
| 2 | import { errorText } from '@/client/radio/api.ts'; |
| 3 | |
| 4 | export function Login({ onLogin }: { onLogin: (password: string) => Promise<void> }) { |
| 5 | const [error, submit, pending] = useActionState(async (_previous: string, form: FormData) => { |
| 6 | try { |
| 7 | await onLogin(String(form.get('password') ?? '')); |
| 8 | return ''; |
| 9 | } catch (error) { |
| 10 | return errorText(error); |
| 11 | } |
| 12 | }, ''); |
| 13 | return ( |
| 14 | <form |
| 15 | action={submit} |
| 16 | className="mb-5 flex flex-wrap items-center gap-3 rounded-lg border border-line p-4 sm:p-5 [&>div]:min-w-[200px] [&>div]:flex-1 [&>div]:basis-full lg:[&>div]:basis-auto [&_strong]:text-base [&_strong]:font-medium [&_p]:mt-1 [&_p]:text-xs [&_p]:leading-relaxed [&_p]:text-muted [&_label]:min-w-32 [&_label]:flex-1 lg:[&_label]:flex-none [&_input]:w-full [&_input]:rounded-md [&_input]:border [&_input]:border-line [&_input]:bg-leaf [&_input]:px-3 [&_input]:py-3 [&_input]:text-sm" |
| 17 | aria-label="Private radio access" |
| 18 | > |
| 19 | <div> |
| 20 | <strong>Private access</strong> |
| 21 | <p>Enter the password to listen and use the controls.</p> |
| 22 | </div> |
| 23 | <label> |
| 24 | <span className="sr-only">Viewer password</span> |
| 25 | <input |
| 26 | name="password" |
| 27 | type="password" |
| 28 | autoComplete="current-password" |
| 29 | placeholder="Viewer password" |
| 30 | aria-label="Viewer password" |
| 31 | required |
| 32 | maxLength={256} |
| 33 | disabled={pending} |
| 34 | /> |
| 35 | </label> |
| 36 | <button |
| 37 | type="submit" |
| 38 | className="inline-flex min-h-11 items-center justify-center gap-2 rounded-md border border-line px-3 py-2 text-xs font-medium whitespace-nowrap border-ink bg-ink text-paper enabled:hover:bg-ink-hover" |
| 39 | disabled={pending} |
| 40 | > |
| 41 | {pending ? 'Unlocking…' : 'Unlock exhibit'} |
| 42 | </button> |
| 43 | {error ? ( |
| 44 | <p className="basis-full text-error!" role="alert"> |
| 45 | {error} |
| 46 | </p> |
| 47 | ) : null} |
| 48 | </form> |
| 49 | ); |
| 50 | } |