Fix all comments

This commit is contained in:
2023-11-05 18:28:51 +07:00
parent b0dd3abf0e
commit 3f95ce8343
39 changed files with 483 additions and 443 deletions
+53 -44
View File
@@ -1,60 +1,69 @@
import { forwardRef, useEffect } from 'react';
import {
Dispatch,
MutableRefObject,
SetStateAction,
forwardRef,
useEffect,
} from 'react';
// Components
import Dialog from '../../components/Dialog';
import RoomForm from './Form';
import { TRoom } from '../../globals/types';
// Types
import { Nullable, TRoom } from '../../globals/types';
// Hooks
import { useForwardRef } from '../../hooks/useForwardRef';
interface IRoomDialog {
onClose?: () => void;
reload?: boolean;
setReload?: React.Dispatch<React.SetStateAction<boolean>>;
ref?: React.MutableRefObject<HTMLDialogElement | null>;
room?: TRoom | null;
setReload?: Dispatch<SetStateAction<boolean>>;
ref?: MutableRefObject<Nullable<HTMLDialogElement>>;
room?: Nullable<TRoom>;
isAdd?: boolean;
}
const RoomDialog = forwardRef((props: IRoomDialog, ref) => {
const dialogRef = ref as React.MutableRefObject<
HTMLDialogElement | undefined
>;
// prettier-ignore
const {
onClose,
setReload,
reload,
room,
isAdd
} = props;
const RoomDialog = forwardRef<HTMLDialogElement, IRoomDialog>(
(props: IRoomDialog, ref) => {
const dialogRef = useForwardRef(ref);
const {
onClose,
setReload,
reload,
room,
isAdd
} = props;
useEffect(() => {
if (dialogRef.current) {
dialogRef.current.addEventListener('click', (e: MouseEvent) => {
const dialogDimensions = dialogRef.current!.getBoundingClientRect();
if (
e.clientX < dialogDimensions.left ||
e.clientX > dialogDimensions.right ||
e.clientY < dialogDimensions.top ||
e.clientY > dialogDimensions.bottom
) {
dialogRef.current!.close();
}
});
}
}, [dialogRef]);
useEffect(() => {
if (dialogRef.current) {
dialogRef.current.addEventListener('click', (e: MouseEvent) => {
const dialogDimensions = dialogRef.current!.getBoundingClientRect();
if (
e.clientX < dialogDimensions.left ||
e.clientX > dialogDimensions.right ||
e.clientY < dialogDimensions.top ||
e.clientY > dialogDimensions.bottom
) {
dialogRef.current!.close();
}
});
}
}, [dialogRef]);
return (
<Dialog title={'Add room'} onClose={onClose} ref={dialogRef}>
<RoomForm
onClose={onClose!}
reload={reload!}
setReload={setReload!}
room={room}
isAdd={isAdd!}
/>
</Dialog>
);
});
return (
<Dialog title={'Add room'} onClose={onClose} ref={dialogRef}>
<RoomForm
onClose={onClose!}
reload={reload!}
setReload={setReload!}
room={room}
isAdd={isAdd!}
/>
</Dialog>
);
}
);
export default RoomDialog;
+95 -71
View File
@@ -1,20 +1,42 @@
import { Dispatch, SetStateAction, useEffect } from 'react';
import toast from 'react-hot-toast';
import styled from 'styled-components';
// Hooks
import { useForm } from 'react-hook-form';
// Styled
import Button from '../../commons/styles/Button.ts';
import Input from '../../commons/styles/Input.ts';
// Types
import { TRoom } from '../../globals/types.ts';
import { useForm } from 'react-hook-form';
import { Nullable, TRoom } from '../../globals/types.ts';
// Constants
import { ROOM_PATH } from '../../constants/path.ts';
import { STATUS_CODE } from '../../constants/responseStatus.ts';
import toast from 'react-hot-toast';
import { ADD_SUCCESS, EDIT_SUCCESS, errorMsg } from '../../constants/messages.ts';
import {
ADD_SUCCESS,
EDIT_SUCCESS,
errorMsg,
} from '../../constants/messages.ts';
import {
INVALID_DISCOUNT,
INVALID_FIELD,
REQUIRED_FIELD_ERROR,
} from '../../constants/formValidateMessage.ts';
// Helpers
import { sendRequest } from '../../helpers/sendRequest.ts';
import Input from '../../commons/styles/Input.ts';
import {
isValidDiscount,
isValidNumber,
isValidString,
} from '../../helpers/validators.ts';
// Components
import FormRow from '../../components/LabelControl/index.tsx';
import { INVALID_DISCOUNT, INVALID_FIELD, REQUIRED_FIELD_ERROR } from '../../constants/formValidateMessage.ts';
import { isValidDiscount, isValidNumber, isValidString } from '../../helpers/validators.ts';
import Form from '../../components/Form/index.tsx';
import { useEffect } from 'react';
const FormBtn = styled(Button)`
width: 100%;
@@ -29,8 +51,8 @@ const FormBtn = styled(Button)`
interface IRoomFormProp {
onClose: () => void;
reload: boolean;
setReload: React.Dispatch<React.SetStateAction<boolean>>;
room?: TRoom | null;
setReload: Dispatch<SetStateAction<boolean>>;
room?: Nullable<TRoom>;
isAdd: boolean;
}
@@ -51,20 +73,24 @@ const RoomForm = ({
} = formMethods;
useEffect(() => {
if(room) {
if (room) {
reset(room);
}
}, [room, reset]);
// Submit form
const onSubmit = async (room: TRoom) => {
// Calculate final price
room.finalPrice = room.price - (room.price * room.discount) / 100;
try {
if (isAdd) {
// Add request
const response = await sendRequest(
ROOM_PATH,
'POST',
JSON.stringify(room),
JSON.stringify(room)
);
if (response.statusCode === STATUS_CODE.CREATE) {
@@ -77,7 +103,7 @@ const RoomForm = ({
const response = await sendRequest(
ROOM_PATH + `/${room!.id}`,
'PUT',
JSON.stringify(room),
JSON.stringify(room)
);
if (response.statusCode == STATUS_CODE.OK) {
@@ -99,68 +125,66 @@ const RoomForm = ({
};
return (
<Form onSubmit={handleSubmit(onSubmit)}>
<Input type="hidden" id="id" {...register('id')} />
<FormRow label="Name" error={errors?.name?.message}>
<Input
type="text"
id="name"
{...register('name', {
required: REQUIRED_FIELD_ERROR,
validate: {
checkValidName: (value) =>
isValidString(value) || INVALID_FIELD,
},
onChange: () => trigger('name'),
})}
/>
</FormRow>
<Form onSubmit={handleSubmit(onSubmit)}>
<Input type="hidden" id="id" {...register('id')} />
<FormRow label="Name" error={errors?.name?.message}>
<Input
type="text"
id="name"
{...register('name', {
required: REQUIRED_FIELD_ERROR,
validate: {
checkValidName: (value) => isValidString(value) || INVALID_FIELD,
},
onChange: () => trigger('name'),
})}
/>
</FormRow>
<FormRow
label="Price"
error={errors?.price?.message}
>
<Input
type="text"
id="price"
{...register('price', {
required: REQUIRED_FIELD_ERROR,
validate: {
checkIdentifiedCode: (v) => isValidNumber(v) || INVALID_FIELD,
},
onChange: () => trigger('price'),
})}
/>
</FormRow>
<FormRow label="Price" error={errors?.price?.message}>
<Input
type="text"
id="price"
{...register('price', {
valueAsNumber: true,
required: REQUIRED_FIELD_ERROR,
validate: {
checkIdentifiedCode: (v) =>
isValidNumber(v.toString()) || INVALID_FIELD,
},
onChange: () => trigger('price'),
})}
/>
</FormRow>
<FormRow label="discount" error={errors?.discount?.message}>
<Input
type="text"
id="phone"
{...register('discount', {
required: REQUIRED_FIELD_ERROR,
validate: {
checkPhoneNum: (v) => isValidDiscount(v) || INVALID_DISCOUNT,
},
onChange: () => trigger('discount'),
})}
/>
</FormRow>
<FormRow label="discount" error={errors?.discount?.message}>
<Input
type="text"
id="phone"
{...register('discount', {
valueAsNumber: true,
required: REQUIRED_FIELD_ERROR,
validate: {
checkPhoneNum: (v) => isValidDiscount(v) || INVALID_DISCOUNT,
},
onChange: () => trigger('discount'),
})}
/>
</FormRow>
<Form.Action>
<FormBtn type="submit" name="submit" disabled={!isDirty || !isValid}>
{
// prettier-ignore
isAdd
? 'Add'
<Form.Action>
<FormBtn type="submit" name="submit" disabled={!isDirty || !isValid}>
{
isAdd
? 'Add'
: 'Save'
}
</FormBtn>
<FormBtn type="button" styled="secondary" onClick={onClose}>
Close
</FormBtn>
</Form.Action>
</Form>
}
</FormBtn>
<FormBtn type="button" styled="secondary" onClick={onClose}>
Close
</FormBtn>
</Form.Action>
</Form>
);
};
+12 -16
View File
@@ -1,5 +1,5 @@
import { useSearchParams } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import toast from 'react-hot-toast';
// Components
@@ -15,7 +15,7 @@ import SortBy from '../../components/SortBy';
import OrderBy from '../../components/OrderBy';
// Types
import { TRoom } from '../../globals/types';
import { Nullable, TRoom } from '../../globals/types';
// Constants
import { useFetch } from '../../hooks/useFetch';
@@ -27,16 +27,16 @@ import { ORDERBY_OPTIONS, ROOM_PAGE } from '../../constants/variables';
// Styled
import Spinner from '../../commons/styles/Spinner';
// Utils
// Helpers
import { sendRequest } from '../../helpers/sendRequest';
import { formatCurrency } from '../../helpers/utils';
interface IRoomRow {
room: TRoom;
openFormDialog: () => void;
setRoom: React.Dispatch<React.SetStateAction<TRoom | null>>;
setRoom: Dispatch<SetStateAction<Nullable<TRoom>>>;
reload: boolean;
setReload: React.Dispatch<React.SetStateAction<boolean>>;
setReload: Dispatch<SetStateAction<boolean>>;
}
const RoomRow = ({
@@ -46,12 +46,12 @@ const RoomRow = ({
reload,
setReload,
}: IRoomRow) => {
const handleOnEdit = (room: TRoom) => {
const handleEdit = (room: TRoom) => {
setRoom(room);
openFormDialog();
};
const handleOnDelete = async (room: TRoom) => {
const handleDelete = async (room: TRoom) => {
if (confirm(CONFIRM_DELETE)) {
const response = await sendRequest(ROOM_PATH + `/${room.id}`, 'DELETE');
@@ -64,12 +64,8 @@ const RoomRow = ({
}
};
const { id, name, price, discount, status } = room;
const { id, name, finalPrice, status } = room;
// Calculate final price
const finalPrice = price - (price * discount / 100);
// prettier-ignore
const statusText = status
? 'Unavailable'
: 'Available';
@@ -87,11 +83,11 @@ const RoomRow = ({
<Menus.List id={id.toString()}>
<Menus.Button
icon={<RiEditBoxFill />}
onClick={() => handleOnEdit(room)}
onClick={() => handleEdit(room)}
>
Edit
</Menus.Button>
<Menus.Button icon={<HiTrash />} onClick={() => handleOnDelete(room)}>
<Menus.Button icon={<HiTrash />} onClick={() => handleDelete(room)}>
Delete
</Menus.Button>
</Menus.List>
@@ -102,9 +98,9 @@ const RoomRow = ({
interface IRoomTable {
reload: boolean;
setReload: React.Dispatch<React.SetStateAction<boolean>>;
setReload: Dispatch<SetStateAction<boolean>>;
openFormDialog: () => void;
setRoom?: React.Dispatch<React.SetStateAction<TRoom | null>>;
setRoom?: Dispatch<SetStateAction<Nullable<TRoom>>>;
}
const RoomTable = ({
+5 -5
View File
@@ -1,21 +1,21 @@
import { useRef, useState } from 'react';
// Components
import Direction from '../../commons/styles/Direction';
import Button from '../../commons/styles/Button';
import RoomTable from './Table';
// Styled
import { StyledRoom, Title } from './styled';
import Direction from '../../commons/styles/Direction';
import Button from '../../commons/styles/Button';
import RoomDialog from './Dialog';
// Types
import { TRoom } from '../../globals/types';
import { Nullable, TRoom } from '../../globals/types';
const Room = () => {
const dialogRef = useRef<HTMLDialogElement>();
const dialogRef = useRef<HTMLDialogElement>(null);
const [reload, setReload] = useState(true);
const [room, setRoom] = useState<TRoom | null>(null);
const [room, setRoom] = useState<Nullable<TRoom>>(null);
const [isAdd, setIsAdd] = useState(false);
const openFormDialog = (isAddForm: boolean = false) => {
+15 -8
View File
@@ -1,26 +1,33 @@
import { forwardRef, useEffect } from 'react';
import {
Dispatch,
MutableRefObject,
SetStateAction,
forwardRef,
useEffect,
} from 'react';
// Components
import Dialog from '../../components/Dialog';
import UserForm from './Form';
// Types
import { TUser } from '../../globals/types';
import { Nullable, TUser } from '../../globals/types';
// Hooks
import { useForwardRef } from '../../hooks/useForwardRef';
interface IUserDialog {
onClose: () => void;
reload: boolean;
setReload: React.Dispatch<React.SetStateAction<boolean>>;
ref: React.MutableRefObject<HTMLDialogElement | null>;
user: TUser | null;
setReload: Dispatch<SetStateAction<boolean>>;
ref: MutableRefObject<Nullable<HTMLDialogElement>>;
user: Nullable<TUser>;
isAdd: boolean;
}
const UserDialog = forwardRef((props, ref) => {
const UserDialog = forwardRef<HTMLDialogElement, IUserDialog>((props, ref) => {
const dialogRef = useForwardRef(ref);
// prettier-ignore
const {
onClose,
setReload,
@@ -56,6 +63,6 @@ const UserDialog = forwardRef((props, ref) => {
/>
</Dialog>
);
}) as React.FC<IUserDialog>;
});
export default UserDialog;
+43 -32
View File
@@ -1,5 +1,13 @@
import { useCallback, useEffect, useState } from 'react';
import {
Dispatch,
SetStateAction,
useCallback,
useEffect,
useState,
} from 'react';
import toast from 'react-hot-toast';
// Hooks
import { FormProvider, useForm } from 'react-hook-form';
// Styled
@@ -8,14 +16,15 @@ import Input from '../../commons/styles/Input';
// Components
import Form from '../../components/Form';
import FormRow from '../../components/LabelControl/index.tsx';
import Select, { ISelectOptions } from '../../components/Select';
// Utils
// Helpers
import { sendRequest } from '../../helpers/sendRequest.ts';
import {
isEmptyObj,
isValidName,
isValidNumber,
isValidPhoneNumber,
isValidString,
} from '../../helpers/validators.ts';
// Constants
@@ -26,26 +35,27 @@ import {
errorMsg,
} from '../../constants/messages.ts';
import { USER_PATH } from '../../constants/path.ts';
import Select, { ISelectOptions } from '../../components/Select';
// Hooks
// Styled
import { FormBtn } from './styled.ts';
import {
INVALID_FIELD,
INVALID_PHONE,
REQUIRED_FIELD_ERROR,
} from '../../constants/formValidateMessage.ts';
import { getAllRoom, updateRoomStatus } from '../../services/roomServices.ts';
import { TRoom, TUser } from '../../globals/types.ts';
import { INIT_VALUE_USER_FORM } from '../../constants/variables.ts';
// Styled
import { FormBtn } from './styled.ts';
// Services
import { getAllRoom, updateRoomStatus } from '../../services/roomServices.ts';
// Types
import { Nullable, TRoom, TUser } from '../../globals/types.ts';
interface IUserFormProp {
onClose: () => void;
reload: boolean;
setReload: React.Dispatch<React.SetStateAction<boolean>>;
user: TUser | null;
setReload: Dispatch<SetStateAction<boolean>>;
user: Nullable<TUser>;
isAdd: boolean;
}
@@ -78,11 +88,13 @@ const UserForm = ({
rooms.forEach((item) => {
if (!item.status || tempUser?.roomId === item.id)
options.push({
label: item.name! as string,
label: item.name!,
value: item.id!.toString(),
});
});
}
if (options.length > 0) {
setOptions(options);
reset({ roomId: +options[0].value });
}
@@ -178,8 +190,7 @@ const UserForm = ({
{...register('name', {
required: REQUIRED_FIELD_ERROR,
validate: {
checkValidName: (value) =>
isValidString(value) || INVALID_FIELD,
checkValidName: (value) => isValidName(value) || INVALID_FIELD,
},
onChange: () => trigger('name'),
})}
@@ -196,7 +207,8 @@ const UserForm = ({
{...register('identifiedCode', {
required: REQUIRED_FIELD_ERROR,
validate: {
checkIdentifiedCode: (v) => isValidNumber(v) || INVALID_FIELD,
checkIdentifiedCode: (v) =>
isValidNumber(v.toString()) || INVALID_FIELD,
},
onChange: () => trigger('identifiedCode'),
})}
@@ -218,25 +230,24 @@ const UserForm = ({
</FormRow>
<FormRow label="Room">
<Select
id="roomId"
options={options!}
ariaLabel="RoomId"
optionsConfigForm={{
valueAsNumber: true,
onChange: () => trigger('roomId'),
}}
/>
{options && options.length > 0 ? (
<Select
id="roomId"
options={options!}
ariaLabel="RoomId"
optionsConfigForm={{
valueAsNumber: true,
onChange: () => trigger('roomId'),
}}
/>
) : (
<p>No room available!</p>
)}
</FormRow>
<Form.Action>
<FormBtn type="submit" name="submit" disabled={!isDirty || !isValid}>
{
// prettier-ignore
isAdd
? 'Add'
: 'Save'
}
{isAdd ? 'Add' : 'Save'}
</FormBtn>
<FormBtn type="button" styled="secondary" onClick={onClose}>
Close
+21 -18
View File
@@ -1,11 +1,10 @@
import { useSearchParams } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import toast from 'react-hot-toast';
// Components
import { RiEditBoxFill } from 'react-icons/ri';
import { IoExit } from 'react-icons/io5';
import { StyledOperationTable } from './styled';
import Menus from '../../components/Menus';
import Table from '../../components/Table';
import Direction from '../../commons/styles/Direction';
@@ -15,27 +14,30 @@ import SortBy from '../../components/SortBy';
import OrderBy from '../../components/OrderBy';
// Types
import { TUser } from '../../globals/types';
import { Nullable, TUser } from '../../globals/types';
// Hooks
import { useFetch } from '../../hooks/useFetch';
// Constants
import { useFetch } from '../../hooks/useFetch';
import { STATUS_CODE } from '../../constants/responseStatus';
import { ORDERBY_OPTIONS, USER_PAGE } from '../../constants/variables';
import { CONFIRM_MESSAGE } from '../../constants/messages';
// Styled
import { StyledOperationTable } from './styled';
import Spinner from '../../commons/styles/Spinner';
// Utils
// Services
import { updateRoomStatus } from '../../services/roomServices';
import { checkOutUser } from '../../services/userServices';
import { CONFIRM_MESSAGE } from '../../constants/messages';
interface IUserRow {
user: TUser;
openFormDialog: () => void;
setUser: React.Dispatch<React.SetStateAction<TUser | null>>;
setUser: Dispatch<SetStateAction<Nullable<TUser>>>;
reload: boolean;
setReload: React.Dispatch<React.SetStateAction<boolean>>;
setReload: Dispatch<SetStateAction<boolean>>;
}
const UserRow = ({
@@ -45,12 +47,12 @@ const UserRow = ({
reload,
setReload,
}: IUserRow) => {
const handleOnEdit = (user: TUser) => {
const handleEdit = (user: TUser) => {
setUser(user);
openFormDialog();
};
const handleOnCheckOut = async (user: TUser) => {
const handleCheckOut = async (user: TUser) => {
if (confirm(CONFIRM_MESSAGE)) {
const resUpdateStatus = await updateRoomStatus(user.roomId, false);
const resCheckoutUser = await checkOutUser(user);
@@ -75,7 +77,11 @@ const UserRow = ({
<div>{name}</div>
<div>{identifiedCode}</div>
<div>{phone}</div>
<div>{roomId ? roomId : 'None'}</div>
<div>{
roomId
? roomId
: 'None'
}</div>
<Menus.Menu>
<Menus.Toggle id={id.toString()} />
@@ -83,14 +89,11 @@ const UserRow = ({
<Menus.List id={id.toString()}>
<Menus.Button
icon={<RiEditBoxFill />}
onClick={() => handleOnEdit(user)}
onClick={() => handleEdit(user)}
>
Edit
</Menus.Button>
<Menus.Button
icon={<IoExit />}
onClick={() => handleOnCheckOut(user)}
>
<Menus.Button icon={<IoExit />} onClick={() => handleCheckOut(user)}>
Check out
</Menus.Button>
</Menus.List>
@@ -101,9 +104,9 @@ const UserRow = ({
interface IUserTable {
reload: boolean;
setReload: React.Dispatch<React.SetStateAction<boolean>>;
setReload: Dispatch<SetStateAction<boolean>>;
openFormDialog: () => void;
setUser?: React.Dispatch<React.SetStateAction<TUser | null>>;
setUser?: Dispatch<SetStateAction<Nullable<TUser>>>;
}
const UserTable = ({
+4 -4
View File
@@ -1,21 +1,21 @@
import { useRef, useState } from 'react';
// Components
import Direction from '../../commons/styles/Direction';
import Button from '../../commons/styles/Button';
import UserTable from './Table';
// Styled
import Button from '../../commons/styles/Button';
import Direction from '../../commons/styles/Direction';
import { StyledUser, Title } from './styled';
import UserDialog from './Dialog';
// Types
import { TUser } from '../../globals/types';
import { Nullable, TUser } from '../../globals/types';
const User = () => {
const dialogRef = useRef<HTMLDialogElement>(null);
const [reload, setReload] = useState(true);
const [user, setUser] = useState<TUser | null>(null);
const [user, setUser] = useState<Nullable<TUser>>(null);
const [isAdd, setIsAdd] = useState(false);
const openFormDialog = (isAddForm: boolean = false) => {