Skip to content

Commit afa8f9c

Browse files
authored
feat: Create forms (#13)
* feat: move generators * feat: install react-hook-form * feat: add TextArea * feat: start ProfileForm * feat: impruve type of Btn * feat: add profile form * feat: add AddGoodForm * feat: add auth form * feat: add err message
1 parent 7600c88 commit afa8f9c

29 files changed

+514
-16
lines changed

package-lock.json

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"nanoid": "^5.0.9",
7676
"react": "^18.2.0",
7777
"react-dom": "^18.2.0",
78+
"react-hook-form": "^7.54.2",
7879
"react-i18next": "^15.4.0",
7980
"react-router": "^7.1.1"
8081
}

src/app/i18n/translation.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,40 @@ export const resources = {
5050
isClosed: 'Show more',
5151
isOpened: 'Hide',
5252
},
53+
forms: {
54+
name: {
55+
label: 'Name',
56+
placeholder: 'Enter a name',
57+
requiredMessage: 'Please enter your name.',
58+
patternMessage: 'Invalid characters in the input field',
59+
},
60+
about: {
61+
label: 'About me',
62+
placeholder: 'Tell us about yourself',
63+
},
64+
submit: 'Submit',
65+
goods: {
66+
title: 'Title',
67+
details: 'Details',
68+
price: 'Price',
69+
},
70+
authReg: {
71+
login: {
72+
label: 'Username',
73+
placeholder: 'Enter your username',
74+
requiredMessage: 'Enter your username',
75+
patternMessage: '',
76+
},
77+
password: {
78+
label: 'Password',
79+
placeholder: 'Enter the password',
80+
requiredMessage: 'Enter the password',
81+
patternMessage: '',
82+
},
83+
submitReg: 'Register',
84+
submitAuth: 'Log in',
85+
},
86+
},
5387
},
5488
},
5589
ru: {
@@ -103,6 +137,40 @@ export const resources = {
103137
isClosed: 'Показать больше',
104138
isOpened: 'Скрыть',
105139
},
140+
forms: {
141+
name: {
142+
label: 'Имя',
143+
placeholder: 'Введите имя',
144+
requiredMessage: 'Пожалуйста, введите ваше имя',
145+
patternMessage: 'Недопустимые символы в поле ввода',
146+
},
147+
about: {
148+
label: 'Обо мне',
149+
placeholder: 'Расскажите о себе',
150+
},
151+
submit: 'Отправить',
152+
goods: {
153+
title: 'Название',
154+
details: 'Описание',
155+
price: 'Цена',
156+
},
157+
authReg: {
158+
login: {
159+
label: 'Логин',
160+
placeholder: 'Введите имя пользователя',
161+
requiredMessage: 'Введите имя пользователя',
162+
patternMessage: '',
163+
},
164+
password: {
165+
label: 'Пароль',
166+
placeholder: 'Введите пароль',
167+
requiredMessage: 'Введите пароль',
168+
patternMessage: '',
169+
},
170+
submitReg: 'Зарегистрироваться',
171+
submitAuth: 'Войти',
172+
},
173+
},
106174
},
107175
},
108176
};

src/assets/goods.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import generateAwailibleSizes from '../features/generateAwailibleSizes';
2-
import generateImageAddress from '../features/generateImageAddress';
3-
import generateRandomNumber from '../features/generateRandomNumber';
4-
import generateAwailibleColors from '../features/generateAwailibleColors';
1+
import {
2+
generateAwailibleSizes,
3+
generateImageAddress,
4+
generateRandomNumber,
5+
generateAwailibleColors,
6+
} from '../features/generators';
57
import { EGoodsSizes, IGoodsItem } from '../entities/interfaces';
68

