added Profile Page and Change password (also included breadcrumb
This commit is contained in:
171
src/components/form/signin-form.tsx
Normal file
171
src/components/form/signin-form.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import { authClient } from '@/lib/auth-client'
|
||||
import i18n from '@/lib/i18n'
|
||||
import { useForm } from '@tanstack/react-form'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { createLink, useNavigate } from '@tanstack/react-router'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { toast } from 'sonner'
|
||||
import z from 'zod'
|
||||
import { Button } from '../ui/button'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'
|
||||
import { Field, FieldError, FieldGroup, FieldLabel } from '../ui/field'
|
||||
import { Input } from '../ui/input'
|
||||
|
||||
const SignInFormSchema = z.object({
|
||||
email: z
|
||||
.string()
|
||||
.nonempty(
|
||||
i18n.t('loginPage.messages.is_required', {
|
||||
field: i18n.t('loginPage.form.email'),
|
||||
}),
|
||||
)
|
||||
.email(i18n.t('loginPage.messages.email_invalid')),
|
||||
password: z.string().nonempty(
|
||||
i18n.t('loginPage.messages.is_required', {
|
||||
field: i18n.t('loginPage.form.password'),
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
const ButtonLink = createLink(Button)
|
||||
|
||||
const SignInForm = () => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const queryClient = useQueryClient()
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
email: '',
|
||||
password: '',
|
||||
},
|
||||
validators: {
|
||||
onSubmit: SignInFormSchema,
|
||||
onChange: SignInFormSchema,
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
await authClient.signIn.email(
|
||||
{
|
||||
email: value.email,
|
||||
password: value.password,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
navigate({ to: '/' })
|
||||
queryClient.invalidateQueries({ queryKey: ['session'] })
|
||||
toast.success(t('loginPage.messages.login_success'))
|
||||
},
|
||||
onError: (ctx) => {
|
||||
toast.error(t(`backend.${ctx.error.code}` as any))
|
||||
},
|
||||
},
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<Card>
|
||||
<CardHeader className="text-center">
|
||||
<CardTitle className="text-xl">
|
||||
{t('loginPage.ui.welcome_back')}
|
||||
</CardTitle>
|
||||
{/* <CardDescription>Login with your Google account</CardDescription> */}
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form
|
||||
id="sign-in-form"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
form.handleSubmit()
|
||||
}}
|
||||
>
|
||||
<FieldGroup>
|
||||
{/* <Field>
|
||||
<Button variant="outline" type="button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Login with Google
|
||||
</Button>
|
||||
</Field>
|
||||
<FieldSeparator>Or continue with</FieldSeparator> */}
|
||||
<form.Field
|
||||
name="email"
|
||||
children={(field) => {
|
||||
const isInvalid =
|
||||
field.state.meta.isTouched && !field.state.meta.isValid
|
||||
return (
|
||||
<Field data-invalid={isInvalid}>
|
||||
<FieldLabel htmlFor={field.name}>
|
||||
{t('loginPage.form.email')}
|
||||
</FieldLabel>
|
||||
<Input
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
aria-invalid={isInvalid}
|
||||
type="email"
|
||||
placeholder="m@example.com"
|
||||
autoComplete="off"
|
||||
/>
|
||||
{isInvalid && (
|
||||
<FieldError errors={field.state.meta.errors} />
|
||||
)}
|
||||
</Field>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<form.Field
|
||||
name="password"
|
||||
children={(field) => {
|
||||
const isInvalid =
|
||||
field.state.meta.isTouched && !field.state.meta.isValid
|
||||
return (
|
||||
<Field data-invalid={isInvalid}>
|
||||
<FieldLabel htmlFor={field.name}>
|
||||
{t('loginPage.form.password')}
|
||||
</FieldLabel>
|
||||
<Input
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
value={field.state.value}
|
||||
onBlur={field.handleBlur}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
aria-invalid={isInvalid}
|
||||
type="password"
|
||||
/>
|
||||
{isInvalid && (
|
||||
<FieldError errors={field.state.meta.errors} />
|
||||
)}
|
||||
</Field>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Field>
|
||||
<Button type="submit">{t('ui.login_btn')}</Button>
|
||||
<ButtonLink to="/" variant="outline">
|
||||
{t('ui.cancel_btn')}
|
||||
</ButtonLink>
|
||||
{/* <FieldDescription className="text-center">
|
||||
{t('loginPage.ui.not_have_account')}{' '}
|
||||
<Link to="/sign-up">{t('ui.signup_btn')}</Link>
|
||||
</FieldDescription> */}
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* <FieldDescription className="px-6 text-center">
|
||||
By clicking continue, you agree to our <a href="#">Terms of Service</a>{' '}
|
||||
and <a href="#">Privacy Policy</a>.
|
||||
</FieldDescription> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignInForm
|
||||
Reference in New Issue
Block a user