betterstart-cli 0.0.108 → 0.0.109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -98,7 +98,7 @@
98
98
  --warning-foreground: oklch(0.625 0.165 57.4);
99
99
  --border: oklch(0.93 0 0);
100
100
  --highlight: oklch(0.9 0.004 286.32);
101
- --input: oklch(0.994 0 0);
101
+ --input: oklch(0.93 0 0);
102
102
  --ring: oklch(0.708 0 0);
103
103
  --chart-1: oklch(0.845 0.143 164.978);
104
104
  --chart-2: oklch(0.696 0.17 162.48);
@@ -102,6 +102,7 @@ export function EditMediaDialogContent({
102
102
  </DrawerHeader>
103
103
  <Form {...form}>
104
104
  <form
105
+ autoComplete="off"
105
106
  id={formId}
106
107
  onSubmit={form.handleSubmit(handleSave)}
107
108
  className="flex flex-1 flex-col gap-4 overflow-y-auto px-6 pt-px pb-4"
@@ -2,7 +2,9 @@
2
2
 
3
3
  import * as React from 'react'
4
4
  import type { UploadFileResult } from '@admin/types'
5
+ import type { FieldErrors } from 'react-hook-form'
5
6
  import { Card, CardContent } from '@admin/components/ui/card'
7
+ import { Form } from '@admin/components/ui/form'
6
8
  import {
7
9
  InputGroup,
8
10
  InputGroupAddon,
@@ -15,19 +17,29 @@ import { getMediaUrlPlaceholder } from '@admin/utils/media/get-media-url-placeho
15
17
  import { cn } from '@admin/utils/shared/cn'
16
18
  import { isValidUrl } from '@admin/utils/validation/is-valid-url'
17
19
  import { Upload } from 'lucide-react'
20
+ import { useForm } from 'react-hook-form'
21
+ import { toast } from 'sonner'
18
22
 
19
23
  interface MediaUrlImporterProps extends React.HTMLAttributes<HTMLDivElement> {
20
24
  onImportFromUrl: (url: string) => Promise<UploadFileResult>
21
25
  accept?: string
22
26
  }
23
27
 
28
+ interface MediaUrlFormValues {
29
+ url: string
30
+ }
31
+
24
32
  export function MediaUrlImporter({ onImportFromUrl, accept, className }: MediaUrlImporterProps) {
25
- const [urlValue, setUrlValue] = React.useState('')
26
- const [urlError, setUrlError] = React.useState<string | null>(null)
27
33
  const [isImporting, startImportTransition] = React.useTransition()
28
34
  const mountedRef = React.useRef(true)
29
35
  const importInFlightRef = React.useRef(false)
30
36
  const importRequestIdRef = React.useRef(0)
37
+ const form = useForm<MediaUrlFormValues>({
38
+ defaultValues: { url: '' },
39
+ mode: 'onSubmit',
40
+ reValidateMode: 'onChange'
41
+ })
42
+ const urlField = form.register('url', { required: 'Enter a direct media URL to import.' })
31
43
 
32
44
  React.useEffect(() => {
33
45
  mountedRef.current = true
@@ -42,93 +54,92 @@ export function MediaUrlImporter({ onImportFromUrl, accept, className }: MediaUr
42
54
  const urlHelpText = getMediaUrlHelpText(accept)
43
55
  const urlPlaceholder = getMediaUrlPlaceholder(accept)
44
56
 
45
- const handleImport = React.useCallback(() => {
46
- if (isImporting || importInFlightRef.current) return
47
-
48
- const nextUrl = urlValue.trim()
49
-
50
- if (!nextUrl) {
51
- setUrlError('Enter a direct media URL to import.')
52
- return
53
- }
54
-
55
- if (!isValidUrl(nextUrl)) {
56
- setUrlError('Enter a valid HTTP or HTTPS URL.')
57
- return
58
- }
59
-
60
- setUrlError(null)
61
- const importRequestId = importRequestIdRef.current + 1
62
- importRequestIdRef.current = importRequestId
63
- importInFlightRef.current = true
57
+ const handleImport = React.useCallback(
58
+ (values: MediaUrlFormValues) => {
59
+ if (isImporting || importInFlightRef.current) return
64
60
 
65
- startImportTransition(async () => {
66
- try {
67
- const result = await onImportFromUrl(nextUrl)
61
+ const nextUrl = values.url.trim()
68
62
 
69
- if (!mountedRef.current || importRequestIdRef.current !== importRequestId) {
70
- return
71
- }
72
-
73
- if (!result.success) {
74
- setUrlError(result.error ?? 'Failed to import media from URL.')
75
- return
76
- }
63
+ if (!isValidUrl(nextUrl)) {
64
+ toast.error('Enter a valid HTTP or HTTPS URL.')
65
+ return
66
+ }
77
67
 
78
- setUrlValue('')
79
- } catch (error) {
80
- if (!mountedRef.current || importRequestIdRef.current !== importRequestId) {
81
- return
68
+ const importRequestId = importRequestIdRef.current + 1
69
+ importRequestIdRef.current = importRequestId
70
+ importInFlightRef.current = true
71
+
72
+ startImportTransition(async () => {
73
+ try {
74
+ const result = await onImportFromUrl(nextUrl)
75
+
76
+ if (!mountedRef.current || importRequestIdRef.current !== importRequestId) {
77
+ return
78
+ }
79
+
80
+ if (!result.success) {
81
+ toast.error(result.error ?? 'Failed to import media from URL.')
82
+ return
83
+ }
84
+
85
+ form.reset()
86
+ } catch (error) {
87
+ if (!mountedRef.current || importRequestIdRef.current !== importRequestId) {
88
+ return
89
+ }
90
+
91
+ toast.error(error instanceof Error ? error.message : 'Failed to import media from URL.')
92
+ } finally {
93
+ if (importRequestIdRef.current === importRequestId) {
94
+ importInFlightRef.current = false
95
+ }
82
96
  }
97
+ })
98
+ },
99
+ [form, isImporting, onImportFromUrl]
100
+ )
83
101
 
84
- setUrlError(error instanceof Error ? error.message : 'Failed to import media from URL.')
85
- } finally {
86
- if (importRequestIdRef.current === importRequestId) {
87
- importInFlightRef.current = false
88
- }
89
- }
90
- })
91
- }, [isImporting, onImportFromUrl, urlValue])
102
+ const handleInvalid = (errors: FieldErrors<MediaUrlFormValues>) => {
103
+ const message = errors.url?.message
104
+ toast.error(typeof message === 'string' ? message : 'Enter a direct media URL to import.')
105
+ }
92
106
 
93
107
  return (
94
108
  <Card className={cn('flex items-center justify-center', className)}>
95
109
  <CardContent className="h-full w-full">
96
110
  <div className="flex h-full w-full flex-col items-center justify-center gap-3 text-center">
97
- <InputGroup className="mx-auto max-w-2xl bg-background">
98
- <InputGroupInput
99
- type="url"
100
- name="url"
101
- aria-label="Media URL"
102
- value={urlValue}
103
- placeholder={urlPlaceholder}
111
+ <Form {...form}>
112
+ <form
104
113
  autoComplete="off"
105
- spellCheck={false}
106
- onChange={(event) => {
107
- setUrlValue(event.target.value)
108
- if (urlError) setUrlError(null)
109
- }}
110
- onKeyDown={(event) => {
111
- if (event.key === 'Enter') {
112
- event.preventDefault()
113
- void handleImport()
114
- }
115
- }}
116
- disabled={isImporting}
117
- />
118
- <InputGroupAddon align="inline-end">
119
- <InputGroupButton
120
- type="button"
121
- variant="outline"
122
- onClick={() => void handleImport()}
123
- disabled={isImporting}
124
- aria-busy={isImporting}
125
- className="h-full px-5"
126
- >
127
- {isImporting ? <Spinner /> : <Upload />}
128
- {isImporting ? 'Importing…' : 'Import'}
129
- </InputGroupButton>
130
- </InputGroupAddon>
131
- </InputGroup>
114
+ noValidate
115
+ onSubmit={form.handleSubmit(handleImport, handleInvalid)}
116
+ className="w-full"
117
+ >
118
+ <InputGroup className="mx-auto max-w-2xl bg-background">
119
+ <InputGroupInput
120
+ type="url"
121
+ aria-label="Media URL"
122
+ placeholder={urlPlaceholder}
123
+ autoComplete="off"
124
+ spellCheck={false}
125
+ {...urlField}
126
+ disabled={isImporting}
127
+ />
128
+ <InputGroupAddon align="inline-end">
129
+ <InputGroupButton
130
+ type="submit"
131
+ variant="outline"
132
+ disabled={isImporting}
133
+ aria-busy={isImporting}
134
+ className="h-full px-5"
135
+ >
136
+ {isImporting ? <Spinner /> : <Upload />}
137
+ {isImporting ? 'Importing…' : 'Import'}
138
+ </InputGroupButton>
139
+ </InputGroupAddon>
140
+ </InputGroup>
141
+ </form>
142
+ </Form>
132
143
 
133
144
  <div className="flex w-full flex-col items-center text-center">
134
145
  <p className="text-sm text-muted-foreground">{urlHelpText}</p>
@@ -40,7 +40,11 @@ export function SearchInput({
40
40
 
41
41
  return (
42
42
  <Form {...form}>
43
- <form onSubmit={form.handleSubmit(onSubmit)} className="flex w-full items-center">
43
+ <form
44
+ autoComplete="off"
45
+ onSubmit={form.handleSubmit(onSubmit)}
46
+ className="flex w-full items-center"
47
+ >
44
48
  <FormField
45
49
  control={form.control}
46
50
  name="search"
@@ -1,49 +1,68 @@
1
1
  'use client'
2
2
 
3
3
  import * as React from 'react'
4
+ import type { FieldErrors } from 'react-hook-form'
4
5
  import { isEmailConfigured } from '@admin/actions/profile'
5
6
  import { authClient } from '@admin/auth/client'
6
7
  import { Button } from '@admin/components/ui/button'
7
8
  import { Card, CardContent } from '@admin/components/ui/card'
8
9
  import { Field, FieldGroup, FieldLabel } from '@admin/components/ui/field'
10
+ import { Form } from '@admin/components/ui/form'
9
11
  import { Input } from '@admin/components/ui/input'
10
12
  import { Spinner } from '@admin/components/ui/spinner'
11
13
  import { cn } from '@admin/utils/shared/cn'
14
+ import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
12
15
  import Image from 'next/image'
13
16
  import Link from 'next/link'
17
+ import { useForm } from 'react-hook-form'
18
+ import { toast } from 'sonner'
19
+ import { z } from 'zod/v3'
20
+
21
+ const forgotPasswordSchema = z.object({
22
+ email: z.string().trim().min(1, 'Email is required').email('Enter a valid email address')
23
+ })
24
+
25
+ type ForgotPasswordFormValues = z.infer<typeof forgotPasswordSchema>
14
26
 
15
27
  export function ForgotPasswordForm({ className, ...props }: React.ComponentProps<'div'>) {
16
- const [email, setEmail] = React.useState('')
17
- const [error, setError] = React.useState<string | null>(null)
18
- const [success, setSuccess] = React.useState(false)
19
28
  const [isPending, startTransition] = React.useTransition()
29
+ const form = useForm<ForgotPasswordFormValues>({
30
+ resolver: standardSchemaResolver(forgotPasswordSchema),
31
+ defaultValues: { email: '' },
32
+ mode: 'onChange',
33
+ reValidateMode: 'onChange'
34
+ })
20
35
 
21
- const handleSubmit = (e: React.FormEvent) => {
22
- e.preventDefault()
23
- setError(null)
24
-
36
+ const handleSubmit = (values: ForgotPasswordFormValues) => {
25
37
  startTransition(async () => {
26
38
  try {
27
39
  const emailReady = await isEmailConfigured()
28
40
  if (!emailReady) {
29
- setError('Email delivery is not configured for this project.')
41
+ toast.error('Email delivery is not configured for this project.')
30
42
  return
31
43
  }
32
44
  const { error } = await authClient.requestPasswordReset({
33
- email,
45
+ email: values.email,
34
46
  redirectTo: '/admin/reset-password'
35
47
  })
36
48
  if (error) {
37
- setError(error.message || 'Failed to send reset link')
49
+ toast.error(error.message || 'Failed to send reset link')
38
50
  return
39
51
  }
40
- setSuccess(true)
52
+ toast.success(
53
+ 'If an account exists, we sent a password reset link. Check your inbox and spam folder.'
54
+ )
41
55
  } catch {
42
- setError('Failed to send the reset link. Try again.')
56
+ toast.error('Failed to send the reset link. Try again.')
43
57
  }
44
58
  })
45
59
  }
46
60
 
61
+ const handleInvalid = (errors: FieldErrors<ForgotPasswordFormValues>) => {
62
+ const message = errors.email?.message
63
+ toast.error(typeof message === 'string' ? message : 'Enter a valid email address.')
64
+ }
65
+
47
66
  return (
48
67
  <div className={cn('flex flex-col gap-6', className)} {...props}>
49
68
  <Card className="overflow-hidden p-0">
@@ -58,21 +77,12 @@ export function ForgotPasswordForm({ className, ...props }: React.ComponentProps
58
77
  />
59
78
  </div>
60
79
  <div className="p-6 md:p-20">
61
- {success ? (
62
- <FieldGroup className="gap-10 pb-12">
63
- <div className="flex flex-col items-start">
64
- <h1 className="text-xl font-medium">Check your email</h1>
65
- <p className="text-balance text-muted-foreground">
66
- If an account exists for {email}, we sent a password reset link. Check your
67
- inbox and spam folder.
68
- </p>
69
- </div>
70
- <Link href="/admin/login" className="text-sm underline-offset-4 hover:underline">
71
- Back to sign in
72
- </Link>
73
- </FieldGroup>
74
- ) : (
75
- <form onSubmit={handleSubmit}>
80
+ <Form {...form}>
81
+ <form
82
+ autoComplete="off"
83
+ noValidate
84
+ onSubmit={form.handleSubmit(handleSubmit, handleInvalid)}
85
+ >
76
86
  <FieldGroup className="gap-10 pb-12">
77
87
  <div className="flex flex-col items-start">
78
88
  <h1 className="text-xl font-medium">Forgot password</h1>
@@ -81,21 +91,16 @@ export function ForgotPasswordForm({ className, ...props }: React.ComponentProps
81
91
  enabled
82
92
  </p>
83
93
  </div>
84
- {error ? (
85
- <div className="rounded-md bg-destructive/10 p-3 text-sm text-destructive">
86
- {error}
87
- </div>
88
- ) : null}
89
94
  <div className="flex flex-col gap-4">
90
95
  <Field>
91
96
  <FieldLabel htmlFor="email">Email</FieldLabel>
92
97
  <Input
93
98
  id="email"
94
99
  type="email"
95
- autoComplete="email"
100
+ autoComplete="off"
96
101
  placeholder="m@example.com"
97
- value={email}
98
- onChange={(e) => setEmail(e.target.value)}
102
+ aria-invalid={Boolean(form.formState.errors.email)}
103
+ {...form.register('email')}
99
104
  required
100
105
  disabled={isPending}
101
106
  />
@@ -115,7 +120,7 @@ export function ForgotPasswordForm({ className, ...props }: React.ComponentProps
115
120
  </div>
116
121
  </FieldGroup>
117
122
  </form>
118
- )}
123
+ </Form>
119
124
  </div>
120
125
  </CardContent>
121
126
  </Card>
@@ -1,41 +1,64 @@
1
1
  'use client'
2
2
 
3
3
  import * as React from 'react'
4
+ import type { FieldErrors } from 'react-hook-form'
4
5
  import { authClient } from '@admin/auth/client'
5
6
  import { Button } from '@admin/components/ui/button'
6
7
  import { Card, CardContent } from '@admin/components/ui/card'
7
8
  import { Field, FieldGroup, FieldLabel } from '@admin/components/ui/field'
9
+ import { Form } from '@admin/components/ui/form'
8
10
  import { Input } from '@admin/components/ui/input'
9
11
  import { Spinner } from '@admin/components/ui/spinner'
10
12
  import { getAdminPostLoginPath } from '@admin/utils/auth/get-admin-post-login-path'
11
13
  import { cn } from '@admin/utils/shared/cn'
14
+ import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
12
15
  import Image from 'next/image'
13
16
  import Link from 'next/link'
14
17
  import { useRouter } from 'next/navigation'
15
18
  import { parseAsStringLiteral, useQueryState } from 'nuqs'
19
+ import { useForm } from 'react-hook-form'
20
+ import { toast } from 'sonner'
21
+ import { z } from 'zod/v3'
22
+
23
+ const loginSchema = z.object({
24
+ email: z.string().trim().min(1, 'Email is required').email('Enter a valid email address'),
25
+ password: z.string().min(1, 'Password is required')
26
+ })
27
+
28
+ type LoginFormValues = z.infer<typeof loginSchema>
16
29
 
17
30
  export function LoginForm({ className, ...props }: React.ComponentProps<'div'>) {
18
31
  const router = useRouter()
19
32
  const [reset] = useQueryState('reset', parseAsStringLiteral(['success']))
20
- const resetSuccess = reset === 'success'
21
- const [email, setEmail] = React.useState('')
22
- const [password, setPassword] = React.useState('')
23
- const [error, setError] = React.useState<string | null>(null)
24
33
  const [isPending, startTransition] = React.useTransition()
34
+ const form = useForm<LoginFormValues>({
35
+ resolver: standardSchemaResolver(loginSchema),
36
+ defaultValues: {
37
+ email: '',
38
+ password: ''
39
+ },
40
+ mode: 'onChange',
41
+ reValidateMode: 'onChange'
42
+ })
25
43
 
26
- const handleSubmit = (e: React.FormEvent) => {
27
- e.preventDefault()
28
- setError(null)
44
+ React.useEffect(() => {
45
+ if (reset === 'success') {
46
+ toast.success('Password reset. Sign in with your new password.', {
47
+ id: 'admin-login-password-reset-success'
48
+ })
49
+ }
50
+ }, [reset])
29
51
 
52
+ const handleSubmit = (values: LoginFormValues) => {
30
53
  startTransition(async () => {
31
54
  try {
32
55
  const result = await authClient.signIn.email({
33
- email,
34
- password
56
+ email: values.email,
57
+ password: values.password
35
58
  })
36
59
 
37
60
  if (result.error) {
38
- setError(result.error.message || 'Invalid email or password')
61
+ toast.error(result.error.message || 'Invalid email or password')
39
62
  return
40
63
  }
41
64
 
@@ -45,11 +68,16 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
45
68
  router.replace(getAdminPostLoginPath(role))
46
69
  router.refresh()
47
70
  } catch {
48
- setError('Failed to sign you in. Try again.')
71
+ toast.error('Failed to sign you in. Try again.')
49
72
  }
50
73
  })
51
74
  }
52
75
 
76
+ const handleInvalid = (errors: FieldErrors<LoginFormValues>) => {
77
+ const message = errors.email?.message ?? errors.password?.message
78
+ toast.error(typeof message === 'string' ? message : 'Please check your sign-in details.')
79
+ }
80
+
53
81
  return (
54
82
  <div className={cn('flex flex-col gap-6', className)} {...props}>
55
83
  <Card className="overflow-hidden p-0">
@@ -63,67 +91,63 @@ export function LoginForm({ className, ...props }: React.ComponentProps<'div'>)
63
91
  className="object-cover"
64
92
  />
65
93
  </div>
66
- <form onSubmit={handleSubmit} className="p-6 md:p-20">
67
- <FieldGroup className="gap-10 pb-12">
68
- <div className="flex flex-col items-start">
69
- <h1 className="text-xl font-medium">Welcome back</h1>
70
- <p className="text-balance text-muted-foreground">Sign in to your account</p>
71
- </div>
72
- {resetSuccess ? (
73
- <div className="rounded-md bg-emerald-500/10 p-3 text-sm text-emerald-700">
74
- Password reset. Sign in with your new password.
94
+ <Form {...form}>
95
+ <form
96
+ autoComplete="off"
97
+ noValidate
98
+ onSubmit={form.handleSubmit(handleSubmit, handleInvalid)}
99
+ className="p-6 md:p-20"
100
+ >
101
+ <FieldGroup className="gap-10 pb-12">
102
+ <div className="flex flex-col items-start">
103
+ <h1 className="text-xl font-medium">Sign In</h1>
75
104
  </div>
76
- ) : null}
77
- {error ? (
78
- <div className="rounded-md bg-destructive/10 p-3 text-sm text-destructive">
79
- {error}
105
+ <div className="flex flex-col gap-4">
106
+ <Field>
107
+ <FieldLabel htmlFor="email">Email</FieldLabel>
108
+ <Input
109
+ id="email"
110
+ type="email"
111
+ autoComplete="off"
112
+ placeholder="m@example.com"
113
+ aria-invalid={Boolean(form.formState.errors.email)}
114
+ {...form.register('email')}
115
+ required
116
+ disabled={isPending}
117
+ />
118
+ </Field>
119
+ <Field>
120
+ <div className="flex items-center">
121
+ <FieldLabel htmlFor="password">Password</FieldLabel>
122
+ <Link
123
+ href="/admin/forgot-password"
124
+ className="ml-auto text-sm underline-offset-4 hover:underline"
125
+ tabIndex={-1}
126
+ >
127
+ Forgot password?
128
+ </Link>
129
+ </div>
130
+ <Input
131
+ id="password"
132
+ type="password"
133
+ autoComplete="off"
134
+ placeholder="Enter your password"
135
+ aria-invalid={Boolean(form.formState.errors.password)}
136
+ {...form.register('password')}
137
+ required
138
+ disabled={isPending}
139
+ />
140
+ </Field>
141
+ <Field className="pt-4">
142
+ <Button type="submit" disabled={isPending} aria-busy={isPending}>
143
+ {isPending && <Spinner />}
144
+ {isPending ? 'Signing in…' : 'Sign in'}
145
+ </Button>
146
+ </Field>
80
147
  </div>
81
- ) : null}
82
- <div className="flex flex-col gap-4">
83
- <Field>
84
- <FieldLabel htmlFor="email">Email</FieldLabel>
85
- <Input
86
- id="email"
87
- type="email"
88
- autoComplete="email"
89
- placeholder="m@example.com"
90
- value={email}
91
- onChange={(e) => setEmail(e.target.value)}
92
- required
93
- disabled={isPending}
94
- />
95
- </Field>
96
- <Field>
97
- <div className="flex items-center">
98
- <FieldLabel htmlFor="password">Password</FieldLabel>
99
- <Link
100
- href="/admin/forgot-password"
101
- className="ml-auto text-sm underline-offset-4 hover:underline"
102
- tabIndex={-1}
103
- >
104
- Forgot password?
105
- </Link>
106
- </div>
107
- <Input
108
- id="password"
109
- type="password"
110
- autoComplete="current-password"
111
- placeholder="Enter your password"
112
- value={password}
113
- onChange={(e) => setPassword(e.target.value)}
114
- required
115
- disabled={isPending}
116
- />
117
- </Field>
118
- <Field className="pt-4">
119
- <Button type="submit" disabled={isPending} aria-busy={isPending}>
120
- {isPending && <Spinner />}
121
- {isPending ? 'Signing in…' : 'Sign in'}
122
- </Button>
123
- </Field>
124
- </div>
125
- </FieldGroup>
126
- </form>
148
+ </FieldGroup>
149
+ </form>
150
+ </Form>
127
151
  </CardContent>
128
152
  </Card>
129
153
  </div>
@@ -90,7 +90,7 @@ export function ProfileForm({ user }: ProfileFormProps) {
90
90
  newPassword: '',
91
91
  confirmPassword: ''
92
92
  },
93
- mode: 'onBlur',
93
+ mode: 'onChange',
94
94
  reValidateMode: 'onChange'
95
95
  })
96
96
 
@@ -195,7 +195,7 @@ export function ProfileForm({ user }: ProfileFormProps) {
195
195
  return (
196
196
  <div className="space-y-6">
197
197
  <Form {...profileForm}>
198
- <form onSubmit={profileForm.handleSubmit(onProfileSubmit)}>
198
+ <form autoComplete="off" onSubmit={profileForm.handleSubmit(onProfileSubmit)}>
199
199
  <Card>
200
200
  <CardHeader>
201
201
  <div className="flex items-center gap-4 pl-1">
@@ -282,7 +282,7 @@ export function ProfileForm({ user }: ProfileFormProps) {
282
282
  <Input
283
283
  type="password"
284
284
  placeholder="Enter current password to change email"
285
- autoComplete="current-password"
285
+ autoComplete="off"
286
286
  disabled={profilePending}
287
287
  {...formField}
288
288
  />
@@ -309,7 +309,7 @@ export function ProfileForm({ user }: ProfileFormProps) {
309
309
  </Form>
310
310
 
311
311
  <Form {...passwordForm}>
312
- <form onSubmit={passwordForm.handleSubmit(onPasswordSubmit)}>
312
+ <form autoComplete="off" onSubmit={passwordForm.handleSubmit(onPasswordSubmit)}>
313
313
  <Card>
314
314
  <CardHeader>
315
315
  <CardTitle>Change Password</CardTitle>
@@ -325,7 +325,7 @@ export function ProfileForm({ user }: ProfileFormProps) {
325
325
  <FormControl>
326
326
  <Input
327
327
  type="password"
328
- autoComplete="current-password"
328
+ autoComplete="off"
329
329
  disabled={passwordPending}
330
330
  {...field}
331
331
  />
@@ -343,7 +343,7 @@ export function ProfileForm({ user }: ProfileFormProps) {
343
343
  <FormControl>
344
344
  <Input
345
345
  type="password"
346
- autoComplete="new-password"
346
+ autoComplete="off"
347
347
  disabled={passwordPending}
348
348
  {...field}
349
349
  />
@@ -362,7 +362,7 @@ export function ProfileForm({ user }: ProfileFormProps) {
362
362
  <FormControl>
363
363
  <Input
364
364
  type="password"
365
- autoComplete="new-password"
365
+ autoComplete="off"
366
366
  disabled={passwordPending}
367
367
  {...field}
368
368
  />