79
export const goods: IGoodsItem[] = [

src/features/forms/AddGoodForm.tsx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React from 'react';
2+
import { useForm } from 'react-hook-form';
3+
import { useTranslation } from 'react-i18next';
4+
import clsx from 'clsx';
5+
import { goods } from '../../assets/goods';
6+
import { EThemeVariables, StoreContext } from '../../app/StoreContext';
7+
import { Btn } from '../../shared';
8+
import { IGoodsItem } from 'src/entities/interfaces';
9+
import { generateRandomNumber } from '../generators';
10+
11+
export const AddGoodForm: React.FC = () => {
12+
const {
13+
handleSubmit,
14+
register,
15+
reset,
16+
formState: { errors },
17+
} = useForm<IGoodsItem>();
18+
const [lastId, setLastId] = React.useState<number>(goods[goods.length - 1].id);
19+
const { t } = useTranslation();
20+
21+
const onSubmit = (data: Partial<IGoodsItem>): void => {
22+
setLastId(lastId + 1);
23+
console.log('Form :', data);
24+
reset();
25+
};
26+
27+
const { theme } = React.useContext(StoreContext);
28+
const isDarkTheme = theme === EThemeVariables.DARK;
29+
30+
const className = clsx(
31+
'w-full p-2 border-[1px] border-solid active:outline focus:outline rounded',
32+
'disabled:bg-w-100 txt-b-700',
33+
isDarkTheme
34+
? 'bg-b-900 border-b-100 active:outline-w-900 focus:outline-w-900'
35+
: 'bg-w-900 active:outline-b-900 border-b-200 focus:outline-b-900'
36+
);
37+
38+
return (
39+
<form name="add-good" className="grid gap-2 grid-cols-[100px_auto]" onSubmit={handleSubmit(onSubmit)}>
40+
<label htmlFor="id" className="pt-2">
41+
ID:
42+
</label>
43+
<input id="id" value={lastId} className={className} disabled />
44+
<label htmlFor="title" className="pt-2">
45+
{t('forms.goods.title')}
46+
</label>
47+
<input
48+
{...register('title', {
49+
value: `Some cool goods ${lastId}`,
50+
required: true,
51+
})}
52+
id="title"
53+
className={className}
54+
/>
55+
<label htmlFor="details" className="pt-2">
56+
{t('forms.goods.details')}
57+
</label>
58+
<textarea
59+
{...register('details', {
60+
value:
61+
'Curabitur tincidunt ex vel magna iaculis varius. Duis eleifend ligula vitae lectus cursus, eu luctus leo rutrum. ',
62+
required: true,
63+
})}
64+
className={className}
65+
name="details"
66+
required
67+
/>
68+
<label htmlFor="price" className="pt-2">
69+
{t('forms.goods.price')}
70+
</label>
71+
<input
72+
{...register('price', {
73+
value: generateRandomNumber(50, 250),
74+
required: true,
75+
})}
76+
id="price"
77+
className={className}
78+
/>
79+
<Btn type="submit">{t('forms.submit')}</Btn>
80+
</form>
81+
);
82+
};

src/features/forms/AuthRegForm.tsx

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import React, { useEffect } from 'react';
2+
import { useForm } from 'react-hook-form';
3+
import { useTranslation } from 'react-i18next';
4+
import clsx from 'clsx';
5+
import { EThemeVariables, StoreContext } from '../../app/StoreContext';
6+
import { Btn } from '../../shared';
7+
8+
type TFormData = {
9+
login: string;
10+
password: string;
11+
};
12+
13+
export enum EAuthRegVariables {
14+
registration = 'REG',
15+
authorization = 'AUTH',
16+
}
17+
18+
interface IAuthRegForm {
19+
type: EAuthRegVariables;
20+
}
21+
22+
export const AuthRegForm: React.FC<IAuthRegForm> = ({ type }) => {
23+
const {
24+
handleSubmit,
25+
register,
26+
reset,
27+
formState: { errors },
28+
} = useForm();
29+
30+
const { t } = useTranslation();
31+
32+
const onSubmit = (data: TFormData): void => {
33+
console.log('Form :', data);
34+
reset();
35+
};
36+
37+
const { theme } = React.useContext(StoreContext);
38+
const isDarkTheme = theme === EThemeVariables.DARK;
39+
40+
const className = clsx(
41+
'w-full p-2 border-[1px] border-solid active:outline focus:outline rounded',
42+
'disabled:bg-w-100 txt-b-700',
43+
isDarkTheme
44+
? 'bg-b-900 border-b-100 active:outline-w-900 focus:outline-w-900'
45+
: 'bg-w-900 active:outline-b-900 border-b-200 focus:outline-b-900'
46+
);
47+
48+
return (
49+
<form name="auth-reg" className="grid gap-2" onSubmit={handleSubmit(onSubmit)}>
50+
<label className="grid gap-2">
51+
{t('forms.authReg.login.label')}
52+
<div>
53+
<input
54+
{...register('login', {
55+
required: t('forms.authReg.login.requiredMessage'),
56+
pattern: {
57+
value: /^[A-Za-z]+$/g,
58+
message: t('forms.authReg.login.patternMessage'),
59+
},
60+
})}
61+
className={className}
62+
placeholder={t('forms.authReg.login.placeholder')}
63+
name="login"
64+
required
65+
form="auth-reg"
66+
/>
67+
{errors.login && <p className="text-red-600">{`${errors.login.message}`}</p>}
68+
</div>
69+
</label>
70+
<label className="grid gap-2">
71+
{t('forms.authReg.password.label')}
72+
<div>
73+
<input
74+
{...register('password', {
75+
required: t('forms.authReg.password.requiredMessage'),
76+
minLength: type === EAuthRegVariables.registration && {
77+
value: 8,
78+
message: t('forms.authReg.password.patternMessage'),
79+
},
80+
})}
81+
className={className}
82+
placeholder={t('forms.authReg.password.placeholder')}
83+
name="password"
84+
required
85+
form="auth-reg"
86+
/>
87+
{errors.password && <p className="text-red-600">{`${errors.password.message}`}</p>}
88+
</div>
89+
</label>
90+
91+
<Btn type="submit">
92+
{type === EAuthRegVariables.registration ? t('forms.authReg.submitReg') : t('forms.authReg.submitAuth')}
93+
</Btn>
94+
</form>
95+
);
96+
};

0 commit comments

Comments
 (0)