homework 2 done

This commit is contained in:
2023-08-03 19:52:53 +02:00
parent c230948b57
commit 8d75b18f61
53 changed files with 1631 additions and 510 deletions

View File

@@ -0,0 +1,28 @@
.card {
max-width: 413px;
padding: 35px 33px 48px;
}
.title {
margin-bottom: 27px;
text-align: center;
}
.iconContainer {
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 19px;
}
.instructions {
margin-bottom: 65px;
color: var(--color-light-900);
text-align: center;
opacity: 0.5;
}
.signInLink {
display: flex;
justify-content: center;
}

View File

@@ -0,0 +1,18 @@
import type { Meta, StoryObj } from '@storybook/react'
import { CheckEmail } from './'
const meta = {
title: 'Auth/Check email',
component: CheckEmail,
tags: ['autodocs'],
} satisfies Meta<typeof CheckEmail>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
email: 'your_email@domain.com',
},
}

View File

@@ -0,0 +1,31 @@
import { Link } from 'react-router-dom'
import { Email } from '../../../assets/icons'
import { Button, Card, Typography } from '../../ui'
import s from './check-email.module.scss'
type Props = {
email: string
}
export const CheckEmail = ({ email }: Props) => {
const message = `We've sent an e-mail with instructions to ${email}`
return (
<Card className={s.card}>
<Typography variant="large" className={s.title}>
Check your email
</Typography>
<div className={s.iconContainer}>
<Email />
</div>
<Typography variant="body2" className={s.instructions}>
{message}
</Typography>
<Button fullWidth as={Link} to={'/sing-in'}>
Back to Sign in
</Button>
</Card>
)
}

View File

@@ -0,0 +1 @@
export * from './check-email'

View File

@@ -1 +1,5 @@
export * from './login-form'
export * from './check-email'
export * from './new-password'
export * from './recover-password'
export * from './sign-up'
export * from './sign-in'

View File

@@ -1 +0,0 @@
export * from './login-form'

View File

@@ -1,9 +0,0 @@
import { z } from 'zod'
export const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(3),
rememberMe: z.boolean().default(false),
})
export type LoginFormData = z.infer<typeof loginSchema>

View File

@@ -1,19 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react'
import { LoginForm } from './login-form'
import { LoginFormData } from './login-form.schema'
const meta = {
title: 'Auth/LoginForm',
component: LoginForm,
tags: ['autodocs'],
} satisfies Meta<typeof LoginForm>
export default meta
type Story = StoryObj<typeof meta>
export const Primary: Story = {
args: {
onSubmit: (data: LoginFormData) => console.log(data),
},
}

View File

@@ -1,36 +0,0 @@
import { DevTool } from '@hookform/devtools'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { LoginFormData, loginSchema } from './login-form.schema'
import { Button, TextField, ControlledCheckbox } from '@/components'
type Props = {
onSubmit: (data: LoginFormData) => void
}
export const LoginForm = ({ onSubmit }: Props) => {
const {
control,
register,
handleSubmit,
formState: { errors },
} = useForm<LoginFormData>({
resolver: zodResolver(loginSchema),
})
return (
<form onSubmit={handleSubmit(onSubmit)}>
<DevTool control={control} />
<TextField {...register('email')} label={'email'} errorMessage={errors.email?.message} />
<TextField
{...register('password')}
label={'password'}
errorMessage={errors.password?.message}
/>
<ControlledCheckbox label={'remember me'} control={control} name={'rememberMe'} />
<Button type="submit">Submit</Button>
</form>
)
}

View File

@@ -0,0 +1 @@
export * from './new-password'

View File

@@ -0,0 +1,38 @@
.card {
max-width: 413px;
padding: 33px 36px 60px;
}
.form {
display: flex;
flex-direction: column;
gap: 24px;
width: 100%;
margin-bottom: 26px;
}
.title {
margin-bottom: 51px;
text-align: center;
}
.input {
margin-bottom: 19px;
}
.instructions {
margin-bottom: 41px;
color: var(--color-light-900);
opacity: 0.5;
}
.caption {
margin-bottom: 11px;
text-align: center;
}
.signInLink {
display: flex;
justify-content: center;
}

View File

@@ -0,0 +1,18 @@
import type { Meta, StoryObj } from '@storybook/react'
import { NewPassword } from './'
const meta = {
title: 'Auth/New password',
component: NewPassword,
tags: ['autodocs'],
} satisfies Meta<typeof NewPassword>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
onSubmit: data => console.info(data),
},
}

View File

