|
1 | | -import AuthenticatedSessionController from '@/actions/App/Http/Controllers/Auth/AuthenticatedSessionController'; |
2 | 1 | import InputError from '@/components/input-error'; |
3 | 2 | import TextLink from '@/components/text-link'; |
4 | 3 | import { Button } from '@/components/ui/button'; |
5 | 4 | import { Checkbox } from '@/components/ui/checkbox'; |
| 5 | +import { Field, FieldLabel } from '@/components/ui/field'; |
6 | 6 | import { Input } from '@/components/ui/input'; |
7 | 7 | import { Label } from '@/components/ui/label'; |
| 8 | +import { Spinner } from '@/components/ui/spinner'; |
8 | 9 | import AuthLayout from '@/layouts/auth-layout'; |
9 | 10 | import { register } from '@/routes'; |
| 11 | +import { store } from '@/routes/login'; |
10 | 12 | import { request } from '@/routes/password'; |
11 | 13 | import { Form, Head } from '@inertiajs/react'; |
12 | | -import { LoaderCircle } from 'lucide-react'; |
13 | 14 | import { Activity } from 'react'; |
14 | 15 |
|
15 | 16 | interface LoginProps { |
16 | 17 | status?: string; |
17 | 18 | canResetPassword: boolean; |
| 19 | + canRegister: boolean; |
18 | 20 | } |
19 | 21 |
|
20 | | -export default function Login({ status, canResetPassword }: Readonly<LoginProps>) { |
| 22 | +export default function Login({ status, canResetPassword, canRegister }: Readonly<LoginProps>) { |
21 | 23 | return ( |
22 | 24 | <AuthLayout title="Log in to your account" description="Enter your email and password below to log in"> |
23 | 25 | <Head title="Log in" /> |
24 | 26 |
|
25 | 27 | <Form |
26 | | - {...AuthenticatedSessionController.store.form()} |
| 28 | + {...store.form()} |
27 | 29 | resetOnSuccess={['password']} |
| 30 | + className="flex flex-col gap-6 inert:pointer-events-none inert:opacity-60 inert:grayscale-100" |
28 | 31 | disableWhileProcessing |
29 | | - className="space-y-6 inert:pointer-events-none inert:opacity-50 inert:grayscale-100" |
30 | 32 | > |
31 | 33 | {({ processing, errors }) => ( |
32 | 34 | <> |
33 | 35 | <div className="grid gap-6"> |
34 | | - <div className="grid gap-2"> |
35 | | - <Label htmlFor="email">Email address</Label> |
| 36 | + <Field> |
| 37 | + <FieldLabel htmlFor="email">Email address</FieldLabel> |
36 | 38 | <Input |
37 | | - id="email" |
38 | 39 | type="email" |
| 40 | + id="email" |
39 | 41 | name="email" |
40 | | - required |
41 | 42 | autoFocus |
42 | 43 | tabIndex={1} |
43 | 44 | autoComplete="email" |
44 | 45 | |
| 46 | + required |
| 47 | + aria-required |
| 48 | + aria-invalid={!!errors.email} |
45 | 49 | /> |
46 | 50 | <InputError message={errors.email} /> |
47 | | - </div> |
| 51 | + </Field> |
48 | 52 |
|
49 | | - <div className="grid gap-2"> |
| 53 | + <Field> |
50 | 54 | <div className="flex items-center"> |
51 | | - <Label htmlFor="password">Password</Label> |
| 55 | + <FieldLabel htmlFor="password">Password</FieldLabel> |
| 56 | + |
52 | 57 | <Activity mode={canResetPassword ? 'visible' : 'hidden'}> |
53 | 58 | <TextLink href={request()} className="ml-auto text-sm" tabIndex={5}> |
54 | 59 | Forgot password? |
55 | 60 | </TextLink> |
56 | 61 | </Activity> |
57 | 62 | </div> |
| 63 | + |
58 | 64 | <Input |
59 | | - id="password" |
60 | 65 | type="password" |
| 66 | + id="password" |
61 | 67 | name="password" |
62 | | - required |
63 | 68 | tabIndex={2} |
64 | 69 | autoComplete="current-password" |
65 | 70 | placeholder="Password" |
| 71 | + required |
| 72 | + aria-required |
| 73 | + aria-invalid={!!errors.password} |
66 | 74 | /> |
67 | 75 | <InputError message={errors.password} /> |
68 | | - </div> |
| 76 | + </Field> |
69 | 77 |
|
70 | 78 | <div className="flex items-center space-x-3"> |
71 | 79 | <Checkbox id="remember" name="remember" tabIndex={3} /> |
72 | 80 | <Label htmlFor="remember">Remember me</Label> |
73 | 81 | </div> |
74 | 82 |
|
75 | | - <Button type="submit" className="mt-4 w-full" tabIndex={4} data-test="login-button"> |
76 | | - <Activity mode={processing ? 'visible' : 'hidden'}> |
77 | | - <LoaderCircle className="size-4 animate-spin" /> |
78 | | - </Activity> |
| 83 | + <Button |
| 84 | + type="submit" |
| 85 | + className="mt-4 w-full" |
| 86 | + tabIndex={4} |
| 87 | + disabled={processing} |
| 88 | + data-test="login-button" |
| 89 | + > |
| 90 | + {processing && <Spinner />} |
79 | 91 | Log in |
80 | 92 | </Button> |
81 | 93 | </div> |
82 | 94 |
|
83 | | - <div className="text-center text-sm text-muted-foreground"> |
84 | | - Don't have an account?{' '} |
85 | | - <TextLink href={register()} tabIndex={5}> |
86 | | - Sign up |
87 | | - </TextLink> |
88 | | - </div> |
| 95 | + <Activity mode={canRegister ? 'visible' : 'hidden'}> |
| 96 | + <div className="text-center text-sm text-muted-foreground"> |
| 97 | + Don't have an account?{' '} |
| 98 | + <TextLink href={register()} tabIndex={5}> |
| 99 | + Sign up |
| 100 | + </TextLink> |
| 101 | + </div> |
| 102 | + </Activity> |
89 | 103 | </> |
90 | 104 | )} |
91 | 105 | </Form> |
92 | 106 |
|
93 | | - {status && <div className="mb-4 text-center text-sm font-medium text-green-600">{status}</div>} |
| 107 | + {status && <div className="mt-4 text-center text-sm font-medium text-green-600">{status}</div>} |
94 | 108 | </AuthLayout> |
95 | 109 | ); |
96 | 110 | } |
0 commit comments