mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 20:01:59 +00:00
Remove unnecessary code, refactor, fix bug and add section comment
This commit is contained in:
@@ -14,7 +14,7 @@ import RootLayout from '@component/RootLayout';
|
||||
import User from './pages/User';
|
||||
import Booking from './pages/Booking';
|
||||
import Room from './pages/Room';
|
||||
import NotFound from './pages/NotFound';
|
||||
import NotFound from './pages/NotFound/NotFound';
|
||||
import Login from '@page/Login';
|
||||
import Account from '@page/Account';
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,5 +1,4 @@
|
||||
import styled, { css } from 'styled-components';
|
||||
import { COLOR, SIZE } from '@constant/styles';
|
||||
|
||||
interface IStyledButtonIcon {
|
||||
isHaveChildren: boolean;
|
||||
@@ -77,13 +76,13 @@ const StyledButtonIcon = styled.button<IStyledButtonIcon>`
|
||||
|
||||
StyledButtonIcon.defaultProps = {
|
||||
style: {
|
||||
fontSize: SIZE.DEFAULT,
|
||||
backgroundColor: COLOR.DEFAULT,
|
||||
color: COLOR.BLACK,
|
||||
fontSize: '14px',
|
||||
backgroundColor: 'var(--primary-color)',
|
||||
color: '#000',
|
||||
},
|
||||
iconStyle: {
|
||||
color: COLOR.BLACK,
|
||||
size: SIZE.DEFAULT,
|
||||
color: '#000',
|
||||
size: '14px',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,11 @@ interface IConfirmMessage {
|
||||
}
|
||||
|
||||
const ConfirmMessage = memo(
|
||||
({ message, onConfirm, onCloseModal }: IConfirmMessage) => {
|
||||
({
|
||||
message,
|
||||
onConfirm,
|
||||
onCloseModal
|
||||
}: IConfirmMessage) => {
|
||||
const handleConfirmBtn = useCallback(() => {
|
||||
onConfirm();
|
||||
onCloseModal!();
|
||||
|
||||
@@ -34,7 +34,12 @@ const Form = ({ children, onSubmit, id }: IFormProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const FormRow = ({ label, error, children, direction }: IFormRow) => {
|
||||
const FormRow = ({
|
||||
label,
|
||||
error,
|
||||
children,
|
||||
direction
|
||||
}: IFormRow) => {
|
||||
return (
|
||||
<StyledFormRow direction={direction}>
|
||||
<Label>{label}</Label>
|
||||
|
||||
@@ -65,4 +65,11 @@ StyledFormRow.defaultProps = {
|
||||
direction: 'horizontal',
|
||||
};
|
||||
|
||||
export { StyledForm, StyledActionBtn, StyledFormRow, Label, Error, FormBtn };
|
||||
export {
|
||||
StyledForm,
|
||||
StyledActionBtn,
|
||||
StyledFormRow,
|
||||
Label,
|
||||
Error,
|
||||
FormBtn
|
||||
};
|
||||
|
||||
@@ -16,9 +16,6 @@ import MenusContext from '@context/MenuContext';
|
||||
// Types
|
||||
import { Nullable } from '@type/common';
|
||||
|
||||
// Constants
|
||||
import { COLOR } from '@constant/styles';
|
||||
|
||||
interface IButton {
|
||||
children?: string;
|
||||
icon?: ReactNode;
|
||||
@@ -32,19 +29,29 @@ const Menus = ({ children }: { children: ReactNode }) => {
|
||||
const open = setOpenId;
|
||||
|
||||
return (
|
||||
<MenusContext.Provider value={{ openId, close, open }}>
|
||||
<MenusContext.Provider
|
||||
value={{
|
||||
openId,
|
||||
close,
|
||||
open,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</MenusContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const Toggle = ({ id }: { id: string }): ReactNode => {
|
||||
const { openId, close, open } = useContext(MenusContext);
|
||||
const {
|
||||
openId,
|
||||
close,
|
||||
open
|
||||
} = useContext(MenusContext);
|
||||
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation();
|
||||
|
||||
openId === '' || openId !== id
|
||||
? open!(id)
|
||||
openId !== id
|
||||
? open!(id)
|
||||
: close!();
|
||||
};
|
||||
|
||||
@@ -82,7 +89,7 @@ const Button = ({ children, icon, onClick, disabled }: IButton): ReactNode => {
|
||||
<ButtonIcon
|
||||
icon={icon}
|
||||
onClick={handleClick}
|
||||
iconStyle={{ color: COLOR.PRIMARY, size: '19px' }}
|
||||
iconStyle={{ color: 'var(--primary-color)', size: '19px' }}
|
||||
style={{ fontSize: '16px' }}
|
||||
disabled={disabled}
|
||||
>
|
||||
|
||||
@@ -63,4 +63,9 @@ const StyledList = styled.ul`
|
||||
width: max-content;
|
||||
`;
|
||||
|
||||
export { StyledMenu, StyledButton, StyledList, StyledToggle };
|
||||
export {
|
||||
StyledMenu,
|
||||
StyledButton,
|
||||
StyledList,
|
||||
StyledToggle
|
||||
};
|
||||
|
||||
@@ -39,4 +39,9 @@ const TitleModal = styled.p`
|
||||
margin-bottom: 30px;
|
||||
`
|
||||
|
||||
export { StyledModal, Overlay, StyledModalContent, TitleModal };
|
||||
export {
|
||||
StyledModal,
|
||||
Overlay,
|
||||
StyledModalContent,
|
||||
TitleModal
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
// Constants
|
||||
@@ -22,35 +21,24 @@ const Pagination = ({ count }: IPagination) => {
|
||||
|
||||
const totalPage = Math.ceil(count / DEFAULT_PAGE_SIZE);
|
||||
|
||||
const nextPage = useCallback(() => {
|
||||
const next = currentPage === totalPage
|
||||
? currentPage
|
||||
: currentPage + 1;
|
||||
const nextPage = () => {
|
||||
const next = currentPage === totalPage ? currentPage : currentPage + 1;
|
||||
|
||||
searchParams.set('page', next.toString());
|
||||
setSearchParams(searchParams);
|
||||
}, [currentPage, searchParams, totalPage, setSearchParams]);
|
||||
};
|
||||
|
||||
const previousPage = useCallback(() => {
|
||||
const previous = currentPage === 1
|
||||
? currentPage
|
||||
: currentPage - 1;
|
||||
const previousPage = () => {
|
||||
const previous = currentPage === 1 ? currentPage : currentPage - 1;
|
||||
|
||||
searchParams.set('page', previous.toString());
|
||||
setSearchParams(searchParams);
|
||||
}, [currentPage, searchParams, setSearchParams]);
|
||||
};
|
||||
|
||||
const fromIndex = useMemo(
|
||||
() => (currentPage - 1) * DEFAULT_PAGE_SIZE + 1,
|
||||
[currentPage]
|
||||
);
|
||||
const fromIndex = (currentPage - 1) * DEFAULT_PAGE_SIZE + 1;
|
||||
|
||||
const toIndex = useMemo(
|
||||
() => (currentPage === totalPage
|
||||
? count
|
||||
: currentPage * DEFAULT_PAGE_SIZE),
|
||||
[currentPage, count, totalPage]
|
||||
);
|
||||
const toIndex =
|
||||
currentPage === totalPage ? count : currentPage * DEFAULT_PAGE_SIZE;
|
||||
|
||||
return (
|
||||
totalPage > 1 && (
|
||||
|
||||
@@ -47,4 +47,8 @@ const PaginationBtn = styled.button<IPaginationBtn>`
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledPagination, PaginationBtn, Buttons };
|
||||
export {
|
||||
StyledPagination,
|
||||
PaginationBtn,
|
||||
Buttons
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { ChangeEvent } from 'react';
|
||||
import { ChangeEvent, memo } from 'react';
|
||||
|
||||
// Components
|
||||
import Select from '@component/Select';
|
||||
@@ -11,7 +11,7 @@ interface ISortByProps {
|
||||
}[];
|
||||
}
|
||||
|
||||
const SortBy = ({ options }: ISortByProps) => {
|
||||
const SortBy = memo(({ options }: ISortByProps) => {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const sortBy = searchParams.get('sortBy') || '';
|
||||
const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
|
||||
@@ -27,6 +27,6 @@ const SortBy = ({ options }: ISortByProps) => {
|
||||
ariaLabel="Sort"
|
||||
/>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default SortBy;
|
||||
|
||||
@@ -9,7 +9,6 @@ const BOOKINGS_TABLE = 'bookings';
|
||||
const ERROR_FETCHING_BOOKING = "Can't fetch booking data!";
|
||||
const ERROR_UPDATE_BOOKING = "Can't update booking!";
|
||||
const ERROR_CREATE_BOOKING = "Can't create booking!";
|
||||
const ERROR_DELETE_BOOKING = "Can't delete booking!";
|
||||
const CHECKOUT_SUCCESS = 'Check out success!';
|
||||
const ERROR_CHECKOUT = "Can't checkout!";
|
||||
|
||||
@@ -41,7 +40,6 @@ export {
|
||||
ERROR_FETCHING_BOOKING,
|
||||
ERROR_UPDATE_BOOKING,
|
||||
ERROR_CREATE_BOOKING,
|
||||
ERROR_DELETE_BOOKING,
|
||||
ROOMS_TABLE,
|
||||
ERROR_FETCHING_ROOM,
|
||||
ERROR_UPDATE_ROOM,
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
const COLOR = {
|
||||
PRIMARY: '#0010ba',
|
||||
BLACK: '#000',
|
||||
DEFAULT: 'transparent',
|
||||
};
|
||||
|
||||
const SIZE = {
|
||||
DEFAULT: '14px',
|
||||
};
|
||||
|
||||
export { COLOR, SIZE };
|
||||
@@ -34,4 +34,8 @@ const convertCurrencyToNumber = (currency: string) => {
|
||||
return Number(currency.replace(/[^0-9.-]+/g,""))
|
||||
}
|
||||
|
||||
export { formatCurrency, getDayDiff, convertCurrencyToNumber };
|
||||
export {
|
||||
formatCurrency,
|
||||
getDayDiff,
|
||||
convertCurrencyToNumber
|
||||
};
|
||||
|
||||
@@ -26,8 +26,19 @@ const isValidString = (value: string): boolean => {
|
||||
return value.trim().length >= 5;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare two days
|
||||
* @param startDate The start date
|
||||
* @param endDate The end date
|
||||
* @returns Return true if start date greater than end date
|
||||
*/
|
||||
const compareTwoDates = (startDate: Date, endDate: Date): boolean => {
|
||||
return startDate > endDate;
|
||||
};
|
||||
|
||||
export { isValidRegex, isValidString, isValidDiscount, compareTwoDates };
|
||||
export {
|
||||
isValidRegex,
|
||||
isValidString,
|
||||
isValidDiscount,
|
||||
compareTwoDates
|
||||
};
|
||||
|
||||
@@ -3,6 +3,10 @@ import { useQuery } from '@tanstack/react-query';
|
||||
// Services
|
||||
import { getCurrentAccount } from '@service/authenticationService';
|
||||
|
||||
/**
|
||||
* Get data of account currently login
|
||||
* @returns The data of account currently login
|
||||
*/
|
||||
const useAccount = () => {
|
||||
let isAuthenticated: boolean = false;
|
||||
const { data: account, isPending } = useQuery({
|
||||
|
||||
@@ -8,6 +8,10 @@ import { login as loginFn } from '@service/authenticationService';
|
||||
// Types
|
||||
import { ILogin } from '@type/common';
|
||||
|
||||
/**
|
||||
* Login web
|
||||
* @returns The login function and isPending boolean
|
||||
*/
|
||||
const useLogin = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -4,6 +4,10 @@ import { useNavigate } from 'react-router-dom';
|
||||
// Services
|
||||
import { logout as logoutFn } from '@service/authenticationService';
|
||||
|
||||
/**
|
||||
* Logout web
|
||||
* @returns The logout function and isPending boolean
|
||||
*/
|
||||
const useLogout = () => {
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
@@ -7,6 +7,10 @@ import { updateAccount as updateAccountFn } from '@service/authenticationService
|
||||
// Constants
|
||||
import { UPDATE_ACCOUNT_SUCCESSFUL } from '@constant/messages';
|
||||
|
||||
/**
|
||||
* Update account currently login.
|
||||
* @returns Return updateAccount function and isUpdating boolean
|
||||
*/
|
||||
const useUpdateAccount = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
// Services
|
||||
import { deleteBooking as deleteBookingFn } from '@service/bookingServices';
|
||||
|
||||
// Constants
|
||||
import { DELETE_SUCCESS } from '@constant/messages';
|
||||
|
||||
/**
|
||||
* Delete the booking from database
|
||||
* @returns The boolean of status deleting and deleteBooking function
|
||||
*/
|
||||
const useDeleteBooking = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const {
|
||||
isPending: isDeleting,
|
||||
mutate: deleteBooking,
|
||||
isSuccess,
|
||||
} = useMutation({
|
||||
mutationFn: deleteBookingFn,
|
||||
onSuccess: () => {
|
||||
toast.success(DELETE_SUCCESS);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['bookings'],
|
||||
});
|
||||
},
|
||||
onError: (err) => toast.error(err.message),
|
||||
});
|
||||
|
||||
return { isDeleting, deleteBooking, isSuccess };
|
||||
};
|
||||
|
||||
export { useDeleteBooking };
|
||||
@@ -1,5 +1,11 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
/**
|
||||
* The debounce hooks use to execute the callback function every x delay time
|
||||
* @param value The value need to be pass
|
||||
* @param delay The number of time want to delay
|
||||
* @returns Return the value after delay number
|
||||
*/
|
||||
const useDebounce = <T>(value: T, delay: number): T => {
|
||||
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@ import { useContext } from 'react';
|
||||
// Contexts
|
||||
import { UserRoomAvailableContext } from '@context/UserRoomAvailableContext';
|
||||
|
||||
/**
|
||||
* The hooks check the place call is in UserRoomAvailableProvider or not
|
||||
* @returns The context of UserRoomAvailableContext
|
||||
*/
|
||||
const useUserRoomAvailable = () => {
|
||||
const context = useContext(UserRoomAvailableContext);
|
||||
|
||||
|
||||
@@ -33,4 +33,9 @@ const FormArea = styled.div`
|
||||
margin-top: 50px;
|
||||
`
|
||||
|
||||
export { Title, StyledAccount, FormArea, SubTitle };
|
||||
export {
|
||||
Title,
|
||||
StyledAccount,
|
||||
FormArea,
|
||||
SubTitle
|
||||
};
|
||||
|
||||
@@ -32,16 +32,18 @@ interface IBookingFormProp {
|
||||
}
|
||||
|
||||
const BookingForm = ({ onCloseModal, booking }: IBookingFormProp) => {
|
||||
const { roomsAvailable, usersAvailable, dispatch } = useUserRoomAvailable();
|
||||
const {
|
||||
roomsAvailable,
|
||||
usersAvailable,
|
||||
dispatch
|
||||
} = useUserRoomAvailable();
|
||||
const { isCreating, createBooking } = useCreateBooking();
|
||||
const { isUpdating, updateBooking } = useUpdateBooking();
|
||||
const isLoading = isCreating || isUpdating;
|
||||
const { id: editId, ...editValues } = { ...booking };
|
||||
const [ amountValue, setAmountValue ] = useState(
|
||||
editValues.amount
|
||||
? formatCurrency(editValues.amount)
|
||||
: '$0.00'
|
||||
);
|
||||
const [amountValue, setAmountValue] = useState(
|
||||
editValues.amount ? formatCurrency(editValues.amount) : '$0.00'
|
||||
);
|
||||
const formMethods = useForm<IBooking>({
|
||||
defaultValues: editId
|
||||
? {
|
||||
@@ -177,15 +179,12 @@ const BookingForm = ({ onCloseModal, booking }: IBookingFormProp) => {
|
||||
const startDate = new Date(getValues().startDate);
|
||||
const endDate = new Date(getValues().endDate);
|
||||
|
||||
if(startDate >= endDate) {
|
||||
if (startDate >= endDate) {
|
||||
setValue('endDate', '');
|
||||
}
|
||||
}, [setValue, getValues]);
|
||||
|
||||
const startDateValidate = useMemo(
|
||||
() => new Date().toISOString().split('T')[0],
|
||||
[]
|
||||
);
|
||||
const startDateValidate = new Date().toISOString().split('T')[0];
|
||||
|
||||
const endDateValidate = () => {
|
||||
if (getValues('startDate')) {
|
||||
@@ -201,7 +200,7 @@ const BookingForm = ({ onCloseModal, booking }: IBookingFormProp) => {
|
||||
return (
|
||||
<FormProvider {...formMethods}>
|
||||
<Form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Form.Row label="User" error={errors?.userId?.message}>
|
||||
<Form.Row label="User:" error={errors?.userId?.message}>
|
||||
{userOptions.length ? (
|
||||
<Select
|
||||
ariaLabel="user-select"
|
||||
@@ -218,7 +217,7 @@ const BookingForm = ({ onCloseModal, booking }: IBookingFormProp) => {
|
||||
)}
|
||||
</Form.Row>
|
||||
|
||||
<Form.Row label="Room" error={errors?.roomId?.message}>
|
||||
<Form.Row label="Room:" error={errors?.roomId?.message}>
|
||||
{roomOptions.length ? (
|
||||
<Select
|
||||
ariaLabel="user-select"
|
||||
@@ -234,7 +233,7 @@ const BookingForm = ({ onCloseModal, booking }: IBookingFormProp) => {
|
||||
)}
|
||||
</Form.Row>
|
||||
|
||||
<Form.Row label="Start Date" error={errors?.startDate?.message}>
|
||||
<Form.Row label="Start Date:" error={errors?.startDate?.message}>
|
||||
<Input
|
||||
type="date"
|
||||
id="startDate"
|
||||
@@ -251,7 +250,7 @@ const BookingForm = ({ onCloseModal, booking }: IBookingFormProp) => {
|
||||
/>
|
||||
</Form.Row>
|
||||
|
||||
<Form.Row label="End Date" error={errors?.endDate?.message}>
|
||||
<Form.Row label="End Date:" error={errors?.endDate?.message}>
|
||||
<Input
|
||||
type="date"
|
||||
id="endDate"
|
||||
@@ -269,7 +268,7 @@ const BookingForm = ({ onCloseModal, booking }: IBookingFormProp) => {
|
||||
/>
|
||||
</Form.Row>
|
||||
|
||||
<Form.Row label="Amount">
|
||||
<Form.Row label="Amount:">
|
||||
<Input
|
||||
type="hidden"
|
||||
id="amount"
|
||||
@@ -289,7 +288,11 @@ const BookingForm = ({ onCloseModal, booking }: IBookingFormProp) => {
|
||||
name="submit"
|
||||
disabled={!isDirty || !isValid || isLoading}
|
||||
>
|
||||
{!editId ? 'Add' : 'Save'}
|
||||
{
|
||||
!editId
|
||||
? 'Add'
|
||||
: 'Save'
|
||||
}
|
||||
</Form.Button>
|
||||
<Form.Button
|
||||
type="button"
|
||||
|
||||
@@ -29,8 +29,15 @@ interface IBookingRow {
|
||||
|
||||
const BookingRow = ({ booking }: IBookingRow) => {
|
||||
const { checkOutBooking } = useCheckOut();
|
||||
const { id, users, startDate, endDate, rooms, amount, status } = booking;
|
||||
const statusText = status ? 'Check in' : 'Check out';
|
||||
const {
|
||||
id,
|
||||
users,
|
||||
startDate,
|
||||
endDate,
|
||||
rooms,
|
||||
amount,
|
||||
status
|
||||
} = booking;
|
||||
const formattedPrice = useMemo(() => formatCurrency(amount), [amount]);
|
||||
const renderEditBtn = useCallback(
|
||||
(onCloseModal: () => void) => (
|
||||
@@ -79,7 +86,11 @@ const BookingRow = ({ booking }: IBookingRow) => {
|
||||
</div>
|
||||
<div>{rooms?.name}</div>
|
||||
<div>{formattedPrice}</div>
|
||||
<div>{statusText}</div>
|
||||
<div>{
|
||||
status
|
||||
? 'Check in'
|
||||
: 'Check out'
|
||||
}</div>
|
||||
|
||||
<div>
|
||||
<Modal>
|
||||
|
||||
@@ -21,4 +21,8 @@ const StyledOperationTable = styled.div`
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
export { StyledBooking, Title, StyledOperationTable };
|
||||
export {
|
||||
StyledBooking,
|
||||
Title,
|
||||
StyledOperationTable
|
||||
};
|
||||
|
||||
@@ -3,9 +3,6 @@ import { Navigate } from 'react-router-dom';
|
||||
// Styled
|
||||
import { LoginLayout, StyledLogo, TitleStyled } from './styled';
|
||||
|
||||
// Assets
|
||||
import logo from '@assets/images/logo.png';
|
||||
|
||||
// Components
|
||||
import LoginForm from './LoginForm';
|
||||
|
||||
@@ -21,7 +18,7 @@ const Login = () => {
|
||||
|
||||
return (
|
||||
<LoginLayout>
|
||||
<StyledLogo src={logo} />
|
||||
<StyledLogo src={'./assets/images/logo.png'} />
|
||||
<TitleStyled>Log In</TitleStyled>
|
||||
|
||||
<LoginForm />
|
||||
|
||||
@@ -40,4 +40,10 @@ const FieldInput = styled.input`
|
||||
width: 300px;
|
||||
`
|
||||
|
||||
export { LoginLayout, StyledLogo, TitleStyled, StyledLoginForm, FieldInput };
|
||||
export {
|
||||
LoginLayout,
|
||||
StyledLogo,
|
||||
TitleStyled,
|
||||
StyledLoginForm,
|
||||
FieldInput
|
||||
};
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
// Styled
|
||||
import { Button, Heading, StyledNotFound } from '.';
|
||||
|
||||
|
||||
const NotFound = () => {
|
||||
const useMoveBack = () => {
|
||||
const navigate = useNavigate();
|
||||
return () => navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledNotFound>
|
||||
<Heading>The page not found!</Heading>
|
||||
<Button onClick={useMoveBack}>Go back!</Button>
|
||||
</StyledNotFound>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotFound;
|
||||
+6
-19
@@ -1,5 +1,4 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledNotFound = styled.div`
|
||||
height: 100vh;
|
||||
@@ -27,20 +26,8 @@ const Button = styled.button`
|
||||
border-radius: var(--radius-sm);
|
||||
`;
|
||||
|
||||
const useMoveBack = () => {
|
||||
const navigate = useNavigate();
|
||||
return () => navigate(-1);
|
||||
};
|
||||
|
||||
const NotFound = () => {
|
||||
const onBack = useMoveBack();
|
||||
|
||||
return (
|
||||
<StyledNotFound>
|
||||
<Heading>The page not found!</Heading>
|
||||
<Button onClick={onBack}>Go back!</Button>
|
||||
</StyledNotFound>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotFound;
|
||||
export {
|
||||
StyledNotFound,
|
||||
Heading,
|
||||
Button,
|
||||
}
|
||||
@@ -26,11 +26,13 @@ interface IRoomRow {
|
||||
}
|
||||
|
||||
const RoomRow = ({ room }: IRoomRow) => {
|
||||
const { id, name, price, status } = room;
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
price,
|
||||
status
|
||||
} = room;
|
||||
const { setIsDeleteRoom } = useSetIsDeleteRoom();
|
||||
const statusText = status
|
||||
? 'Unavailable'
|
||||
: 'Available';
|
||||
const formattedPrice = useMemo(() => formatCurrency(price), [price]);
|
||||
const renderEditBtn = useCallback(
|
||||
(onCloseModal: () => void) => (
|
||||
@@ -42,11 +44,11 @@ const RoomRow = ({ room }: IRoomRow) => {
|
||||
);
|
||||
const renderDeleteBtn = useCallback(
|
||||
(onCloseModal: () => void) => (
|
||||
<Menus.Button icon={<HiTrash />} onClick={onCloseModal}>
|
||||
<Menus.Button icon={<HiTrash />} onClick={onCloseModal} disabled={status}>
|
||||
Delete
|
||||
</Menus.Button>
|
||||
),
|
||||
[]
|
||||
[status]
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -54,7 +56,11 @@ const RoomRow = ({ room }: IRoomRow) => {
|
||||
<div>{id}</div>
|
||||
<div>{name}</div>
|
||||
<div>{formattedPrice}</div>
|
||||
<div>{statusText}</div>
|
||||
<div>{
|
||||
status
|
||||
? 'Unavailable'
|
||||
: 'Available'
|
||||
}</div>
|
||||
|
||||
<div>
|
||||
<Modal>
|
||||
|
||||
@@ -26,7 +26,11 @@ import Pagination from '@component/Pagination';
|
||||
|
||||
const RoomTable = () => {
|
||||
const columnName = ['Id', 'Name', 'Price', 'Status'];
|
||||
const { isLoading, rooms, count } = useRooms();
|
||||
const {
|
||||
isLoading,
|
||||
rooms,
|
||||
count
|
||||
} = useRooms();
|
||||
|
||||
const renderRoomRow = useCallback(
|
||||
(room: IRoom) => <RoomRow room={room} key={room.id} />,
|
||||
|
||||
@@ -21,4 +21,8 @@ const StyledOperationTable = styled.div`
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
export { StyledRoom, Title, StyledOperationTable };
|
||||
export {
|
||||
StyledRoom,
|
||||
Title,
|
||||
StyledOperationTable
|
||||
};
|
||||
|
||||
@@ -18,7 +18,12 @@ interface IUserRow {
|
||||
}
|
||||
|
||||
const UserRow = ({ user }: IUserRow) => {
|
||||
const { id, name, phone, isBooked } = user;
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
phone,
|
||||
isBooked
|
||||
} = user;
|
||||
const { setIsDeleteUser } = useIsDeleteUser();
|
||||
|
||||
const renderEditBtn = useCallback(
|
||||
@@ -32,11 +37,11 @@ const UserRow = ({ user }: IUserRow) => {
|
||||
|
||||
const renderDeleteBtn = useCallback(
|
||||
(onCloseModal: () => void) => (
|
||||
<Menus.Button icon={<HiTrash />} onClick={onCloseModal}>
|
||||
<Menus.Button icon={<HiTrash />} onClick={onCloseModal} disabled={isBooked}>
|
||||
Delete
|
||||
</Menus.Button>
|
||||
),
|
||||
[]
|
||||
[isBooked]
|
||||
);
|
||||
return (
|
||||
<Table.Row>
|
||||
|
||||
@@ -25,8 +25,17 @@ import { useUsers } from '@hook/users/useUsers';
|
||||
import Pagination from '@component/Pagination';
|
||||
|
||||
const UserTable = () => {
|
||||
const columnName = ['Id', 'Name', 'Phone', 'Is Booked'];
|
||||
const { isLoading, users, count } = useUsers();
|
||||
const columnName = [
|
||||
'Id',
|
||||
'Name',
|
||||
'Phone',
|
||||
'Is Booked'
|
||||
];
|
||||
const {
|
||||
isLoading,
|
||||
users,
|
||||
count
|
||||
} = useUsers();
|
||||
|
||||
const renderUserRow = useCallback(
|
||||
(user: IUser) => (
|
||||
@@ -52,7 +61,7 @@ const UserTable = () => {
|
||||
|
||||
{users && users.length ? (
|
||||
<Menus>
|
||||
<Table columns="10% 35% 30% 15% 10%">
|
||||
<Table columns="10% 35% 30% 15% 5%">
|
||||
<Table.Header headerColumn={columnName} />
|
||||
<Table.Body<IUser> data={users} render={renderUserRow} />
|
||||
<Table.Footer>
|
||||
|
||||
@@ -21,4 +21,8 @@ const StyledOperationTable = styled.div`
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
export { StyledUser, Title, StyledOperationTable };
|
||||
export {
|
||||
StyledUser,
|
||||
Title,
|
||||
StyledOperationTable
|
||||
};
|
||||
|
||||
@@ -7,6 +7,11 @@ import supabase from './supabaseService';
|
||||
// Types
|
||||
import { IAccount } from '@type/account';
|
||||
|
||||
/**
|
||||
* Login services
|
||||
* @param param0 The object contains email and password to login
|
||||
* @returns The data of account login if success
|
||||
*/
|
||||
const login = async ({ email, password }: ILogin) => {
|
||||
const { data, error } = await supabase.auth.signInWithPassword({
|
||||
email,
|
||||
@@ -20,6 +25,10 @@ const login = async ({ email, password }: ILogin) => {
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get data of account current login
|
||||
* @returns The data of account currently login
|
||||
*/
|
||||
const getCurrentAccount = async () => {
|
||||
const { data } = await supabase.auth.getSession();
|
||||
|
||||
@@ -36,6 +45,9 @@ const getCurrentAccount = async () => {
|
||||
return accountData.user;
|
||||
};
|
||||
|
||||
/**
|
||||
* Logout services
|
||||
*/
|
||||
const logout = async () => {
|
||||
const { error } = await supabase.auth.signOut();
|
||||
|
||||
@@ -44,6 +56,11 @@ const logout = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update account currently login
|
||||
* @param param0 The object contain fullName, password to be updated
|
||||
* @returns The data of account after updated
|
||||
*/
|
||||
const updateAccount = async ({ fullName, password }: IAccount) => {
|
||||
let accountUpdate: UserAttributes | null = null;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
BOOKINGS_TABLE,
|
||||
ERROR_CHECKOUT,
|
||||
ERROR_CREATE_BOOKING,
|
||||
ERROR_DELETE_BOOKING,
|
||||
ERROR_FETCHING_BOOKING,
|
||||
ERROR_UPDATE_BOOKING,
|
||||
} from '@constant/messages';
|
||||
@@ -100,22 +99,6 @@ const createBooking = async (booking: IBooking): Promise<IBooking> => {
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete booking in database
|
||||
* @param idRoom The id of booking need to delete
|
||||
*/
|
||||
const deleteBooking = async (idBooking: number) => {
|
||||
const { error } = await supabase
|
||||
.from(BOOKINGS_TABLE)
|
||||
.delete()
|
||||
.eq('id', idBooking);
|
||||
|
||||
if (error) {
|
||||
console.error(error.message);
|
||||
throw new Error(ERROR_DELETE_BOOKING);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check out booking services
|
||||
* @param param0 The ICheckOutBooking object
|
||||
@@ -150,6 +133,5 @@ export {
|
||||
createBooking,
|
||||
getAllBookings,
|
||||
updateBooking,
|
||||
deleteBooking,
|
||||
checkOutBooking,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,8 @@ import { IUser } from '@type/user';
|
||||
|
||||
// Services
|
||||
import supabase from './supabaseService';
|
||||
import { IDataState } from '@type/common';
|
||||
|
||||
// Constants
|
||||
import { DEFAULT_PAGE_SIZE } from '@constant/config';
|
||||
import {
|
||||
ERROR_CREATE_USER,
|
||||
@@ -118,19 +119,6 @@ const getAllUsers = async ({
|
||||
return { data, count };
|
||||
};
|
||||
|
||||
const getUserNotBooked = async (): Promise<IDataState[]> => {
|
||||
const { data, error } = await supabase
|
||||
.from(USERS_TABLE)
|
||||
.select('id, name, isBooked');
|
||||
|
||||
if (error) {
|
||||
console.error(error.message);
|
||||
throw new Error(ERROR_FETCHING_USER);
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const updateUserBookedStatus = async (
|
||||
id: number,
|
||||
isBooked: boolean
|
||||
@@ -153,6 +141,5 @@ export {
|
||||
createUser,
|
||||
getAllUsers,
|
||||
setIsDeleteUser,
|
||||
getUserNotBooked,
|
||||
updateUserBookedStatus,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@font-face {
|
||||
font-family: 'Cabin';
|
||||
src: url('../../assets/fonts/Cabin.woff2') format('woff2'),
|
||||
url('../../assets/fonts/Cabin.ttf') format('truetype');
|
||||
src: url('./assets/fonts/Cabin.woff2') format('woff2'),
|
||||
url('./assets/fonts/Cabin.ttf') format('truetype');
|
||||
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user