@@ -0,0 +1,55 @@
import { DevTool } from '@hookform/devtools'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { Button, Card, ControlledTextField, Typography } from '../../ui'
import s from './new-password.module.scss'
const schema = z.object({
password: z.string().nonempty('Enter password'),
})
type FormType = z.infer<typeof schema>
type Props = {
onSubmit: (data: FormType) => void
}
export const NewPassword = (props: Props) => {
const { control, handleSubmit } = useForm<FormType>({
resolver: zodResolver(schema),
defaultValues: {
password: '',
},
})
const handleFormSubmitted = handleSubmit(props.onSubmit)
return (
<>
<DevTool control={control} />
<Card className={s.card}>
<Typography variant="large" className={s.title}>
Create new password
</Typography>
<form onSubmit={handleFormSubmitted}>
<ControlledTextField
placeholder={'Password'}
name={'password'}
control={control}
type={'password'}
containerProps={{ className: s.input }}
/>
<Typography variant="caption" className={s.instructions}>
Create new password and we will send you further instructions to email
</Typography>
<Button fullWidth type={'submit'}>
Create new password
</Button>
</form>
</Card>
</>
)
}

View File

@@ -0,0 +1 @@
export * from './recover-password'

View File

@@ -0,0 +1,39 @@
.card {
max-width: 413px;
padding: 33px 36px 29px;
}
.form {
width: 100%;
margin-bottom: 19px;
}
.title {
margin-bottom: 50px;
text-align: center;
}
.button {
margin-bottom: 31px;
}
.instructions {
margin-bottom: 65px;
color: var(--color-light-900);
opacity: 0.5;
}
.caption {
margin-bottom: 11px;
color: var(--color-light-900);
text-align: center;
}
.loginLink {
display: flex;
justify-content: center;
font-size: var(--font-size-m);
font-weight: var(--font-weight-bold);
color: var(--color-accent-500);
}

View File

@@ -0,0 +1,18 @@
import type { Meta, StoryObj } from '@storybook/react'
import { RecoverPassword } from './'
const meta = {
title: 'Auth/Recover password',
component: RecoverPassword,
tags: ['autodocs'],
} satisfies Meta<typeof RecoverPassword>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
onSubmit: data => console.info(data),
},
}

View File

@@ -0,0 +1,59 @@
import { DevTool } from '@hookform/devtools'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { Link } from 'react-router-dom'
import { z } from 'zod'
import { Button, Card, ControlledTextField, Typography } from '../../ui'
import s from './recover-password.module.scss'
const schema = z.object({
email: z.string().email('Invalid email address').nonempty('Enter email'),
})
type FormType = z.infer<typeof schema>
type Props = {
onSubmit: (data: FormType) => void
}
export const RecoverPassword = (props: Props) => {
const { control, handleSubmit } = useForm<FormType>({
mode: 'onSubmit',
resolver: zodResolver(schema),
defaultValues: {
email: '',
},
})
const handleFormSubmitted = handleSubmit(props.onSubmit)
return (
<>
<DevTool control={control} />
<Card className={s.card}>
<Typography variant="large" className={s.title}>
Forgot your password?
</Typography>
<form onSubmit={handleFormSubmitted}>
<div className={s.form}>
<ControlledTextField placeholder={'Email'} name={'email'} control={control} />
</div>
<Typography variant="body2" className={s.instructions}>
Enter your email address and we will send you further instructions
</Typography>
<Button className={s.button} fullWidth type={'submit'}>
Send Instructions
</Button>
</form>
<Typography variant="body2" className={s.caption}>
Did you remember your password?
</Typography>
<Typography variant="link1" as={Link} to="/sign-in" className={s.loginLink}>
Try logging in
</Typography>
</Card>
</>
)
}

View File

@@ -0,0 +1 @@
export * from './sign-in'

View File

@@ -0,0 +1,55 @@
.card {
width: 100%;
max-width: 413px;
padding: 33px 36px 29px;
}
.form {
display: flex;
flex-direction: column;
gap: 24px;
width: 100%;
margin-bottom: 12px;
}
.checkbox {
margin-bottom: 6px;
}
.title {
margin-bottom: 27px;
text-align: center;
}
.recoverPasswordLink,
.recoverPasswordLink:visited {
display: flex;
justify-content: flex-end;
width: 100%;
margin-bottom: 66px;
color: inherit;
text-align: right;
text-decoration: none;
}
.button {
margin-bottom: 20px;
}
.caption {
margin-bottom: 11px;
color: var(--color-light-900);
text-align: center;
}
.signUpLink {
display: flex;
justify-content: center;
font-size: var(--font-size-m);
font-weight: var(--font-weight-bold);
color: var(--color-accent-500);
}

View File

@@ -0,0 +1,18 @@
import type { Meta, StoryObj } from '@storybook/react'
import { SignIn } from './'
const meta = {
title: 'Auth/Sign in',
component: SignIn,
tags: ['autodocs'],
} satisfies Meta<typeof SignIn>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
onSubmit: data => console.info(data),
},
}

View File

