mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 20:01:59 +00:00
Apply modal instead of dialog in user pages
This commit is contained in:
@@ -1,29 +0,0 @@
|
|||||||
import { MutableRefObject, ReactNode, forwardRef } from 'react';
|
|
||||||
|
|
||||||
// Styled
|
|
||||||
import { StyledBody, StyledDialog, StyledTitle } from './styled';
|
|
||||||
|
|
||||||
// Type
|
|
||||||
import { Nullable } from '../../types/common';
|
|
||||||
|
|
||||||
export interface IDialogProps {
|
|
||||||
title?: string;
|
|
||||||
children?: ReactNode;
|
|
||||||
onClose?: () => void;
|
|
||||||
ref?: MutableRefObject<Nullable<HTMLDialogElement>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Dialog = forwardRef<HTMLDialogElement, IDialogProps>(
|
|
||||||
(props: IDialogProps, ref) => {
|
|
||||||
const { title, children, onClose } = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledDialog ref={ref} onClose={onClose}>
|
|
||||||
<StyledTitle>{title}</StyledTitle>
|
|
||||||
<StyledBody>{children}</StyledBody>
|
|
||||||
</StyledDialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Dialog;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const StyledDialog = styled.dialog`
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
border-color: var(--border-color);
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledTitle = styled.p`
|
|
||||||
font-size: var(--fs-md);
|
|
||||||
text-transform: capitalize;
|
|
||||||
font-weight: 600;
|
|
||||||
|
|
||||||
padding: 20px 40px;
|
|
||||||
border-bottom: 1px solid var(--border-color);
|
|
||||||
|
|
||||||
text-align: center;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledBody = styled.div`
|
|
||||||
padding: 10px 20px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export { StyledDialog, StyledTitle, StyledBody };
|
|
||||||
@@ -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 = 'http://localhost:3000/';
|
export const BASE_URL = 'https://hotel-management-api.loiphan.com/';
|
||||||
export const USER_PATH = 'users';
|
export const USER_PATH = 'users';
|
||||||
export const ROOM_PATH = 'rooms';
|
export const ROOM_PATH = 'rooms';
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
import {
|
|
||||||
Dispatch,
|
|
||||||
MutableRefObject,
|
|
||||||
SetStateAction,
|
|
||||||
forwardRef,
|
|
||||||
useEffect,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import Dialog from '../../components/Dialog';
|
|
||||||
import RoomForm from './Form';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { Nullable } from '../../types/common';
|
|
||||||
import { IRoom } from '../../types/rooms';
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
import { useForwardRef } from '../../hooks/useForwardRef';
|
|
||||||
|
|
||||||
interface IRoomDialog {
|
|
||||||
onClose?: () => void;
|
|
||||||
reload?: boolean;
|
|
||||||
setReload?: Dispatch<SetStateAction<boolean>>;
|
|
||||||
ref?: MutableRefObject<Nullable<HTMLDialogElement>>;
|
|
||||||
room?: Nullable<IRoom>;
|
|
||||||
isAdd?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog title={'Add room'} onClose={onClose} ref={dialogRef}>
|
|
||||||
<RoomForm
|
|
||||||
onClose={onClose!}
|
|
||||||
reload={reload!}
|
|
||||||
setReload={setReload!}
|
|
||||||
room={room}
|
|
||||||
isAdd={isAdd!}
|
|
||||||
/>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export default RoomDialog;
|
|
||||||
+1
-71
@@ -1,10 +1,7 @@
|
|||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
||||||
import toast from 'react-hot-toast';
|
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { RiEditBoxFill } from 'react-icons/ri';
|
|
||||||
import { HiTrash } from 'react-icons/hi';
|
|
||||||
import { StyledOperationTable } from './styled';
|
import { StyledOperationTable } from './styled';
|
||||||
import Menus from '../../components/Menus';
|
import Menus from '../../components/Menus';
|
||||||
import Table from '../../components/Table';
|
import Table from '../../components/Table';
|
||||||
@@ -19,78 +16,11 @@ import { IRoom } from '../../types/rooms';
|
|||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { useFetch } from '../../hooks/useFetch';
|
import { useFetch } from '../../hooks/useFetch';
|
||||||
import { ROOM_PATH } from '../../constants/path';
|
|
||||||
import { STATUS_CODE } from '../../constants/responseStatus';
|
|
||||||
import { CONFIRM_DELETE, DELETE_SUCCESS } from '../../constants/messages';
|
|
||||||
import { ORDERBY_OPTIONS, ROOM_PAGE } from '../../constants/variables';
|
import { ORDERBY_OPTIONS, ROOM_PAGE } from '../../constants/variables';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import Spinner from '../../commons/styles/Spinner';
|
import Spinner from '../../commons/styles/Spinner';
|
||||||
|
import RoomRow from './RoomRow';
|
||||||
// Helpers
|
|
||||||
import { sendRequest } from '../../helpers/sendRequest';
|
|
||||||
import { formatCurrency } from '../../helpers/helper';
|
|
||||||
import Modal from '../../components/Modal';
|
|
||||||
import RoomForm from './Form';
|
|
||||||
|
|
||||||
interface IRoomRow {
|
|
||||||
room: IRoom;
|
|
||||||
reload: boolean;
|
|
||||||
setReload: Dispatch<SetStateAction<boolean>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RoomRow = ({ room, reload, setReload }: IRoomRow) => {
|
|
||||||
const handleDelete = async (room: IRoom) => {
|
|
||||||
if (confirm(CONFIRM_DELETE)) {
|
|
||||||
const response = await sendRequest(ROOM_PATH + `/${room.id}`, 'DELETE');
|
|
||||||
|
|
||||||
if (response.statusCode === STATUS_CODE.OK) {
|
|
||||||
toast.success(DELETE_SUCCESS);
|
|
||||||
|
|
||||||
// Reload table
|
|
||||||
setReload(!reload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const { id, name, finalPrice, status } = room;
|
|
||||||
|
|
||||||
const statusText = status ? 'Unavailable' : 'Available';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table.Row>
|
|
||||||
<div>{id}</div>
|
|
||||||
<div>{name}</div>
|
|
||||||
<div>{formatCurrency(finalPrice)}</div>
|
|
||||||
<div>{statusText}</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Modal>
|
|
||||||
<Menus.Menu>
|
|
||||||
<Menus.Toggle id={id.toString()} />
|
|
||||||
|
|
||||||
<Menus.List id={id.toString()}>
|
|
||||||
<Modal.Open modalName="edit">
|
|
||||||
<Menus.Button icon={<RiEditBoxFill />}>Edit</Menus.Button>
|
|
||||||
</Modal.Open>
|
|
||||||
|
|
||||||
<Menus.Button
|
|
||||||
icon={<HiTrash />}
|
|
||||||
onClick={() => handleDelete(room)}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</Menus.Button>
|
|
||||||
</Menus.List>
|
|
||||||
|
|
||||||
<Modal.Window name="edit" title="Edit Room">
|
|
||||||
<RoomForm room={room} />
|
|
||||||
</Modal.Window>
|
|
||||||
</Menus.Menu>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
</Table.Row>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface IRoomTable {
|
interface IRoomTable {
|
||||||
reload: boolean;
|
reload: boolean;
|
||||||
+15
-17
@@ -10,8 +10,8 @@ import Button from '../../commons/styles/Button.ts';
|
|||||||
import Input from '../../commons/styles/Input.ts';
|
import Input from '../../commons/styles/Input.ts';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Nullable } from '../../types/common';
|
import { Nullable } from '../../types/common.ts';
|
||||||
import { IRoom } from '../../types/rooms';
|
import { IRoom } from '../../types/rooms.ts';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import {
|
import {
|
||||||
@@ -51,33 +51,31 @@ interface IRoomFormProp {
|
|||||||
onCloseModal?: () => void;
|
onCloseModal?: () => void;
|
||||||
reload?: boolean;
|
reload?: boolean;
|
||||||
setReload?: Dispatch<SetStateAction<boolean>>;
|
setReload?: Dispatch<SetStateAction<boolean>>;
|
||||||
room?: Nullable<IRoom>;
|
roomEdit?: Nullable<IRoom>;
|
||||||
isAdd?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const RoomForm = ({
|
const RoomForm = ({
|
||||||
onCloseModal,
|
onCloseModal,
|
||||||
reload,
|
reload,
|
||||||
setReload,
|
setReload,
|
||||||
room,
|
roomEdit,
|
||||||
isAdd,
|
|
||||||
}: IRoomFormProp) => {
|
}: IRoomFormProp) => {
|
||||||
const formMethods = useForm<IRoom>();
|
const formMethods = useForm<IRoom>();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
formState: { errors, isDirty, isValid },
|
formState: { errors, isDirty, isValid, isSubmitting },
|
||||||
trigger,
|
trigger,
|
||||||
} = formMethods;
|
} = formMethods;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (room && !isAdd) {
|
if (roomEdit) {
|
||||||
reset(room);
|
reset(roomEdit);
|
||||||
} else {
|
} else {
|
||||||
reset(INIT_VALUE_ROOM_FORM);
|
reset(INIT_VALUE_ROOM_FORM);
|
||||||
}
|
}
|
||||||
}, [room, reset, isAdd]);
|
}, [roomEdit, reset]);
|
||||||
|
|
||||||
// Submit form
|
// Submit form
|
||||||
const onSubmit = async (room: IRoom) => {
|
const onSubmit = async (room: IRoom) => {
|
||||||
@@ -85,7 +83,7 @@ const RoomForm = ({
|
|||||||
room.finalPrice = room.price - (room.price * room.discount) / 100;
|
room.finalPrice = room.price - (room.price * room.discount) / 100;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isAdd) {
|
if (!roomEdit) {
|
||||||
// Add request
|
// Add request
|
||||||
const response = await addRoom(room);
|
const response = await addRoom(room);
|
||||||
|
|
||||||
@@ -101,7 +99,7 @@ const RoomForm = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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);
|
||||||
@@ -109,7 +107,7 @@ const RoomForm = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
// onClose();
|
onCloseModal!();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -161,11 +159,11 @@ const RoomForm = ({
|
|||||||
</FormRow>
|
</FormRow>
|
||||||
|
|
||||||
<Form.Action>
|
<Form.Action>
|
||||||
<FormBtn type="submit" name="submit" disabled={!isDirty || !isValid}>
|
<FormBtn type="submit" name="submit" disabled={!isDirty || !isValid || isSubmitting}>
|
||||||
{
|
{
|
||||||
isAdd
|
!roomEdit
|
||||||
? 'Add'
|
? 'Add'
|
||||||
: 'Save'
|
: 'Save'
|
||||||
}
|
}
|
||||||
</FormBtn>
|
</FormBtn>
|
||||||
<FormBtn type="button" styled="secondary" onClick={onCloseModal}>
|
<FormBtn type="button" styled="secondary" onClick={onCloseModal}>
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
|
import toast from 'react-hot-toast';
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import { ROOM_PATH } from '../../constants/path';
|
||||||
|
import { STATUS_CODE } from '../../constants/responseStatus';
|
||||||
|
import { CONFIRM_DELETE, DELETE_SUCCESS } from '../../constants/messages';
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
import { sendRequest } from '../../helpers/sendRequest';
|
||||||
|
import { formatCurrency } from '../../helpers/helper';
|
||||||
|
import Modal from '../../components/Modal';
|
||||||
|
import RoomForm from './RoomForm';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { IRoom } from '../../types/rooms';
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import Table from '../../components/Table';
|
||||||
|
import Menus from '../../components/Menus';
|
||||||
|
import { RiEditBoxFill } from 'react-icons/ri';
|
||||||
|
import { HiTrash } from 'react-icons/hi';
|
||||||
|
|
||||||
|
interface IRoomRow {
|
||||||
|
room: IRoom;
|
||||||
|
reload: boolean;
|
||||||
|
setReload: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RoomRow = ({ room, reload, setReload }: IRoomRow) => {
|
||||||
|
const handleDelete = async (room: IRoom) => {
|
||||||
|
if (confirm(CONFIRM_DELETE)) {
|
||||||
|
const response = await sendRequest(ROOM_PATH + `/${room.id}`, 'DELETE');
|
||||||
|
|
||||||
|
if (response.statusCode === STATUS_CODE.OK) {
|
||||||
|
toast.success(DELETE_SUCCESS);
|
||||||
|
|
||||||
|
// Reload table
|
||||||
|
setReload(!reload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { id, name, finalPrice, status } = room;
|
||||||
|
|
||||||
|
const statusText = status ? 'Unavailable' : 'Available';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table.Row>
|
||||||
|
<div>{id}</div>
|
||||||
|
<div>{name}</div>
|
||||||
|
<div>{formatCurrency(finalPrice)}</div>
|
||||||
|
<div>{statusText}</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Modal>
|
||||||
|
<Menus.Menu>
|
||||||
|
<Menus.Toggle id={id.toString()} />
|
||||||
|
|
||||||
|
<Menus.List id={id.toString()}>
|
||||||
|
<Modal.Open modalName="edit">
|
||||||
|
<Menus.Button icon={<RiEditBoxFill />}>Edit</Menus.Button>
|
||||||
|
</Modal.Open>
|
||||||
|
|
||||||
|
<Menus.Button
|
||||||
|
icon={<HiTrash />}
|
||||||
|
onClick={() => handleDelete(room)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Menus.Button>
|
||||||
|
</Menus.List>
|
||||||
|
|
||||||
|
<Modal.Window name="edit" title="Edit Room">
|
||||||
|
<RoomForm roomEdit={room} setReload={setReload} reload={reload} />
|
||||||
|
</Modal.Window>
|
||||||
|
</Menus.Menu>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</Table.Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RoomRow;
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import RoomTable from './Table';
|
import RoomTable from './RomTable';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { StyledRoom, Title } from './styled';
|
import { StyledRoom, Title } from './styled';
|
||||||
@@ -10,7 +10,7 @@ import Button from '../../commons/styles/Button';
|
|||||||
|
|
||||||
// Types
|
// Types
|
||||||
import Modal from '../../components/Modal';
|
import Modal from '../../components/Modal';
|
||||||
import RoomForm from './Form';
|
import RoomForm from './RoomForm';
|
||||||
|
|
||||||
const Room = () => {
|
const Room = () => {
|
||||||
const [reload, setReload] = useState(true);
|
const [reload, setReload] = useState(true);
|
||||||
@@ -22,10 +22,10 @@ const Room = () => {
|
|||||||
<Title>List Room</Title>
|
<Title>List Room</Title>
|
||||||
|
|
||||||
<Modal>
|
<Modal>
|
||||||
<Modal.Open modalName="cabin-form">
|
<Modal.Open modalName="room-form">
|
||||||
<Button>Add room</Button>
|
<Button>Add room</Button>
|
||||||
</Modal.Open>
|
</Modal.Open>
|
||||||
<Modal.Window name="cabin-form" title="Add form">
|
<Modal.Window name="room-form" title="Add form">
|
||||||
<RoomForm setReload={setReload} reload={reload} />
|
<RoomForm setReload={setReload} reload={reload} />
|
||||||
</Modal.Window>
|
</Modal.Window>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
import {
|
|
||||||
Dispatch,
|
|
||||||
MutableRefObject,
|
|
||||||
SetStateAction,
|
|
||||||
forwardRef,
|
|
||||||
useEffect,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import Dialog from '../../components/Dialog';
|
|
||||||
import UserForm from './Form';
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { Nullable } from '../../types/common';
|
|
||||||
import { IUser } from '../../types/users';
|
|
||||||
|
|
||||||
// Hooks
|
|
||||||
import { useForwardRef } from '../../hooks/useForwardRef';
|
|
||||||
|
|
||||||
interface IUserDialog {
|
|
||||||
onClose: () => void;
|
|
||||||
reload: boolean;
|
|
||||||
setReload: Dispatch<SetStateAction<boolean>>;
|
|
||||||
ref: MutableRefObject<Nullable<HTMLDialogElement>>;
|
|
||||||
user: Nullable<IUser>;
|
|
||||||
isAdd: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UserDialog = forwardRef<HTMLDialogElement, IUserDialog>((props, ref) => {
|
|
||||||
const dialogRef = useForwardRef(ref);
|
|
||||||
|
|
||||||
const {
|
|
||||||
onClose,
|
|
||||||
setReload,
|
|
||||||
reload,
|
|
||||||
user,
|
|
||||||
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]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog title={'Add user'} onClose={onClose} ref={dialogRef}>
|
|
||||||
<UserForm
|
|
||||||
onClose={onClose!}
|
|
||||||
reload={reload!}
|
|
||||||
setReload={setReload!}
|
|
||||||
user={user}
|
|
||||||
isAdd={isAdd!}
|
|
||||||
/>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default UserDialog;
|
|
||||||
+16
-23
@@ -11,12 +11,12 @@ import toast from 'react-hot-toast';
|
|||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import Input from '../../commons/styles/Input';
|
import Input from '../../commons/styles/Input.ts';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Form from '../../components/Form';
|
import Form from '../../components/Form/index.tsx';
|
||||||
import FormRow from '../../components/LabelControl/index.tsx';
|
import FormRow from '../../components/LabelControl/index.tsx';
|
||||||
import Select, { ISelectOptions } from '../../components/Select';
|
import Select, { ISelectOptions } from '../../components/Select/index.tsx';
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
import {
|
import {
|
||||||
@@ -48,26 +48,19 @@ import { IRoom } from '../../types/rooms.ts';
|
|||||||
import { createUser, updateUser } from '../../services/userServices.ts';
|
import { createUser, updateUser } from '../../services/userServices.ts';
|
||||||
|
|
||||||
interface IUserFormProp {
|
interface IUserFormProp {
|
||||||
onClose: () => void;
|
onCloseModal?: () => void;
|
||||||
reload: boolean;
|
reload: boolean;
|
||||||
setReload: Dispatch<SetStateAction<boolean>>;
|
setReload: Dispatch<SetStateAction<boolean>>;
|
||||||
user: Nullable<IUser>;
|
user?: Nullable<IUser>;
|
||||||
isAdd: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserForm = ({
|
const UserForm = ({ onCloseModal, reload, setReload, user }: IUserFormProp) => {
|
||||||
onClose,
|
|
||||||
reload,
|
|
||||||
setReload,
|
|
||||||
user,
|
|
||||||
isAdd,
|
|
||||||
}: IUserFormProp) => {
|
|
||||||
const formMethods = useForm<IUser>();
|
const formMethods = useForm<IUser>();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
formState: { errors, isDirty, isValid },
|
formState: { errors, isDirty, isValid, isSubmitting },
|
||||||
trigger,
|
trigger,
|
||||||
} = formMethods;
|
} = formMethods;
|
||||||
const [rooms, setRooms] = useState<IRoom[]>([]);
|
const [rooms, setRooms] = useState<IRoom[]>([]);
|
||||||
@@ -77,8 +70,8 @@ const UserForm = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
const options: ISelectOptions[] = [];
|
const options: ISelectOptions[] = [];
|
||||||
const tempUser = isAdd
|
const tempUser = !user
|
||||||
? {}
|
? { roomId: 0 }
|
||||||
: { ...user };
|
: { ...user };
|
||||||
|
|
||||||
// Load and set default options room
|
// Load and set default options room
|
||||||
@@ -111,13 +104,13 @@ const UserForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
load();
|
load();
|
||||||
}, [reset, user, isAdd, rooms]);
|
}, [reset, user, rooms]);
|
||||||
|
|
||||||
// Submit form
|
// Submit form
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
async (newUser: IUser) => {
|
async (newUser: IUser) => {
|
||||||
try {
|
try {
|
||||||
if (isAdd) {
|
if (!user) {
|
||||||
// Add request
|
// Add request
|
||||||
const response = await createUser(newUser);
|
const response = await createUser(newUser);
|
||||||
|
|
||||||
@@ -147,9 +140,9 @@ const UserForm = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
onClose();
|
onCloseModal!();
|
||||||
},
|
},
|
||||||
[isAdd, onClose, reload, reset, setReload, user]
|
[onCloseModal, reload, reset, setReload, user]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Load all rooms
|
// Load all rooms
|
||||||
@@ -233,14 +226,14 @@ const UserForm = ({
|
|||||||
</FormRow>
|
</FormRow>
|
||||||
|
|
||||||
<Form.Action>
|
<Form.Action>
|
||||||
<FormBtn type="submit" name="submit" disabled={!isDirty || !isValid}>
|
<FormBtn type="submit" name="submit" disabled={!isDirty || !isValid || isSubmitting}>
|
||||||
{
|
{
|
||||||
isAdd
|
!user
|
||||||
? 'Add'
|
? 'Add'
|
||||||
: 'Save'
|
: 'Save'
|
||||||
}
|
}
|
||||||
</FormBtn>
|
</FormBtn>
|
||||||
<FormBtn type="button" styled="secondary" onClick={onClose}>
|
<FormBtn type="button" styled="secondary" onClick={onCloseModal}>
|
||||||
Close
|
Close
|
||||||
</FormBtn>
|
</FormBtn>
|
||||||
</Form.Action>
|
</Form.Action>
|
||||||
+2
-91
@@ -1,10 +1,7 @@
|
|||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
||||||
import toast from 'react-hot-toast';
|
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { RiEditBoxFill } from 'react-icons/ri';
|
|
||||||
import { IoExit } from 'react-icons/io5';
|
|
||||||
import Menus from '../../components/Menus';
|
import Menus from '../../components/Menus';
|
||||||
import Table from '../../components/Table';
|
import Table from '../../components/Table';
|
||||||
import Direction from '../../commons/styles/Direction';
|
import Direction from '../../commons/styles/Direction';
|
||||||
@@ -14,113 +11,29 @@ import SortBy from '../../components/SortBy';
|
|||||||
import OrderBy from '../../components/OrderBy';
|
import OrderBy from '../../components/OrderBy';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Nullable } from '../../types/common';
|
|
||||||
import { IUser } from '../../types/users';
|
import { IUser } from '../../types/users';
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
import { useFetch } from '../../hooks/useFetch';
|
import { useFetch } from '../../hooks/useFetch';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { STATUS_CODE } from '../../constants/responseStatus';
|
|
||||||
import { ORDERBY_OPTIONS, USER_PAGE } from '../../constants/variables';
|
import { ORDERBY_OPTIONS, USER_PAGE } from '../../constants/variables';
|
||||||
import { CONFIRM_MESSAGE, DENIED_ACTION } from '../../constants/messages';
|
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { StyledOperationTable } from './styled';
|
import { StyledOperationTable } from './styled';
|
||||||
import Spinner from '../../commons/styles/Spinner';
|
import Spinner from '../../commons/styles/Spinner';
|
||||||
|
import UserRow from './UserRow';
|
||||||
|
|
||||||
// Services
|
|
||||||
import { updateRoomStatus } from '../../services/roomServices';
|
|
||||||
import { checkOutUser } from '../../services/userServices';
|
|
||||||
|
|
||||||
interface IUserRow {
|
|
||||||
user: IUser;
|
|
||||||
openFormDialog: () => void;
|
|
||||||
setUser: Dispatch<SetStateAction<Nullable<IUser>>>;
|
|
||||||
reload: boolean;
|
|
||||||
setReload: Dispatch<SetStateAction<boolean>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UserRow = ({
|
|
||||||
user,
|
|
||||||
openFormDialog,
|
|
||||||
setUser,
|
|
||||||
reload,
|
|
||||||
setReload,
|
|
||||||
}: IUserRow) => {
|
|
||||||
const handleEdit = (user: IUser) => {
|
|
||||||
setUser(user);
|
|
||||||
openFormDialog();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCheckOut = async (user: IUser) => {
|
|
||||||
if(user.roomId !== 0) {
|
|
||||||
if (confirm(CONFIRM_MESSAGE)) {
|
|
||||||
const resUpdateStatus = await updateRoomStatus(user.roomId, false);
|
|
||||||
const resCheckoutUser = await checkOutUser(user);
|
|
||||||
|
|
||||||
if (
|
|
||||||
resCheckoutUser?.statusCode === STATUS_CODE.OK &&
|
|
||||||
resUpdateStatus?.statusCode === STATUS_CODE.OK
|
|
||||||
) {
|
|
||||||
toast.success('Check out complete!');
|
|
||||||
|
|
||||||
// Reload table
|
|
||||||
setReload(!reload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
toast.error(DENIED_ACTION);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const { id, name, identifiedCode, phone, roomId } = user;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table.Row>
|
|
||||||
<div>{id}</div>
|
|
||||||
<div>{name}</div>
|
|
||||||
<div>{identifiedCode}</div>
|
|
||||||
<div>{phone}</div>
|
|
||||||
<div>{
|
|
||||||
roomId
|
|
||||||
? roomId
|
|
||||||
: 'None'
|
|
||||||
}</div>
|
|
||||||
|
|
||||||
<Menus.Menu>
|
|
||||||
<Menus.Toggle id={id.toString()} />
|
|
||||||
|
|
||||||
<Menus.List id={id.toString()}>
|
|
||||||
<Menus.Button
|
|
||||||
icon={<RiEditBoxFill />}
|
|
||||||
onClick={() => handleEdit(user)}
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</Menus.Button>
|
|
||||||
<Menus.Button icon={<IoExit />} onClick={() => handleCheckOut(user)}>
|
|
||||||
Check out
|
|
||||||
</Menus.Button>
|
|
||||||
</Menus.List>
|
|
||||||
</Menus.Menu>
|
|
||||||
</Table.Row>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
interface IUserTable {
|
interface IUserTable {
|
||||||
reload: boolean;
|
reload: boolean;
|
||||||
setReload: Dispatch<SetStateAction<boolean>>;
|
setReload: Dispatch<SetStateAction<boolean>>;
|
||||||
openFormDialog: () => void;
|
|
||||||
setUser?: Dispatch<SetStateAction<Nullable<IUser>>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const UserTable = ({
|
const UserTable = ({ reload, setReload }: IUserTable) => {
|
||||||
reload,
|
|
||||||
setReload,
|
|
||||||
openFormDialog,
|
|
||||||
setUser,
|
|
||||||
}: IUserTable) => {
|
|
||||||
const [users, setUsers] = useState<IUser[]>([]);
|
const [users, setUsers] = useState<IUser[]>([]);
|
||||||
const [phoneSearch, setPhoneSearch] = useState('');
|
const [phoneSearch, setPhoneSearch] = useState('');
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
@@ -185,8 +98,6 @@ const UserTable = ({
|
|||||||
key={user.id}
|
key={user.id}
|
||||||
reload={reload}
|
reload={reload}
|
||||||
setReload={setReload}
|
setReload={setReload}
|
||||||
openFormDialog={openFormDialog}
|
|
||||||
setUser={setUser!}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import toast from 'react-hot-toast';
|
||||||
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import { RiEditBoxFill } from 'react-icons/ri';
|
||||||
|
import { IoExit } from 'react-icons/io5';
|
||||||
|
import Modal from '../../components/Modal';
|
||||||
|
import UserForm from './FormUser';
|
||||||
|
import Table from '../../components/Table';
|
||||||
|
import Menus from '../../components/Menus';
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import { STATUS_CODE } from '../../constants/responseStatus';
|
||||||
|
import { CONFIRM_MESSAGE, DENIED_ACTION } from '../../constants/messages';
|
||||||
|
|
||||||
|
// Services
|
||||||
|
import { updateRoomStatus } from '../../services/roomServices';
|
||||||
|
import { checkOutUser } from '../../services/userServices';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { IUser } from '../../types/users';
|
||||||
|
|
||||||
|
interface IUserRow {
|
||||||
|
user: IUser;
|
||||||
|
reload: boolean;
|
||||||
|
setReload: Dispatch<SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserRow = ({ user, reload, setReload }: IUserRow) => {
|
||||||
|
const handleCheckOut = async (user: IUser) => {
|
||||||
|
if (user.roomId !== 0) {
|
||||||
|
if (confirm(CONFIRM_MESSAGE)) {
|
||||||
|
const resUpdateStatus = await updateRoomStatus(user.roomId, false);
|
||||||
|
const resCheckoutUser = await checkOutUser(user);
|
||||||
|
|
||||||
|
if (
|
||||||
|
resCheckoutUser?.statusCode === STATUS_CODE.OK &&
|
||||||
|
resUpdateStatus?.statusCode === STATUS_CODE.OK
|
||||||
|
) {
|
||||||
|
toast.success('Check out complete!');
|
||||||
|
|
||||||
|
// Reload table
|
||||||
|
setReload(!reload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast.error(DENIED_ACTION);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const { id, name, identifiedCode, phone, roomId } = user;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table.Row>
|
||||||
|
<div>{id}</div>
|
||||||
|
<div>{name}</div>
|
||||||
|
<div>{identifiedCode}</div>
|
||||||
|
<div>{phone}</div>
|
||||||
|
<div>{roomId ? roomId : 'None'}</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Modal>
|
||||||
|
<Menus.Menu>
|
||||||
|
<Menus.Toggle id={id.toString()} />
|
||||||
|
|
||||||
|
<Menus.List id={id.toString()}>
|
||||||
|
<Modal.Open modalName="edit">
|
||||||
|
<Menus.Button icon={<RiEditBoxFill />}>Edit</Menus.Button>
|
||||||
|
</Modal.Open>
|
||||||
|
<Menus.Button
|
||||||
|
icon={<IoExit />}
|
||||||
|
onClick={() => handleCheckOut(user)}
|
||||||
|
>
|
||||||
|
Check out
|
||||||
|
</Menus.Button>
|
||||||
|
</Menus.List>
|
||||||
|
|
||||||
|
<Modal.Window name="edit" title="Edit user">
|
||||||
|
<UserForm user={user} setReload={setReload} reload={reload} />
|
||||||
|
</Modal.Window>
|
||||||
|
</Menus.Menu>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</Table.Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserRow;
|
||||||
@@ -1,57 +1,38 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import UserTable from './Table';
|
import UserTable from './TableUser';
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import Button from '../../commons/styles/Button';
|
import Button from '../../commons/styles/Button';
|
||||||
import Direction from '../../commons/styles/Direction';
|
import Direction from '../../commons/styles/Direction';
|
||||||
import { StyledUser, Title } from './styled';
|
import { StyledUser, Title } from './styled';
|
||||||
import UserDialog from './Dialog';
|
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Nullable } from '../../types/common';
|
import Modal from '../../components/Modal';
|
||||||
import { IUser } from '../../types/users';
|
import UserForm from './FormUser';
|
||||||
|
|
||||||
const User = () => {
|
const User = () => {
|
||||||
const dialogRef = useRef<HTMLDialogElement>(null);
|
|
||||||
const [reload, setReload] = useState(true);
|
const [reload, setReload] = useState(true);
|
||||||
const [user, setUser] = useState<Nullable<IUser>>(null);
|
|
||||||
const [isAdd, setIsAdd] = useState(false);
|
|
||||||
|
|
||||||
const openFormDialog = (isAddForm: boolean = false) => {
|
|
||||||
setIsAdd(isAddForm);
|
|
||||||
dialogRef.current?.showModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeFormDialog = () => {
|
|
||||||
dialogRef.current?.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledUser>
|
<StyledUser>
|
||||||
<Direction type="horizontal">
|
<Direction type="horizontal">
|
||||||
<Title>List User</Title>
|
<Title>List User</Title>
|
||||||
<Button onClick={() => openFormDialog(true)}>Add user</Button>
|
|
||||||
|
<Modal>
|
||||||
|
<Modal.Open modalName="user-form">
|
||||||
|
<Button>Add user</Button>
|
||||||
|
</Modal.Open>
|
||||||
|
<Modal.Window name="user-form" title="Add form">
|
||||||
|
<UserForm setReload={setReload} reload={reload} />
|
||||||
|
</Modal.Window>
|
||||||
|
</Modal>
|
||||||
</Direction>
|
</Direction>
|
||||||
|
|
||||||
<UserTable
|
<UserTable reload={reload} setReload={setReload} />
|
||||||
reload={reload}
|
|
||||||
setReload={setReload}
|
|
||||||
openFormDialog={() => openFormDialog()}
|
|
||||||
setUser={setUser}
|
|
||||||
/>
|
|
||||||
</StyledUser>
|
</StyledUser>
|
||||||
|
|
||||||
<UserDialog
|
|
||||||
onClose={closeFormDialog}
|
|
||||||
ref={dialogRef}
|
|
||||||
setReload={setReload}
|
|
||||||
reload={reload}
|
|
||||||
user={user}
|
|
||||||
isAdd={isAdd}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user