mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 20:01:59 +00:00
Merge pull request #44 from Nez27/feat/impl-booking-page
Add booking page
This commit is contained in:
@@ -2,35 +2,33 @@ import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
|
||||
import isPropValid from '@emotion/is-prop-valid';
|
||||
import { StyleSheetManager } from 'styled-components';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { useEffect, useMemo, useReducer } from 'react';
|
||||
|
||||
// Components
|
||||
import AppLayout from './components/AppLayout';
|
||||
import Toast from './components/Toast';
|
||||
|
||||
// Pages
|
||||
import User from './pages/User';
|
||||
import Dashboard from './pages/Dashboard';
|
||||
import Booking from './pages/Booking';
|
||||
import Room from './pages/Room';
|
||||
import NotFound from './pages/NotFound';
|
||||
|
||||
// Constants
|
||||
import * as PATH from './constants/path';
|
||||
import Toast from './components/Toast';
|
||||
|
||||
// Hooks
|
||||
import {
|
||||
UserRoomAvailableContext,
|
||||
initialState,
|
||||
reducer,
|
||||
} from '@context/UserRoomAvailableContext';
|
||||
import { useEffect, useMemo, useReducer } from 'react';
|
||||
|
||||
// Services
|
||||
import { getUserNotBooked } from '@service/userServices';
|
||||
import { getRoomsAvailable } from '@service/roomServices';
|
||||
import { getAllUsers } from '@service/userServices';
|
||||
import { getAllRooms } from '@service/roomServices';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 60 * 1000,
|
||||
},
|
||||
},
|
||||
});
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
function App() {
|
||||
const [{ usersAvailable, roomsAvailable }, dispatch] = useReducer(
|
||||
@@ -41,16 +39,26 @@ function App() {
|
||||
// Init list user and room available
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
const tempUser = await getUserNotBooked();
|
||||
const tempUser = await getAllUsers({
|
||||
sortBy: 'id',
|
||||
orderBy: 'asc',
|
||||
phoneSearch: '',
|
||||
page: 0,
|
||||
});
|
||||
|
||||
if (tempUser) {
|
||||
dispatch({ type: 'initUser', payload: tempUser });
|
||||
dispatch({ type: 'initUser', payload: tempUser.data });
|
||||
}
|
||||
|
||||
const tempRoom = await getRoomsAvailable();
|
||||
const tempRoom = await getAllRooms({
|
||||
sortBy: 'id',
|
||||
orderBy: 'asc',
|
||||
roomSearch: '',
|
||||
page: 0,
|
||||
});
|
||||
|
||||
if (tempRoom) {
|
||||
dispatch({ type: 'initRoom', payload: tempRoom });
|
||||
dispatch({ type: 'initRoom', payload: tempRoom.data });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,11 +76,8 @@ function App() {
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route element={<AppLayout />}>
|
||||
<Route
|
||||
index
|
||||
element={<Navigate replace to={PATH.DASHBOARD} />}
|
||||
/>
|
||||
<Route path={PATH.DASHBOARD} element={<Dashboard />} />
|
||||
<Route index element={<Navigate replace to={PATH.BOOKING} />} />
|
||||
<Route path={PATH.BOOKING} element={<Booking />} />
|
||||
<Route path={PATH.USER} element={<User />} />
|
||||
<Route path={PATH.ROOM} element={<Room />} />
|
||||
</Route>
|
||||
|
||||
@@ -4,7 +4,8 @@ const StyledSearch = styled.input`
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border-color);
|
||||
font-size: var(--fs-sm-x);
|
||||
padding: 5px 10px;
|
||||
padding: 10px 10px;
|
||||
|
||||
`;
|
||||
|
||||
export { StyledSearch };
|
||||
|
||||
@@ -48,6 +48,7 @@ const FORM = {
|
||||
DELETE: 'delete',
|
||||
USER: 'user-form',
|
||||
ROOM: 'room-form',
|
||||
BOOKING: 'booking-form',
|
||||
CHECKOUT: 'checkout',
|
||||
};
|
||||
|
||||
@@ -57,10 +58,4 @@ const REGEX = {
|
||||
NUMBER: /^[0-9]*$/,
|
||||
};
|
||||
|
||||
export {
|
||||
USER_PAGE,
|
||||
ROOM_PAGE,
|
||||
FORM,
|
||||
REGEX,
|
||||
ORDERBY_OPTIONS,
|
||||
};
|
||||
export { USER_PAGE, ROOM_PAGE, FORM, REGEX, ORDERBY_OPTIONS };
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { FormProvider, useForm } from 'react-hook-form';
|
||||
|
||||
// Styled
|
||||
import Input from '@commonStyle/Input.ts';
|
||||
|
||||
// Constants
|
||||
import { REQUIRED_FIELD_ERROR } from '@constant/formValidateMessage.ts';
|
||||
|
||||
// Components
|
||||
import Form from '@component/Form/index.tsx';
|
||||
import Select, { ISelectOptions } from '@component/Select/index.tsx';
|
||||
|
||||
// Hooks
|
||||
import { useCreateBooking } from '@hook/bookings/useCreateBooking.ts';
|
||||
import { useUpdateBooking } from '@hook/bookings/useUpdateBooking.ts';
|
||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable.ts';
|
||||
|
||||
// Helpers
|
||||
import {
|
||||
convertCurrencyToNumber,
|
||||
formatCurrency,
|
||||
getDayDiff,
|
||||
} from '@helper/helper.ts';
|
||||
|
||||
// Services
|
||||
import { getRoomById, updateRoomStatus } from '@service/roomServices.ts';
|
||||
import { updateUserBookedStatus } from '@service/userServices.ts';
|
||||
|
||||
// Types
|
||||
import { IBooking, TBookingResponse } from '@type/booking.ts';
|
||||
|
||||
interface IBookingFormProp {
|
||||
onCloseModal?: () => void;
|
||||
booking?: TBookingResponse;
|
||||
}
|
||||
|
||||
interface IBookingForm extends Omit<IBooking, 'amount'> {
|
||||
amount: string;
|
||||
}
|
||||
|
||||
const BookingForm = ({ onCloseModal, booking }: IBookingFormProp) => {
|
||||
const { roomsAvailable, usersAvailable, dispatch } = useUserRoomAvailable();
|
||||
const { isCreating, createBooking } = useCreateBooking();
|
||||
const { isUpdating, updateBooking } = useUpdateBooking();
|
||||
const isLoading = isCreating || isUpdating;
|
||||
const { id: editId, ...editValues } = { ...booking };
|
||||
const formMethods = useForm<IBookingForm>({
|
||||
defaultValues: editId
|
||||
? {
|
||||
startDate: editValues.startDate,
|
||||
endDate: editValues.endDate,
|
||||
roomId: editValues.rooms?.id,
|
||||
userId: editValues.users?.id,
|
||||
amount: formatCurrency(editValues.amount!),
|
||||
}
|
||||
: {},
|
||||
});
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
reset,
|
||||
formState: { errors, isDirty, isValid },
|
||||
trigger,
|
||||
getValues,
|
||||
setValue,
|
||||
} = formMethods;
|
||||
|
||||
// Submit form
|
||||
const onSubmit = async (newBooking: IBookingForm) => {
|
||||
if (!editId) {
|
||||
// Add request
|
||||
createBooking(
|
||||
{ ...newBooking, amount: convertCurrencyToNumber(newBooking.amount) },
|
||||
{
|
||||
onSuccess: async () => {
|
||||
reset();
|
||||
onCloseModal?.();
|
||||
|
||||
// Update room, user available in global state
|
||||
dispatch!({
|
||||
type: 'updateStatusUser',
|
||||
payload: [{ id: newBooking.userId, isBooked: true }],
|
||||
});
|
||||
dispatch!({
|
||||
type: 'updateStatusRoom',
|
||||
payload: [{ id: newBooking.roomId, status: true }],
|
||||
});
|
||||
|
||||
// Update room, user available in server
|
||||
await updateRoomStatus(newBooking.roomId, true);
|
||||
await updateUserBookedStatus(newBooking.userId, true);
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Edit request
|
||||
newBooking.id = editId!;
|
||||
updateBooking(
|
||||
{ ...newBooking, amount: convertCurrencyToNumber(newBooking.amount) },
|
||||
{
|
||||
onSuccess: async () => {
|
||||
reset();
|
||||
onCloseModal?.();
|
||||
|
||||
// Update room status in global state
|
||||
// Reset status old room
|
||||
dispatch!({
|
||||
type: 'updateStatusRoom',
|
||||
payload: [{ id: booking!.rooms!.id, status: false }],
|
||||
});
|
||||
|
||||
// Change status new room
|
||||
dispatch!({
|
||||
type: 'updateStatusRoom',
|
||||
payload: [{ id: newBooking.roomId, status: true }],
|
||||
});
|
||||
|
||||
// Update room status in server
|
||||
// Reset status old room
|
||||
await updateRoomStatus(booking!.rooms!.id, false);
|
||||
|
||||
// Change status new room
|
||||
await updateRoomStatus(newBooking.roomId, true);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Init user, room available to options choice
|
||||
const userOptions = useMemo(() => {
|
||||
const options: ISelectOptions[] = [];
|
||||
|
||||
usersAvailable?.forEach((item) => {
|
||||
if (booking?.users?.id === item.id || !item.isBooked) {
|
||||
options.push({
|
||||
label: item.name!,
|
||||
value: item.id.toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return options;
|
||||
}, [usersAvailable, booking?.users?.id]);
|
||||
|
||||
const roomOptions = useMemo(() => {
|
||||
const options: ISelectOptions[] = [];
|
||||
|
||||
roomsAvailable?.forEach((item) => {
|
||||
if (booking?.rooms?.id === item.id || !item.status) {
|
||||
options.push({
|
||||
label: item.name!,
|
||||
value: item.id.toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return options;
|
||||
}, [roomsAvailable, booking?.rooms?.id]);
|
||||
|
||||
// Calculate the final price
|
||||
const computePrice = useCallback(async () => {
|
||||
setValue('amount', 'Loading...');
|
||||
const startDateValue = getValues('startDate');
|
||||
const endDateValue = getValues('endDate');
|
||||
const roomValue = getValues('roomId');
|
||||
|
||||
if (startDateValue && endDateValue && roomValue) {
|
||||
const daysDiff = getDayDiff(
|
||||
new Date(startDateValue),
|
||||
new Date(endDateValue)
|
||||
);
|
||||
|
||||
// Fetch room data
|
||||
const room = await getRoomById(roomValue.toString());
|
||||
const amount = daysDiff * room.price;
|
||||
|
||||
setValue('amount', formatCurrency(amount), { shouldValidate: true });
|
||||
}
|
||||
}, [getValues, setValue]);
|
||||
|
||||
const startDateValidate = useMemo(
|
||||
() => new Date().toISOString().split('T')[0],
|
||||
[]
|
||||
);
|
||||
|
||||
const endDateValidate = () => {
|
||||
if (getValues('startDate')) {
|
||||
const date = new Date(getValues('startDate'));
|
||||
date.setDate(date.getDate() + 1);
|
||||
|
||||
return date.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
return (
|
||||
<FormProvider {...formMethods}>
|
||||
<Form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Form.Row label="User" error={errors?.userId?.message}>
|
||||
{userOptions.length ? (
|
||||
<Select
|
||||
ariaLabel="user-select"
|
||||
options={userOptions}
|
||||
id="userId"
|
||||
disable={Boolean(booking?.users)}
|
||||
optionsConfigForm={{
|
||||
valueAsNumber: true,
|
||||
onChange: () => trigger('userId'),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<p>No user available</p>
|
||||
)}
|
||||
</Form.Row>
|
||||
|
||||
<Form.Row label="Room" error={errors?.roomId?.message}>
|
||||
{roomOptions.length ? (
|
||||
<Select
|
||||
ariaLabel="user-select"
|
||||
options={roomOptions}
|
||||
id="roomId"
|
||||
optionsConfigForm={{
|
||||
valueAsNumber: true,
|
||||
onChange: () => trigger('roomId'),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<p>No room available</p>
|
||||
)}
|
||||
</Form.Row>
|
||||
|
||||
<Form.Row label="Start Date" error={errors?.startDate?.message}>
|
||||
<Input
|
||||
type="date"
|
||||
id="startDate"
|
||||
min={startDateValidate}
|
||||
{...register('startDate', {
|
||||
required: REQUIRED_FIELD_ERROR,
|
||||
onChange: () => {
|
||||
trigger('startDate');
|
||||
computePrice();
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</Form.Row>
|
||||
|
||||
<Form.Row label="End Date" error={errors?.startDate?.message}>
|
||||
<Input
|
||||
type="date"
|
||||
id="endDate"
|
||||
min={endDateValidate()}
|
||||
disabled={!getValues('startDate')}
|
||||
{...register('endDate', {
|
||||
required: REQUIRED_FIELD_ERROR,
|
||||
onChange: () => {
|
||||
trigger('endDate');
|
||||
computePrice();
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</Form.Row>
|
||||
|
||||
<Form.Row label="Amount">
|
||||
<Input
|
||||
type="text"
|
||||
id="amount"
|
||||
{...register('amount', {
|
||||
required: REQUIRED_FIELD_ERROR,
|
||||
onChange: () => trigger('amount'),
|
||||
})}
|
||||
readOnly
|
||||
/>
|
||||
</Form.Row>
|
||||
|
||||
<Form.Action>
|
||||
<Form.Button
|
||||
type="submit"
|
||||
name="submit"
|
||||
disabled={!isDirty || !isValid || isLoading}
|
||||
>
|
||||
{
|
||||
!editId
|
||||
? 'Add'
|
||||
: 'Save'
|
||||
}
|
||||
</Form.Button>
|
||||
<Form.Button type="button" variations="secondary" onClick={onCloseModal}>
|
||||
Close
|
||||
</Form.Button>
|
||||
</Form.Action>
|
||||
</Form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default BookingForm;
|
||||
@@ -0,0 +1,120 @@
|
||||
import toast from 'react-hot-toast';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
// Helpers
|
||||
import { formatCurrency } from '@helper/helper';
|
||||
|
||||
// Types
|
||||
import { TBookingResponse } from '@type/booking';
|
||||
|
||||
// Components
|
||||
import Modal from '@component/Modal';
|
||||
import Table from '@component/Table';
|
||||
import Menus from '@component/Menus';
|
||||
import { RiEditBoxFill } from 'react-icons/ri';
|
||||
import { TbArrowNarrowRight } from 'react-icons/tb';
|
||||
import { ImExit } from 'react-icons/im';
|
||||
import BookingForm from './BookingForm';
|
||||
import ConfirmMessage from '@component/ConfirmMessage';
|
||||
|
||||
// Hooks
|
||||
import useCheckOut from '@hook/bookings/useCheckout';
|
||||
|
||||
// Constants
|
||||
import { FORM } from '@constant/commons';
|
||||
|
||||
interface IBookingRow {
|
||||
booking: TBookingResponse;
|
||||
}
|
||||
|
||||
const BookingRow = ({ booking }: IBookingRow) => {
|
||||
const { checkOutBooking } = useCheckOut();
|
||||
const {
|
||||
id,
|
||||
users,
|
||||
startDate,
|
||||
endDate,
|
||||
rooms,
|
||||
amount,
|
||||
status
|
||||
} = booking;
|
||||
const statusText = status
|
||||
? 'Check in'
|
||||
: 'Check out';
|
||||
const formattedPrice = useMemo(() => formatCurrency(amount), [amount]);
|
||||
const renderEditBtn = useCallback(
|
||||
(onCloseModal: () => void) => (
|
||||
<Menus.Button onClick={onCloseModal} icon={<RiEditBoxFill />}>
|
||||
Edit
|
||||
</Menus.Button>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
const renderCheckOutBtn = useCallback(
|
||||
(onCloseModal: () => void) => (
|
||||
<Menus.Button onClick={onCloseModal} icon={<ImExit />}>
|
||||
Checkout
|
||||
</Menus.Button>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
const handleClickCheckOutBtn = useCallback(() => {
|
||||
if (booking.status) {
|
||||
checkOutBooking({
|
||||
idBooking: booking.id,
|
||||
roomId: booking!.rooms!.id,
|
||||
userId: booking!.users!.id,
|
||||
});
|
||||
} else {
|
||||
toast.error('User already checkout!');
|
||||
}
|
||||
}, [booking, checkOutBooking]);
|
||||
|
||||
return (
|
||||
<Table.Row>
|
||||
<div>{users?.name}</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
{startDate} <TbArrowNarrowRight />
|
||||
{endDate}
|
||||
</div>
|
||||
<div>{rooms?.name}</div>
|
||||
<div>{formattedPrice}</div>
|
||||
<div>{statusText}</div>
|
||||
|
||||
<div>
|
||||
<Modal>
|
||||
<Menus.Menu>
|
||||
<Menus.Toggle id={id.toString()} />
|
||||
|
||||
<Menus.List id={id.toString()}>
|
||||
<Modal.Open
|
||||
modalName={FORM.EDIT}
|
||||
renderChildren={renderEditBtn}
|
||||
/>
|
||||
|
||||
<Modal.Open
|
||||
modalName={FORM.CHECKOUT}
|
||||
renderChildren={renderCheckOutBtn}
|
||||
/>
|
||||
</Menus.List>
|
||||
|
||||
<Modal.Window name={FORM.EDIT} title="Edit Booking">
|
||||
<BookingForm booking={booking} key={booking.id} />
|
||||
</Modal.Window>
|
||||
|
||||
<Modal.Window name={FORM.CHECKOUT} title="Checkout">
|
||||
<ConfirmMessage
|
||||
message={`Are you sure to checkout this user? '${booking.users?.name}'`}
|
||||
onConfirm={handleClickCheckOutBtn}
|
||||
/>
|
||||
</Modal.Window>
|
||||
</Menus.Menu>
|
||||
</Modal>
|
||||
</div>
|
||||
</Table.Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default BookingRow;
|
||||
@@ -0,0 +1,73 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
// Components
|
||||
import Menus from '@component/Menus';
|
||||
import Table from '@component/Table';
|
||||
import Message from '@component/Message';
|
||||
import Search from '@component/Search';
|
||||
import Pagination from '@component/Pagination';
|
||||
import BookingRow from './BookingRow';
|
||||
|
||||
// Styled
|
||||
import { StyledOperationTable } from './styled';
|
||||
import Direction from '@commonStyle/Direction';
|
||||
import Spinner from '@commonStyle/Spinner';
|
||||
|
||||
// Hooks
|
||||
import { useBookings } from '@hook/bookings/useBookings';
|
||||
|
||||
// Types
|
||||
import { TBookingResponse } from '@type/booking';
|
||||
|
||||
const BookingTable = () => {
|
||||
const columnName = [
|
||||
'User',
|
||||
'Date',
|
||||
'Room',
|
||||
'Amount',
|
||||
'Status'
|
||||
];
|
||||
const {
|
||||
isLoading,
|
||||
bookings,
|
||||
count
|
||||
} = useBookings();
|
||||
|
||||
const renderBookingRow = useCallback(
|
||||
(booking: TBookingResponse) => (
|
||||
<BookingRow booking={booking} key={booking.id} />
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Direction>
|
||||
<StyledOperationTable>
|
||||
<Search setPlaceHolder="Search by name..." />
|
||||
</StyledOperationTable>
|
||||
|
||||
{isLoading && <Spinner />}
|
||||
|
||||
{bookings && bookings.length ? (
|
||||
<Menus>
|
||||
<Table columns="15% 25% 20% 15% 10% 10% 5%">
|
||||
<Table.Header headerColumn={columnName} />
|
||||
<Table.Body<TBookingResponse>
|
||||
data={bookings}
|
||||
render={renderBookingRow}
|
||||
/>
|
||||
<Table.Footer>
|
||||
<Pagination count={count!} />
|
||||
</Table.Footer>
|
||||
</Table>
|
||||
</Menus>
|
||||
) : (
|
||||
!isLoading && <Message>No data to show here!</Message>
|
||||
)}
|
||||
</Direction>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BookingTable;
|
||||
@@ -0,0 +1,38 @@
|
||||
// Styled
|
||||
import Direction from '@commonStyle/Direction';
|
||||
import { StyledBooking, Title } from './styled';
|
||||
import Button from '@commonStyle/Button';
|
||||
|
||||
// Components
|
||||
import Modal from '@component/Modal';
|
||||
import BookingTable from './BookingTable';
|
||||
import BookingForm from './BookingForm';
|
||||
|
||||
// Constants
|
||||
import { FORM } from '@constant/commons';
|
||||
|
||||
const Booking = () => {
|
||||
return (
|
||||
<StyledBooking>
|
||||
<Direction type="horizontal">
|
||||
<Title>List Booking</Title>
|
||||
|
||||
<Modal>
|
||||
<Modal.Open
|
||||
modalName={FORM.BOOKING}
|
||||
renderChildren={(onCloseModal) => (
|
||||
<Button onClick={onCloseModal}>Add booking</Button>
|
||||
)}
|
||||
/>
|
||||
<Modal.Window name={FORM.BOOKING} title="Add form">
|
||||
<BookingForm />
|
||||
</Modal.Window>
|
||||
</Modal>
|
||||
</Direction>
|
||||
|
||||
<BookingTable />
|
||||
</StyledBooking>
|
||||
);
|
||||
};
|
||||
|
||||
export default Booking;
|
||||
@@ -0,0 +1,24 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledBooking = styled.main`
|
||||
padding: 20px;
|
||||
padding-bottom: 100px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
`;
|
||||
|
||||
const Title = styled.h2`
|
||||
font-size: var(--fs-md);
|
||||
color: var(--dark-text);
|
||||
text-transform: capitalize;
|
||||
`;
|
||||
|
||||
const StyledOperationTable = styled.div`
|
||||
display: flex;
|
||||
gap: 30px;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
export { StyledBooking, Title, StyledOperationTable };
|
||||
@@ -1,23 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
// Hooks
|
||||
import { useUserRoomAvailable } from '@hook/useUserRoomAvailable';
|
||||
|
||||
const StyledDashboard = styled.main`
|
||||
padding: 20px;
|
||||
`;
|
||||
|
||||
const Dashboard = () => {
|
||||
const { roomsAvailable, usersAvailable } = useUserRoomAvailable();
|
||||
|
||||
console.log('Rooms Available: ', roomsAvailable);
|
||||
console.log('Users available: ', usersAvailable);
|
||||
|
||||
return (
|
||||
<StyledDashboard>
|
||||
<p>This page currently still develop. Please try again later!</p>
|
||||
</StyledDashboard>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
@@ -27,7 +27,7 @@ interface IRoomRow {
|
||||
|
||||
const RoomRow = ({ room }: IRoomRow) => {
|
||||
const { id, name, price, status } = room;
|
||||
const { isDeleting, deleteRoom } = useDeleteRoom();
|
||||
const { deleteRoom } = useDeleteRoom();
|
||||
const statusText = status
|
||||
? 'Unavailable'
|
||||
: 'Available';
|
||||
@@ -78,7 +78,6 @@ const RoomRow = ({ room }: IRoomRow) => {
|
||||
|
||||
<Modal.Window name={FORM.DELETE} title="Delete Room">
|
||||
<ConfirmMessage
|
||||
disabled={isDeleting}
|
||||
message={`Are you sure to delete ${name}?`}
|
||||
onConfirm={() => deleteRoom(id)}
|
||||
/>
|
||||
|
||||
@@ -10,27 +10,25 @@ import Button from '@commonStyle/Button';
|
||||
|
||||
const Room = () => {
|
||||
return (
|
||||
<>
|
||||
<StyledRoom>
|
||||
<Direction type="horizontal">
|
||||
<Title>List Room</Title>
|
||||
<StyledRoom>
|
||||
<Direction type="horizontal">
|
||||
<Title>List Room</Title>
|
||||
|
||||
<Modal>
|
||||
<Modal.Open
|
||||
modalName="room-form"
|
||||
renderChildren={(onCloseModal) => (
|
||||
<Button onClick={onCloseModal}>Add room</Button>
|
||||
)}
|
||||
/>
|
||||
<Modal.Window name="room-form" title="Add form">
|
||||
<RoomForm />
|
||||
</Modal.Window>
|
||||
</Modal>
|
||||
</Direction>
|
||||
<Modal>
|
||||
<Modal.Open
|
||||
modalName="room-form"
|
||||
renderChildren={(onCloseModal) => (
|
||||
<Button onClick={onCloseModal}>Add room</Button>
|
||||
)}
|
||||
/>
|
||||
<Modal.Window name="room-form" title="Add form">
|
||||
<RoomForm />
|
||||
</Modal.Window>
|
||||
</Modal>
|
||||
</Direction>
|
||||
|
||||
<RoomTable />
|
||||
</StyledRoom>
|
||||
</>
|
||||
<RoomTable />
|
||||
</StyledRoom>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -5,9 +5,13 @@ import Table from '@component/Table';
|
||||
import Menus from '@component/Menus';
|
||||
import UserForm from './UserForm';
|
||||
|
||||
|
||||
// Types
|
||||
import { IUser } from '@type/users';
|
||||
import { FORM } from '@constant/commons';
|
||||
import { HiTrash } from 'react-icons/hi';
|
||||
import { useCallback } from 'react';
|
||||
import ConfirmMessage from '@component/ConfirmMessage';
|
||||
import { useDeleteUser } from '@hook/users/useDeleteUser';
|
||||
|
||||
interface IUserRow {
|
||||
user: IUser;
|
||||
@@ -15,17 +19,35 @@ interface IUserRow {
|
||||
|
||||
const UserRow = ({ user }: IUserRow) => {
|
||||
const { id, name, phone, isBooked } = user;
|
||||
const { deleteUser } = useDeleteUser();
|
||||
|
||||
const renderEditBtn = useCallback(
|
||||
(onCloseModal: () => void) => (
|
||||
<Menus.Button onClick={onCloseModal} icon={<RiEditBoxFill />}>
|
||||
Edit
|
||||
</Menus.Button>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
const renderDeleteBtn = useCallback(
|
||||
(onCloseModal: () => void) => (
|
||||
<Menus.Button icon={<HiTrash />} onClick={onCloseModal}>
|
||||
Delete
|
||||
</Menus.Button>
|
||||
),
|
||||
[]
|
||||
);
|
||||
return (
|
||||
<Table.Row>
|
||||
<div>{id}</div>
|
||||
<div>{name}</div>
|
||||
<div>{phone}</div>
|
||||
<div>{
|
||||
isBooked
|
||||
? 'Yes'
|
||||
isBooked
|
||||
? 'Yes'
|
||||
: 'No'
|
||||
}</div>
|
||||
}</div>
|
||||
<div>
|
||||
<Modal>
|
||||
<Menus.Menu>
|
||||
@@ -33,18 +55,26 @@ const UserRow = ({ user }: IUserRow) => {
|
||||
|
||||
<Menus.List id={id.toString()}>
|
||||
<Modal.Open
|
||||
modalName="edit"
|
||||
renderChildren={(onCloseModal) => (
|
||||
<Menus.Button onClick={onCloseModal} icon={<RiEditBoxFill />}>
|
||||
Edit
|
||||
</Menus.Button>
|
||||
)}
|
||||
modalName={FORM.EDIT}
|
||||
renderChildren={renderEditBtn}
|
||||
/>
|
||||
|
||||
<Modal.Open
|
||||
modalName={FORM.DELETE}
|
||||
renderChildren={renderDeleteBtn}
|
||||
/>
|
||||
</Menus.List>
|
||||
|
||||
<Modal.Window name="edit" title="Edit user">
|
||||
<Modal.Window name={FORM.EDIT} title="Edit user">
|
||||
<UserForm user={user} />
|
||||
</Modal.Window>
|
||||
|
||||
<Modal.Window name={FORM.DELETE} title="Delete User">
|
||||
<ConfirmMessage
|
||||
message={`Are you sure to delete ${name}?`}
|
||||
onConfirm={() => deleteUser(id)}
|
||||
/>
|
||||
</Modal.Window>
|
||||
</Menus.Menu>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user