@@ -0,0 +1,87 @@
import { DevTool } from '@hookform/devtools'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { Link } from 'react-router-dom'
import { z } from 'zod'
import { Button, Card, ControlledCheckbox, ControlledTextField, Typography } from '../../ui'
import s from './sign-in.module.scss'
const schema = z.object({
email: z.string().email('Invalid email address').nonempty('Enter email'),
password: z.string().nonempty('Enter password'),
rememberMe: z.boolean().optional(),
})
type FormType = z.infer<typeof schema>
type Props = {
onSubmit: (data: FormType) => void
}
export const SignIn = (props: Props) => {
const { control, handleSubmit } = useForm<FormType>({
mode: 'onSubmit',
resolver: zodResolver(schema),
defaultValues: {
email: '',
password: '',
rememberMe: false,
},
})
const handleFormSubmitted = handleSubmit(props.onSubmit)
return (
<>
<DevTool control={control} />
<Card className={s.card}>
<Typography variant="large" className={s.title}>
Sign In
</Typography>
<form onSubmit={handleFormSubmitted}>
<div className={s.form}>
<ControlledTextField
placeholder={'Email'}
label={'Email'}
name={'email'}
control={control}
/>
<ControlledTextField
placeholder={'Password'}
label={'Password'}
type={'password'}
name={'password'}
control={control}
/>
</div>
<ControlledCheckbox
className={s.checkbox}
label={'Remember me'}
control={control}
name={'rememberMe'}
position={'left'}
/>
<Typography
variant="body2"
as={Link}
to="/recover-password"
className={s.recoverPasswordLink}
>
Forgot Password?
</Typography>
<Button className={s.button} fullWidth type={'submit'}>
Sign In
</Button>
</form>
<Typography className={s.caption} variant="body2">
{`Don't have an account?`}
</Typography>
<Typography variant="link1" as={Link} to="/sign-up" className={s.signUpLink}>
Sign Up
</Typography>
</Card>
</>
)
}

View File

@@ -0,0 +1 @@
export * from './sign-up'

View File

@@ -0,0 +1,38 @@
.card {
width: 100%;
max-width: 413px;
padding: 33px 36px 29px;
}
.form {
display: flex;
flex-direction: column;
gap: 24px;
width: 100%;
margin-bottom: 60px;
}
.title {
margin-bottom: 27px;
text-align: center;
}
.button {
margin-bottom: 20px;
}
.caption {
margin-bottom: 11px;
color: var(--color-light-900);
text-align: center;
}
.signInLink {
display: flex;
justify-content: center;
font-size: var(--font-size-m);
font-weight: var(--font-weight-bold);
color: var(--color-accent-500);
}

View File

@@ -0,0 +1,18 @@
import type { Meta, StoryObj } from '@storybook/react'
import { SignUp } from './'
const meta = {
title: 'Auth/Sign up',
component: SignUp,
tags: ['autodocs'],
} satisfies Meta<typeof SignUp>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
onSubmit: data => console.info(data),
},
}

View File

@@ -0,0 +1,95 @@
import { DevTool } from '@hookform/devtools'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { Link } from 'react-router-dom'
import { omit } from 'remeda'
import { z } from 'zod'
import { Button, Card, ControlledTextField, Typography } from '../../ui'
import s from './sign-up.module.scss'
const schema = z
.object({
email: z.string().email('Invalid email address').nonempty('Enter email'),
password: z.string().nonempty('Enter password'),
passwordConfirmation: z.string().nonempty('Confirm your password'),
})
.superRefine((data, ctx) => {
if (data.password !== data.passwordConfirmation) {
ctx.addIssue({
message: 'Passwords do not match',
code: z.ZodIssueCode.custom,
path: ['passwordConfirmation'],
})
}
return data
})
type FormType = z.infer<typeof schema>
type Props = {
onSubmit: (data: Omit<FormType, 'passwordConfirmation'>) => void
}
export const SignUp = (props: Props) => {
const { control, handleSubmit } = useForm<FormType>({
mode: 'onSubmit',
resolver: zodResolver(schema),
defaultValues: {
email: '',
password: '',
passwordConfirmation: '',
},
})
const handleFormSubmitted = handleSubmit(data =>
props.onSubmit(omit(data, ['passwordConfirmation']))
)
return (
<>
<DevTool control={control} />
<Card className={s.card}>
<Typography variant="large" className={s.title}>
Sign Up
</Typography>
<form onSubmit={handleFormSubmitted}>
<div className={s.form}>
<ControlledTextField
label={'Email'}
placeholder={'Email'}
name={'email'}
control={control}
/>
<ControlledTextField
placeholder={'Password'}
label={'Password'}
type={'password'}
name={'password'}
control={control}
/>
<ControlledTextField
placeholder={'Confirm password'}
label={'Confirm password'}
type={'password'}
name={'passwordConfirmation'}
control={control}
/>
</div>
<Button className={s.button} fullWidth type={'submit'}>
Sign Up
</Button>
</form>
{/* eslint-disable-next-line react/no-unescaped-entities */}
<Typography variant="body2" className={s.caption}>
Already have an account?
</Typography>
<Typography variant="link1" as={Link} to="/sign-in" className={s.signInLink}>
Sign In
</Typography>
</Card>
</>
)
}