Apply react hook form for user page

This commit is contained in:
2023-11-03 17:30:34 +07:00
parent ae7c25e6f3
commit 32a65038f5
10 changed files with 404 additions and 577 deletions
+1
View File
@@ -30,6 +30,7 @@
"@emotion/is-prop-valid": "^1.2.1", "@emotion/is-prop-valid": "^1.2.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.47.0",
"react-hot-toast": "^2.4.1", "react-hot-toast": "^2.4.1",
"react-icons": "^4.11.0", "react-icons": "^4.11.0",
"react-router-dom": "^6.17.0", "react-router-dom": "^6.17.0",
+12
View File
@@ -14,6 +14,9 @@ dependencies:
react-dom: react-dom:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0(react@18.2.0) version: 18.2.0(react@18.2.0)
react-hook-form:
specifier: ^7.47.0
version: 7.47.0(react@18.2.0)
react-hot-toast: react-hot-toast:
specifier: ^2.4.1 specifier: ^2.4.1
version: 2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0) version: 2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0)
@@ -1815,6 +1818,15 @@ packages:
scheduler: 0.23.0 scheduler: 0.23.0
dev: false dev: false
/react-hook-form@7.47.0(react@18.2.0):
resolution: {integrity: sha512-F/TroLjTICipmHeFlMrLtNLceO2xr1jU3CyiNla5zdwsGUGu2UOxxR4UyJgLlhMwLW/Wzp4cpJ7CPfgJIeKdSg==}
engines: {node: '>=12.22.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18
dependencies:
react: 18.2.0
dev: false
/react-hot-toast@2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0): /react-hot-toast@2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==} resolution: {integrity: sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -1,3 +1,4 @@
import { FieldValues, RegisterOptions, useFormContext } from 'react-hook-form';
import { StyledSelect } from './styled'; import { StyledSelect } from './styled';
export interface ISelectOptions { export interface ISelectOptions {
@@ -6,20 +7,25 @@ export interface ISelectOptions {
} }
interface ISelect { interface ISelect {
options: ISelectOptions[]; options: ISelectOptions[];
value: string; optionsConfigForm?: RegisterOptions<FieldValues, string> | undefined;
onChange: React.ChangeEventHandler<HTMLSelectElement>; value?: number;
name?: string; id: string;
} }
const Select = ({ options, value, onChange, name }: ISelect) => { const Select = ({ options, id, optionsConfigForm }: ISelect) => {
const { register } = useFormContext() ?? {};
if(!register) {
return null;
}
if (!options) return; if (!options) return;
return ( return (
<StyledSelect <StyledSelect
value={value}
onChange={onChange}
aria-label="Sort" aria-label="Sort"
name={name} id={id}
{...register(id, optionsConfigForm)}
> >
{options.map((option) => ( {options.map((option) => (
<option value={option.value} key={option.value}> <option value={option.value} key={option.value}>
@@ -0,0 +1,6 @@
const INVALID_FIELD = 'This field is invalid format!';
const INVALID_PHONE = 'Phone number is invalid!';
const REQUIRED_FIELD_ERROR = 'This field is required!';
const INVALID_DISCOUNT = 'Discount should be greater than 0 and less than 100!';
export { REQUIRED_FIELD_ERROR, INVALID_DISCOUNT, INVALID_FIELD, INVALID_PHONE };
@@ -1,7 +1,3 @@
const invalidFormatMsg = (field: string) => {
return `Invalid ${field} format`;
};
const errorMsg = (errorCode: number, msg: string) => { const errorMsg = (errorCode: number, msg: string) => {
return `Error code: ${errorCode}. Message: ${msg}`; return `Error code: ${errorCode}. Message: ${msg}`;
}; };
@@ -10,17 +6,11 @@ const ADD_SUCCESS = 'Add success';
const EDIT_SUCCESS = 'Edit success'; const EDIT_SUCCESS = 'Edit success';
const CONFIRM_DELETE = 'Are you sure to delete it?'; const CONFIRM_DELETE = 'Are you sure to delete it?';
const DELETE_SUCCESS = 'Delete success'; const DELETE_SUCCESS = 'Delete success';
const REQUIRED_FIELD_ERROR = 'This is required field';
const DISCOUNT_FIELD_ERROR =
'Discount should be greater than 0 and less than 100';
export { export {
invalidFormatMsg,
ADD_SUCCESS, ADD_SUCCESS,
errorMsg, errorMsg,
EDIT_SUCCESS, EDIT_SUCCESS,
CONFIRM_DELETE, CONFIRM_DELETE,
DELETE_SUCCESS, DELETE_SUCCESS,
REQUIRED_FIELD_ERROR,
DISCOUNT_FIELD_ERROR,
}; };
+1 -1
View File
@@ -2,6 +2,6 @@ export const DASHBOARD = '/dashboard';
export const USER = '/user'; export const USER = '/user';
export const ROOM = '/room'; export const ROOM = '/room';
export const OTHER_PATH = '*'; export const OTHER_PATH = '*';
export const BASE_URL = 'https://hotel-management-api.loiphan.com/'; export const BASE_URL = 'http://localhost:3000/';
export const USER_PATH = 'users'; export const USER_PATH = 'users';
export const ROOM_PATH = 'rooms'; export const ROOM_PATH = 'rooms';
+1 -32
View File
@@ -1,5 +1,5 @@
// Constants // Constants
import { REQUIRED_FIELD_ERROR, invalidFormatMsg } from '../constants/messages'; import { REQUIRED_FIELD_ERROR } from '../constants/formValidateMessage';
// Types // Types
import { import {
@@ -54,36 +54,6 @@ const getPropValues = (stateSchema: TStateSchema, prop?: TPropValues) => {
}, {} as TKeyValue); }, {} as TKeyValue);
}; };
type TValidator = {
validatorFunc: (value: string) => boolean;
prop?: string;
customErrorMsg?: string;
required?: boolean;
};
/**
* Create validator object
* @param param0 Pass TValidator object
* @returns An object contains condition validator
*/
const addValidator = ({
validatorFunc,
prop = '',
customErrorMsg = '',
required = true,
}: TValidator) => {
return {
required,
validator: {
func: validatorFunc,
// prettier-ignore
error: customErrorMsg
? customErrorMsg
: invalidFormatMsg(prop),
},
};
};
/** /**
* Return the object contains values of object pass * Return the object contains values of object pass
* @param obj Object need to get value * @param obj Object need to get value
@@ -163,7 +133,6 @@ export {
isRequired, isRequired,
getPropValues, getPropValues,
getValueFromObj, getValueFromObj,
addValidator,
searchQuery, searchQuery,
REQUIRED_FIELD_ERROR, REQUIRED_FIELD_ERROR,
}; };
+245 -279
View File
@@ -1,47 +1,12 @@
import { useEffect, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import toast from 'react-hot-toast';
// Styled
import Input from '../../commons/styles/Input.ts';
import TextArea from '../../commons/styles/TextArea.ts';
// Components
import Form from '../../components/Form/index.tsx';
import FormRow from '../../components/LabelControl/index.tsx';
import Button from '../../commons/styles/Button.ts'; import Button from '../../commons/styles/Button.ts';
// Types // Types
import { import {
TKeyValue,
TRoom, TRoom,
TStateSchema,
TValidator,
} from '../../globals/types.ts'; } from '../../globals/types.ts';
// Hooks
import useForm from '../../hooks/useForm.ts';
// Utils
import {
isValidDiscount,
isValidNumber,
isValidString,
skipCheck,
} from '../../helpers/validators.ts';
import { addValidator, getValueFromObj } from '../../helpers/utils.ts';
import { sendRequest } from '../../helpers/sendRequest.ts';
// Constants
import { STATUS_CODE } from '../../constants/statusCode.ts';
import {
ADD_SUCCESS,
DISCOUNT_FIELD_ERROR,
EDIT_SUCCESS,
errorMsg,
} from '../../constants/messages.ts';
import { ROOM_PATH } from '../../constants/path.ts';
const FormBtn = styled(Button)` const FormBtn = styled(Button)`
width: 100%; width: 100%;
@@ -67,271 +32,272 @@ const RoomForm = ({
room, room,
isAdd, isAdd,
}: IRoomFormProp) => { }: IRoomFormProp) => {
const [reset, setReset] = useState(true); // const [reset, setReset] = useState(true);
if (isAdd) { // if (isAdd) {
// If this is add form, reset value // // If this is add form, reset value
room = null; // room = null;
} // }
// prettier-ignore // // prettier-ignore
const initialValue: string = isAdd // const initialValue: string = isAdd
? '' // ? ''
: room! // : room!
&& room.id.toString(); // && room.id.toString();
const { // const {
idValue, // idValue,
nameValue, // nameValue,
priceValue, // priceValue,
discountValue, // discountValue,
statusValue, // statusValue,
descriptionValue, // descriptionValue,
} = getValueFromObj<TRoom>(room); // } = getValueFromObj<TRoom>(room);
// Define your state schema // // Define your state schema
// prettier-ignore // // prettier-ignore
const stateSchema: TStateSchema = { // const stateSchema: TStateSchema = {
id: { // id: {
value: idValue || '' // value: idValue || ''
}, // },
name: { // name: {
value: nameValue || '', // value: nameValue || '',
error: '' // error: ''
}, // },
price: { // price: {
value: priceValue || '', // value: priceValue || '',
error: '' // error: ''
}, // },
discount: { // discount: {
value: discountValue || '', // value: discountValue || '',
error: '' // error: ''
}, // },
status: { // status: {
value: statusValue || '', // value: statusValue || '',
error: '' // error: ''
}, // },
description: { // description: {
value: descriptionValue || '', // value: descriptionValue || '',
error: '' // error: ''
}, // },
}; // };
// prettier-ignore // // prettier-ignore
const stateValidatorSchema: TValidator = { // const stateValidatorSchema: TValidator = {
name: addValidator({ // name: addValidator({
validatorFunc: isValidString, // validatorFunc: isValidString,
prop: 'name' // prop: 'name'
}), // }),
price: addValidator({ // price: addValidator({
validatorFunc: isValidNumber, // validatorFunc: isValidNumber,
prop: 'price', // prop: 'price',
}), // }),
discount: addValidator({ // discount: addValidator({
validatorFunc: isValidDiscount, // validatorFunc: isValidDiscount,
customErrorMsg: DISCOUNT_FIELD_ERROR // customErrorMsg: DISCOUNT_FIELD_ERROR
}), // }),
status: addValidator({ // status: addValidator({
validatorFunc: skipCheck, // validatorFunc: skipCheck,
prop: 'status', // prop: 'status',
required: false, // required: false,
}), // }),
description: addValidator({ // description: addValidator({
validatorFunc: isValidString, // validatorFunc: isValidString,
prop: 'description' // prop: 'description'
}), // }),
}; // };
// Submit form // // Submit form
const onSubmitForm = async (state: TKeyValue) => { // const onSubmitForm = async (state: TKeyValue) => {
// Convert to room type // // Convert to room type
const data: TRoom = { // const data: TRoom = {
id: +state.id!, // id: +state.id!,
name: '' + state.name, // name: '' + state.name,
discount: +state.discount!, // discount: +state.discount!,
price: +state.price!, // price: +state.price!,
status: !!state.status, // status: !!state.status,
description: '' + state.description, // description: '' + state.description,
}; // };
try { // try {
if (isAdd) { // if (isAdd) {
// Add request // // Add request
const response = await sendRequest( // const response = await sendRequest(
ROOM_PATH, // ROOM_PATH,
JSON.stringify(data), // JSON.stringify(data),
'POST', // 'POST',
); // );
if (response.statusCode === STATUS_CODE.CREATE) { // if (response.statusCode === STATUS_CODE.CREATE) {
toast.success(ADD_SUCCESS); // toast.success(ADD_SUCCESS);
onResetForm(); // onResetForm();
} else { // } else {
throw new Error(errorMsg(response.statusCode, response.msg)); // throw new Error(errorMsg(response.statusCode, response.msg));
} // }
} else { // } else {
// Edit request // // Edit request
const response = await sendRequest( // const response = await sendRequest(
ROOM_PATH + `/${room!.id}`, // ROOM_PATH + `/${room!.id}`,
JSON.stringify(data), // JSON.stringify(data),
'PUT', // 'PUT',
); // );
if (response.statusCode == STATUS_CODE.OK) { // if (response.statusCode == STATUS_CODE.OK) {
toast.success(EDIT_SUCCESS); // toast.success(EDIT_SUCCESS);
} else { // } else {
throw new Error(errorMsg(response.statusCode, response.msg)); // throw new Error(errorMsg(response.statusCode, response.msg));
} // }
} // }
// Reload table data // // Reload table data
setReload(!reload); // setReload(!reload);
} catch (error: unknown) { // } catch (error: unknown) {
if (error instanceof Error) { // if (error instanceof Error) {
toast.error(error.message); // toast.error(error.message);
} // }
} // }
onClose(); // onClose();
}; // };
// Close and reset form // // Close and reset form
const closeAndReset = () => { // const closeAndReset = () => {
onClose(); // onClose();
onResetForm(); // onResetForm();
}; // };
// prettier-ignore // // prettier-ignore
const { // const {
values, // values,
errors, // errors,
valid, // valid,
handleOnChange, // handleOnChange,
handleOnSubmit, // handleOnSubmit,
disable } = // disable } =
useForm( // useForm(
stateSchema, // stateSchema,
stateValidatorSchema, // stateValidatorSchema,
onSubmitForm, // onSubmitForm,
initialValue // initialValue
); // );
// prettier-ignore // // prettier-ignore
const { // const {
id, // id,
name, // name,
price, // price,
discount, // discount,
status, // status,
description // description
} = values; // } = values;
// Clear form value when this is a add form // // Clear form value when this is a add form
useEffect(() => { // useEffect(() => {
if (isAdd) { // if (isAdd) {
Object.keys(values).forEach((key) => (values[key] = '')); // Object.keys(values).forEach((key) => (values[key] = ''));
} // }
}, [isAdd]); // eslint-disable-line // }, [isAdd]); // eslint-disable-line
// Reset form // // Reset form
const onResetForm = () => { // const onResetForm = () => {
setReset(!reset); // setReset(!reset);
Object.keys(values).forEach((key) => (values[key] = '')); // Object.keys(values).forEach((key) => (values[key] = ''));
}; // };
return ( return (
<Form onSubmit={handleOnSubmit}> // <Form onSubmit={handleOnSubmit}>
<Input type="hidden" name="id" value={id as string} /> // <Input type="hidden" name="id" value={id as string} />
<FormRow // <FormRow
label="Name" // label="Name"
error={ // error={
// prettier-ignore // // prettier-ignore
errors.name && valid.name // errors.name && valid.name
? (errors.name as string) // ? (errors.name as string)
: '' // : ''
} // }
> // >
<Input // <Input
type="text" // type="text"
name="name" // name="name"
value={name as string} // value={name as string}
onChange={handleOnChange} // onChange={handleOnChange}
/> // />
</FormRow> // </FormRow>
<FormRow // <FormRow
label="Price" // label="Price"
error={ // error={
// prettier-ignore // // prettier-ignore
errors.price && valid.price // errors.price && valid.price
? (errors.price as string) // ? (errors.price as string)
: '' // : ''
} // }
> // >
<Input // <Input
type="text" // type="text"
name="price" // name="price"
value={price as string} // value={price as string}
onChange={handleOnChange} // onChange={handleOnChange}
/> // />
</FormRow> // </FormRow>
<FormRow // <FormRow
label="Discount" // label="Discount"
error={ // error={
// prettier-ignore // // prettier-ignore
errors.discount && valid.discount // errors.discount && valid.discount
? (errors.discount as string) // ? (errors.discount as string)
: '' // : ''
} // }
> // >
<Input // <Input
type="text" // type="text"
name="discount" // name="discount"
value={discount as string} // value={discount as string}
onChange={handleOnChange} // onChange={handleOnChange}
/> // />
</FormRow> // </FormRow>
<FormRow label="Status"> // <FormRow label="Status">
<Input // <Input
type="checkbox" // type="checkbox"
name="status" // name="status"
checked={status as boolean} // checked={status as boolean}
onChange={handleOnChange} // onChange={handleOnChange}
/> // />
</FormRow> // </FormRow>
<FormRow // <FormRow
label="Description" // label="Description"
error={ // error={
// prettier-ignore // // prettier-ignore
errors.description && valid.description // errors.description && valid.description
? (errors.description as string) // ? (errors.description as string)
: '' // : ''
} // }
> // >
<TextArea // <TextArea
name="description" // name="description"
rows={3} // rows={3}
value={description as string} // value={description as string}
onChange={handleOnChange} // onChange={handleOnChange}
/> // />
</FormRow> // </FormRow>
<Form.Action> // <Form.Action>
<FormBtn type="submit" name="submit" disabled={disable}> // <FormBtn type="submit" name="submit" disabled={disable}>
{ // {
// prettier-ignore // // prettier-ignore
isAdd // isAdd
? 'Add' // ? 'Add'
: 'Save' // : 'Save'
} // }
</FormBtn> // </FormBtn>
<FormBtn type="button" styled="secondary" onClick={closeAndReset}> // <FormBtn type="button" styled="secondary" onClick={closeAndReset}>
Close // Close
</FormBtn> // </FormBtn>
</Form.Action> // </Form.Action>
</Form> // </Form>
<p></p>
); );
}; };
+111 -247
View File
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import styled from 'styled-components';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { FormProvider, useForm } from 'react-hook-form';
// Styled // Styled
import Input from '../../commons/styles/Input'; import Input from '../../commons/styles/Input';
@@ -9,27 +9,17 @@ import TextArea from '../../commons/styles/TextArea';
// Components // Components
import Form from '../../components/Form'; import Form from '../../components/Form';
import FormRow from '../../components/LabelControl/index.tsx'; import FormRow from '../../components/LabelControl/index.tsx';
import Button from '../../commons/styles/Button.ts';
// Types // Types
import { import { TKeyValue, TUser } from '../../globals/types';
TKeyValue,
TStateSchema,
TUser,
TValidator,
} from '../../globals/types';
// Hooks
import useForm from '../../hooks/useForm';
// Utils // Utils
import { sendRequest } from '../../helpers/sendRequest.ts';
import { import {
isValidString,
isValidNumber, isValidNumber,
isValidPhoneNumber, isValidPhoneNumber,
} from '../../helpers/validators'; isValidString,
import { addValidator, getValueFromObj } from '../../helpers/utils.ts'; } from '../../helpers/validators.ts';
import { sendRequest } from '../../helpers/sendRequest.ts';
// Constants // Constants
import { STATUS_CODE } from '../../constants/statusCode.ts'; import { STATUS_CODE } from '../../constants/statusCode.ts';
@@ -40,17 +30,13 @@ import {
} from '../../constants/messages.ts'; } from '../../constants/messages.ts';
import { USER_PATH } from '../../constants/path.ts'; import { USER_PATH } from '../../constants/path.ts';
import Select, { ISelectOptions } from '../../components/Select'; import Select, { ISelectOptions } from '../../components/Select';
// Hooks
import { useFetch } from '../../hooks/useFetch.ts'; import { useFetch } from '../../hooks/useFetch.ts';
const FormBtn = styled(Button)` // Styled
width: 100%; import { FormBtn } from './styled.ts';
import { INVALID_FIELD, INVALID_PHONE, REQUIRED_FIELD_ERROR } from '../../constants/formValidateMessage.ts';
&:disabled,
&[disabled] {
background-color: var(--disabled-btn-color);
cursor: no-drop;
}
`;
interface IUserFormProp { interface IUserFormProp {
onClose: () => void; onClose: () => void;
@@ -67,10 +53,27 @@ const UserForm = ({
user, user,
isAdd, isAdd,
}: IUserFormProp) => { }: IUserFormProp) => {
const [reset, setReset] = useState(true); const formMethods = useForm<TUser>({
defaultValues: {
roomId: 1,
},
});
const {
register,
handleSubmit,
reset,
formState: { errors, isDirty, isValid },
trigger,
} = formMethods;
const [options, setOptions] = useState<ISelectOptions[]>(); const [options, setOptions] = useState<ISelectOptions[]>();
const { data, errorFetchMsg } = useFetch('rooms'); const { data, errorFetchMsg } = useFetch('rooms');
useEffect(() => {
if (user) {
reset(user);
}
}, [reset, user]);
useEffect(() => { useEffect(() => {
if (data) { if (data) {
const tempData = data as TKeyValue[]; const tempData = data as TKeyValue[];
@@ -90,95 +93,15 @@ const UserForm = ({
} }
}, [data, errorFetchMsg]); }, [data, errorFetchMsg]);
if (isAdd) {
// If this is add form, reset value
user = null;
}
// prettier-ignore
const initialValue: string = isAdd
? ''
: user!
&& user.id.toLocaleString();
const {
idValue,
nameValue,
identifiedCodeValue,
phoneValue,
roomIdValue,
addressValue,
} = getValueFromObj<TUser>(user);
// Define your state schema
// prettier-ignore
const stateSchema: TStateSchema = {
id: { value: idValue || '' },
name: {
value: nameValue || '',
error: '' ,
},
identifiedCode: {
value: identifiedCodeValue || '',
error: '',
},
phone: {
value: phoneValue || '',
error: '',
},
roomId: {
value: roomIdValue || '' + (options && options[0].value),
error: '' ,
},
address: {
value: addressValue || '',
error: ''
},
};
// prettier-ignore
const stateValidatorSchema: TValidator = {
name: addValidator({
validatorFunc: isValidString,
prop: 'full name'
}),
identifiedCode: addValidator({
validatorFunc: isValidNumber,
prop: 'identified code',
}),
phone: addValidator({
validatorFunc: isValidPhoneNumber,
prop: 'phone number',
}),
roomId: addValidator({
validatorFunc: isValidNumber,
prop: 'room number'
}),
address: addValidator({
validatorFunc: isValidString,
prop: 'address'
}),
};
// Submit form // Submit form
const onSubmitForm = async (state: TKeyValue) => { const onSubmit = async (user: TUser) => {
// Convert to user type
const user: TUser = {
id: +state.id!,
name: '' + state.name,
identifiedCode: '' + state.identifiedCode,
phone: '' + state.phone,
roomId: +state.roomId!,
address: '' + state.address,
};
try { try {
if (isAdd) { if (isAdd) {
// Add request // Add request
const response = await sendRequest( const response = await sendRequest(
USER_PATH, USER_PATH,
JSON.stringify(user), JSON.stringify(user),
'POST', 'POST'
); );
if (response.statusCode === STATUS_CODE.CREATE) { if (response.statusCode === STATUS_CODE.CREATE) {
@@ -188,14 +111,12 @@ const UserForm = ({
} }
// TODO Update status when user create // TODO Update status when user create
onResetForm();
} else { } else {
// Edit request // Edit request
const response = await sendRequest( const response = await sendRequest(
USER_PATH + `/${user!.id}`, USER_PATH + `/${user!.id}`,
JSON.stringify(data), JSON.stringify(user),
'PUT', 'PUT'
); );
if (response.statusCode == STATUS_CODE.OK) { if (response.statusCode == STATUS_CODE.OK) {
@@ -212,153 +133,96 @@ const UserForm = ({
} }
} }
reset();
onClose(); onClose();
}; };
// Close and reset form
const closeAndReset = () => {
onClose();
onResetForm();
};
// prettier-ignore
const {
values,
errors,
valid,
handleOnChange,
handleOnSubmit,
disable } =
useForm(
stateSchema,
stateValidatorSchema,
onSubmitForm,
initialValue
);
// prettier-ignore
const {
id,
name,
identifiedCode,
phone,
roomId,
address
} = values;
useEffect(() => {
if (isAdd) {
Object.keys(values).forEach((key) => (values[key] = ''));
}
}, [isAdd]); // eslint-disable-line
// Reset form
const onResetForm = () => {
setReset(!reset);
Object.keys(values).forEach((key) => (values[key] = ''));
};
return ( return (
<Form onSubmit={handleOnSubmit}> <FormProvider {...formMethods}>
<Input type="hidden" name="id" value={id as string} /> <Form onSubmit={handleSubmit(onSubmit)}>
<FormRow <Input type="hidden" id="id" {...register('id')} />
label="Full Name" <FormRow label="Full Name" error={errors?.name?.message}>
error={ <Input
// prettier-ignore type="text"
errors.name && valid.name id="name"
? (errors.name as string) {...register('name', {
: '' required: REQUIRED_FIELD_ERROR,
} validate: {
> checkValidName: (value) =>
<Input isValidString(value) || INVALID_FIELD,
type="text" },
name="name" onChange: () => trigger('name'),
value={name as string} })}
onChange={handleOnChange} />
/> </FormRow>
</FormRow>
<FormRow <FormRow
label="Identified Code" label="Identified Code"
error={ error={errors?.identifiedCode?.message}
errors.identifiedCode && valid.identifiedCode >
? (errors.identifiedCode as string) <Input
: '' type="text"
} id="identifiedCode"
> {...register('identifiedCode', {
<Input required: REQUIRED_FIELD_ERROR,
type="text" validate: {
name="identifiedCode" checkIdentifiedCode: (v) =>
value={identifiedCode as string} isValidNumber(v) || INVALID_FIELD,
onChange={handleOnChange} },
/> onChange: () => trigger('identifiedCode'),
</FormRow> })}
/>
</FormRow>
<FormRow <FormRow label="Phone" error={errors?.phone?.message}>
label="Phone" <Input
error={ type="text"
// prettier-ignore id="phone"
errors.phone && valid.phone {...register('phone', {
? (errors.phone as string) required: REQUIRED_FIELD_ERROR,
: '' validate: {
} checkPhoneNum: (v) =>
> isValidPhoneNumber(v) || INVALID_PHONE,
<Input },
type="text" onChange: () => trigger('phone'),
name="phone" })}
value={phone as string} />
onChange={handleOnChange} </FormRow>
/>
</FormRow>
<FormRow <FormRow label="Room">
label="Room" <Select id="roomId" options={options!} />
error={ </FormRow>
// prettier-ignore
errors.roomId && valid.roomId
? (errors.roomId as string)
: ''
}
>
<Select
name="roomId"
value={roomId as string}
onChange={handleOnChange}
options={options!}
/>
</FormRow>
<FormRow <FormRow label="Address" error={errors.address?.message}>
label="Address" <TextArea
error={ id="address"
// prettier-ignore rows={3}
errors.address && valid.address {...register('address', {
? (errors.address as string) required: REQUIRED_FIELD_ERROR,
: '' validate: {
} checkValidString: (v) =>
> isValidString(v) || INVALID_FIELD,
<TextArea },
name="address" onChange: () => trigger('address'),
rows={3} })}
value={address as string} />
onChange={handleOnChange} </FormRow>
/>
</FormRow>
<Form.Action> <Form.Action>
<FormBtn type="submit" name="submit" disabled={disable}> <FormBtn type="submit" name="submit" disabled={!isDirty || !isValid}>
{ {
// prettier-ignore // prettier-ignore
isAdd isAdd
? 'Add' ? 'Add'
: 'Save' : 'Save'
} }
</FormBtn> </FormBtn>
<FormBtn type="button" styled="secondary" onClick={closeAndReset}> <FormBtn type="button" styled="secondary">
Close Close
</FormBtn> </FormBtn>
</Form.Action> </Form.Action>
</Form> </Form>
</FormProvider>
); );
}; };
+14 -1
View File
@@ -1,5 +1,8 @@
import styled from 'styled-components'; import styled from 'styled-components';
// Components
import Button from '../../commons/styles/Button';
const StyledUser = styled.main` const StyledUser = styled.main`
padding: 20px; padding: 20px;
padding-bottom: 100px; padding-bottom: 100px;
@@ -21,4 +24,14 @@ const StyledOperationTable = styled.div`
justify-content: flex-end; justify-content: flex-end;
`; `;
export { StyledUser, Title, StyledOperationTable }; const FormBtn = styled(Button)`
width: 100%;
&:disabled,
&[disabled] {
background-color: var(--disabled-btn-color);
cursor: no-drop;
}
`;
export { StyledUser, Title, StyledOperationTable, FormBtn };