Update method for user pages and format code

This commit is contained in:
2023-11-19 12:52:07 +07:00
parent 9a9617bb16
commit ce81f7c7f3
36 changed files with 565 additions and 727 deletions
+25 -15
View File
@@ -1,9 +1,25 @@
import styled, { css } from 'styled-components'; import styled, { RuleSet, css } from 'styled-components';
type TButtonStyle = 'primary' | 'secondary'; interface IVariations {
[key: string]: RuleSet<object>;
}
const variations: IVariations = {
primary: css`
background-color: var(--primary-color);
color: var(--light-text);
`,
secondary: css`
background-color: var(--secondary-btn-color);
`,
danger: css`
background-color: var(--danger-btn-color);
color: var(--light-text);
`,
};
interface IButtonStyle { interface IButtonStyle {
styled?: TButtonStyle; variations?: keyof IVariations;
} }
const Button = styled.button<IButtonStyle>` const Button = styled.button<IButtonStyle>`
@@ -16,22 +32,16 @@ const Button = styled.button<IButtonStyle>`
border-radius: var(--radius-md); border-radius: var(--radius-md);
${(props) => ${(props) => variations[props.variations!]}
props.styled === 'primary' &&
css`
background-color: var(--primary-color);
color: var(--light-text);
`}
${(props) => &:disabled,
props.styled === 'secondary' && &[disabled] {
css` cursor: no-drop;
background-color: var(--secondary-btn-color); }
`}
`; `;
Button.defaultProps = { Button.defaultProps = {
styled: 'primary', variations: 'primary',
}; };
export default Button; export default Button;
@@ -0,0 +1,40 @@
import { memo } from 'react';
// Styled
import { StyledConfirmDelete } from './styled';
// Component
import Button from '@commonStyle/Button';
interface IConfirmMessage {
message: string;
disabled: boolean;
onConfirm: () => void;
onCloseModal?: () => void;
}
const ConfirmMessage = memo(
({ message, disabled, onConfirm, onCloseModal }: IConfirmMessage) => {
return (
<StyledConfirmDelete>
<p>{message}</p>
<div>
<Button variations="danger" disabled={disabled} onClick={onConfirm}>
Delete
</Button>
<Button
variations="secondary"
disabled={disabled}
onClick={onCloseModal}
>
Cancel
</Button>
</div>
</StyledConfirmDelete>
);
}
);
export default ConfirmMessage;
@@ -0,0 +1,20 @@
import styled from "styled-components";
const StyledConfirmDelete = styled.div`
width: 450px;
& p {
font-size: var(--fs-sm);
text-align: center;
margin-bottom: 30px;
}
& div {
display: flex;
justify-content: space-around;
padding-inline: 50px;
}
`;
export { StyledConfirmDelete };
@@ -5,19 +5,26 @@ import { StyledSearch } from './styled';
// Hooks // Hooks
import { useDebounce } from '@hook/useDebounce'; import { useDebounce } from '@hook/useDebounce';
import { useSearchParams } from 'react-router-dom';
import { Nullable } from '@type/common';
interface ISearch { interface ISearch {
setPlaceHolder: string; setPlaceHolder: string;
setValueSearch: (phone: string) => void;
} }
const Search = ({ setValueSearch, setPlaceHolder }: ISearch) => { const Search = ({ setPlaceHolder }: ISearch) => {
const [query, setQuery] = useState(''); const field = 'search';
const debounceValue = useDebounce<string>(query, 700); const [searchParams, setSearchParams] = useSearchParams();
const [query, setQuery] = useState<Nullable<string>>(null);
const debounceValue = useDebounce<Nullable<string>>(query, 700);
useEffect(() => { useEffect(() => {
setValueSearch(debounceValue); if (debounceValue !== null) {
}, [debounceValue, setValueSearch]); searchParams.set(field, debounceValue);
setSearchParams(searchParams);
}
}, [debounceValue, searchParams, setSearchParams]);
return ( return (
<StyledSearch <StyledSearch
@@ -41,7 +41,7 @@ const Toast = ({
Toast.defaultProps = { Toast.defaultProps = {
position: 'top-center', position: 'top-center',
gutter: 12, gutter: 12,
containerStyle: { margin: '8px', zIndex: 1 }, containerStyle: { margin: '8px', zIndex: 2000 },
success: { success: {
duration: 3000, duration: 3000,
}, },
+2 -1
View File
@@ -37,7 +37,7 @@ const ROOM_PAGE = {
label: 'Sort by name', label: 'Sort by name',
}, },
{ {
value: 'finalPrice', value: 'price',
label: 'Sort by price', label: 'Sort by price',
}, },
], ],
@@ -59,6 +59,7 @@ const INIT_VALUE_ROOM_FORM = {
const FORM = { const FORM = {
EDIT: 'edit', EDIT: 'edit',
DELETE: 'delete',
USER: 'user-form', USER: 'user-form',
ROOM: 'room-form', ROOM: 'room-form',
}; };
+3 -2
View File
@@ -1,5 +1,6 @@
const TIME_OUT_SEC = 1;
const DEFAULT_SORT_BY = 'id'; const DEFAULT_SORT_BY = 'id';
const DEFAULT_ORDER_BY = 'asc'; const DEFAULT_ORDER_BY = 'asc';
const supabaseUrl = 'https://pjqujsjzzdrlrgqbxepe.supabase.co'
const supabaseKey = import.meta.env.VITE_SUPABASE_KEY
export { TIME_OUT_SEC, DEFAULT_SORT_BY, DEFAULT_ORDER_BY }; export { DEFAULT_SORT_BY, DEFAULT_ORDER_BY, supabaseKey, supabaseUrl };
+2 -4
View File
@@ -1,17 +1,15 @@
const ADD_SUCCESS = 'Add success'; const ADD_SUCCESS = 'Add success';
const EDIT_SUCCESS = 'Edit success'; const UPDATE_SUCCESS = 'Update success';
const CONFIRM_MESSAGE = 'Do you want to checkout this user?'; const CONFIRM_MESSAGE = 'Do you want to checkout this user?';
const CONFIRM_DELETE = 'Are you sure to delete it?' const CONFIRM_DELETE = 'Are you sure to delete it?'
const DELETE_SUCCESS = 'Delete success'; const DELETE_SUCCESS = 'Delete success';
const CHECKOUT_SUCCESS = 'Check out success'; const CHECKOUT_SUCCESS = 'Check out success';
const DENIED_ACTION = "Can't checkout user not in room!";
export { export {
ADD_SUCCESS, ADD_SUCCESS,
EDIT_SUCCESS, UPDATE_SUCCESS,
CONFIRM_MESSAGE, CONFIRM_MESSAGE,
CONFIRM_DELETE, CONFIRM_DELETE,
DELETE_SUCCESS, DELETE_SUCCESS,
CHECKOUT_SUCCESS, CHECKOUT_SUCCESS,
DENIED_ACTION
}; };
-1
View File
@@ -2,6 +2,5 @@ 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 USER_PATH = 'users'; export const USER_PATH = 'users';
export const ROOM_PATH = 'rooms'; export const ROOM_PATH = 'rooms';
@@ -1,11 +0,0 @@
const STATUS_CODE = {
OK: 200,
CREATE: 201,
NOT_FOUND: 404,
};
const RESPONSE_MESSAGE = {
UPDATE_SUCCESS: 'Update success',
};
export { STATUS_CODE, RESPONSE_MESSAGE };
@@ -1,7 +0,0 @@
import { createClient } from '@supabase/supabase-js'
import { Database } from '@type/supabase';
const supabaseUrl = 'https://pjqujsjzzdrlrgqbxepe.supabase.co'
const supabaseKey = import.meta.env.VITE_SUPABASE_KEY
const supabase = createClient<Database>(supabaseUrl, supabaseKey!)
export default supabase;
+1 -14
View File
@@ -1,6 +1,3 @@
// Constants
import { REQUIRED_FIELD_ERROR } from '../constants/formValidateMessage';
/** /**
* Convert value to currency format with value * Convert value to currency format with value
* @param value Value need to be converted * @param value Value need to be converted
@@ -13,14 +10,4 @@ const formatCurrency = (value: number): string => {
}).format(value); }).format(value);
}; };
/** export { formatCurrency };
* Create error string
* @param errorCode Error code
* @param msg Message error
* @returns Return the full error string
*/
const errorMsg = (errorCode: number, msg: string) => {
return `Error code: ${errorCode}. Message: ${msg}`;
};
export { errorMsg, formatCurrency, REQUIRED_FIELD_ERROR };
@@ -1,37 +0,0 @@
// Constants
import { BASE_URL } from '@constant/path';
// Types
import { IResponse } from '@type/responses';
type TMethodRequest = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
/**
* The send request method to the server
* @param path The path of URL
* @param body The content will push on
* @param method HTTP method
* @returns The status code and message from server
*/
export const sendRequest = async <T>(
path: string,
method: TMethodRequest = 'GET',
body?: BodyInit
): Promise<IResponse<T>> => {
const response = await fetch(BASE_URL + path, {
method,
body,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
});
const data = (await response.json()) as T;
return {
statusCode: response.status,
msg: response.statusText,
data,
};
};
+2 -17
View File
@@ -6,7 +6,7 @@
*/ */
const isValidRegex = (regex: RegExp, value: string): boolean => { const isValidRegex = (regex: RegExp, value: string): boolean => {
return regex.test(value); return regex.test(value);
} };
/** /**
* Check value is valid discount or not * Check value is valid discount or not
@@ -17,7 +17,6 @@ const isValidDiscount = (value: number): boolean => {
return value >= 0 && value <= 100; return value >= 0 && value <= 100;
}; };
/** /**
* Check value is valid string or not * Check value is valid string or not
* @param value Value need to be checked * @param value Value need to be checked
@@ -27,18 +26,4 @@ const isValidString = (value: string): boolean => {
return value.trim().length >= 5; return value.trim().length >= 5;
}; };
/** export { isValidRegex, isValidString, isValidDiscount };
* Check object is empty or not
* @param obj Object need to be check
* @returns A boolean indicating whether or not the argument has valid
*/
const isEmptyObj = (obj: object) => {
return Object.keys(obj).length === 0;
};
export {
isValidRegex,
isValidString,
isValidDiscount,
isEmptyObj,
};
@@ -0,0 +1,29 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import toast from 'react-hot-toast';
// Services
import { createRoom as createRoomFn } from '@service/roomServices';
// Constants
import { ADD_SUCCESS } from '@constant/messages';
/**
* Create room on database
* @returns The boolean of isCreating and create room function
*/
const useCreateRoom = () => {
const queryClient = useQueryClient();
const { mutate: createRoom, isPending: isCreating } = useMutation({
mutationFn: createRoomFn,
onSuccess: () => {
toast.success(ADD_SUCCESS);
queryClient.invalidateQueries({ queryKey: ['rooms'] });
},
onError: (err) => toast.error(err.message),
});
return { isCreating, createRoom };
};
export { useCreateRoom };
@@ -0,0 +1,31 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import toast from 'react-hot-toast';
// Services
import { deleteRoom as deleteRoomFn } from '@service/roomServices';
// Constants
import * as messages from '@constant/messages';
/**
* Delete the room from database
* @returns The boolean of status deleting and deleteRoom function
*/
const useDeleteRoom = () => {
const queryClient = useQueryClient();
const { isPending: isDeleting, mutate: deleteRoom } = useMutation({
mutationFn: deleteRoomFn,
onSuccess: () => {
toast.success(messages.DELETE_SUCCESS);
queryClient.invalidateQueries({
queryKey: ['rooms'],
});
},
onError: (err) => toast.error(err.message),
});
return { isDeleting, deleteRoom };
};
export { useDeleteRoom };
@@ -0,0 +1,34 @@
import toast from "react-hot-toast";
import { useQuery } from "@tanstack/react-query";
import { useSearchParams } from "react-router-dom";
// Services
import { getAllRooms } from "@service/roomServices";
/**
* Fetch room from database
* @returns The status of loading rooms from database and data of rooms
*/
const useRooms = () => {
const [searchParams] = useSearchParams();
const sortByValue = searchParams.get('sortBy') || 'id';
const orderByValue = searchParams.get('orderBy') || 'asc';
const phoneSearch = searchParams.get('search') || ''
const {
isLoading,
data: rooms,
error,
} = useQuery({
queryKey: ['rooms', sortByValue, orderByValue, phoneSearch],
queryFn: () => getAllRooms(sortByValue, orderByValue, phoneSearch),
});
if (error) {
toast.error(error.message);
}
return { isLoading, rooms };
};
export { useRooms };
@@ -0,0 +1,29 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import toast from 'react-hot-toast';
// Services
import { updateRoom as updateRoomFn } from '@service/roomServices';
// Constants
import { UPDATE_SUCCESS } from '@constant/messages';
/**
* Update room from database
* @returns The status of updating and updateRoom function
*/
const useUpdateRoom = () => {
const queryClient = useQueryClient();
const { mutate: updateRoom, isPending: isUpdating } = useMutation({
mutationFn: updateRoomFn,
onSuccess: () => {
toast.success(UPDATE_SUCCESS);
queryClient.invalidateQueries({ queryKey: ['rooms'] });
},
onError: (err) => toast.error(err.message),
});
return { isUpdating, updateRoom };
};
export { useUpdateRoom };
@@ -1,25 +0,0 @@
import { ForwardedRef, useEffect, useRef } from 'react';
// Types
import { Nullable } from '@type/common';
const useForwardRef = <T>(
ref: ForwardedRef<T>,
initialValue: Nullable<T> = null
) => {
const targetRef = useRef<T>(initialValue);
useEffect(() => {
if (!ref) return;
if (typeof ref === 'function') {
ref(targetRef.current);
} else {
ref.current = targetRef.current;
}
}, [ref]);
return targetRef;
};
export { useForwardRef };
@@ -0,0 +1,29 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import toast from 'react-hot-toast';
// Services
import { createUser as createUserFn } from '@service/userServices';
// Constants
import { ADD_SUCCESS } from '@constant/messages';
/**
* Create user in database
* @returns The status of creating users and createUser function
*/
const useCreateUser = () => {
const queryClient = useQueryClient();
const { mutate: createUser, isPending: isCreating } = useMutation({
mutationFn: createUserFn,
onSuccess: () => {
toast.success(ADD_SUCCESS);
queryClient.invalidateQueries({ queryKey: ['users'] });
},
onError: (err) => toast.error(err.message),
});
return { isCreating, createUser };
};
export { useCreateUser };
@@ -0,0 +1,29 @@
import toast from 'react-hot-toast';
import { useMutation, useQueryClient } from '@tanstack/react-query';
// Services
import { updateUser as updateUserFn } from '@service/userServices';
// Messages
import { UPDATE_SUCCESS } from '@constant/messages';
/**
* Update user from database
* @returns The status of updating user and updateUser function
*/
const useUpdateUser = () => {
const queryClient = useQueryClient();
const { mutate: updateUser, isPending: isUpdating } = useMutation({
mutationFn: updateUserFn,
onSuccess: () => {
toast.success(UPDATE_SUCCESS);
queryClient.invalidateQueries({ queryKey: ['users'] });
},
onError: (err) => toast.error(err.message),
});
return { isUpdating, updateUser };
};
export { useUpdateUser };
@@ -0,0 +1,34 @@
import toast from "react-hot-toast";
import { useQuery } from "@tanstack/react-query";
import { useSearchParams } from "react-router-dom";
// Services
import { getAllUsers } from "@service/userServices";
/**
* Fetch data of users from database
* @returns The status of loading users and data of users
*/
const useUsers = () => {
const [searchParams] = useSearchParams();
const sortByValue = searchParams.get('sortBy') || 'id';
const orderByValue = searchParams.get('orderBy') || 'asc';
const phoneSearch = searchParams.get('search') || ''
const {
isLoading,
data: users,
error,
} = useQuery({
queryKey: ['users', sortByValue, orderByValue, phoneSearch],
queryFn: () => getAllUsers(sortByValue, orderByValue, phoneSearch),
});
if (error) {
toast.error(error.message);
}
return { isLoading, users };
};
export { useUsers };
+16 -36
View File
@@ -1,5 +1,4 @@
import { useSearchParams } from 'react-router-dom'; import { useCallback } from 'react';
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
// Components // Components
import Menus from '@component/Menus'; import Menus from '@component/Menus';
@@ -21,23 +20,17 @@ import { StyledOperationTable } from './styled';
import Direction from '@commonStyle/Direction'; import Direction from '@commonStyle/Direction';
import Spinner from '@commonStyle/Spinner'; import Spinner from '@commonStyle/Spinner';
// Hooks
import { useRooms } from '@hook/rooms/useRooms';
interface IRoomTable { const RoomTable = () => {
reload: boolean;
setReload: Dispatch<SetStateAction<boolean>>;
}
const RoomTable = ({ reload, setReload }: IRoomTable) => {
const columnName = ['Id', 'Name', 'Price', 'Status']; const columnName = ['Id', 'Name', 'Price', 'Status'];
const [nameSearch, setNameSearch] = useState(''); const { isLoading, rooms } = useRooms();
const [searchParams] = useSearchParams();
const [rooms, setRooms] = useState<IRoom[]>([]); const renderRoomRow = useCallback(
const sortByValue = searchParams.get('sortBy') (room: IRoom) => <RoomRow room={room} key={room.id} />,
? searchParams.get('sortBy')! []
: ''; );
const orderByValue = searchParams.get('orderBy')
? searchParams.get('orderBy')!
: '';
return ( return (
<> <>
@@ -46,33 +39,20 @@ const RoomTable = ({ reload, setReload }: IRoomTable) => {
<OrderBy options={ORDERBY_OPTIONS} /> <OrderBy options={ORDERBY_OPTIONS} />
<SortBy options={ROOM_PAGE.SORTBY_OPTIONS} /> <SortBy options={ROOM_PAGE.SORTBY_OPTIONS} />
<Search <Search setPlaceHolder="Search by name..." />
setValueSearch={setNameSearch}
setPlaceHolder="Search by name..."
/>
</StyledOperationTable> </StyledOperationTable>
{isPending && <Spinner />} {isLoading && <Spinner />}
{rooms.length ? ( {rooms && rooms.length ? (
<Menus> <Menus>
<Table columns="10% 40% 20% 20% 5%"> <Table columns="10% 40% 20% 20% 5%">
<Table.Header headerColumn={columnName}/> <Table.Header headerColumn={columnName} />
<Table.Body<IRoom> <Table.Body<IRoom> data={rooms} render={renderRoomRow} />
data={rooms}
render={(room: IRoom) => (
<RoomRow
room={room}
key={room.id}
reload={reload}
setReload={setReload}
/>
)}
/>
</Table> </Table>
</Menus> </Menus>
) : ( ) : (
!isPending && <Message>No data to show here!</Message> !isLoading && <Message>No data to show here!</Message>
)} )}
</Direction> </Direction>
</> </>
+40 -77
View File
@@ -1,40 +1,30 @@
import { Dispatch, SetStateAction, useEffect } from 'react';
import toast from 'react-hot-toast';
import styled from 'styled-components'; import styled from 'styled-components';
// Hooks
import { useForm } from 'react-hook-form';
// Styled // Styled
import Button from '@commonStyle/Button.ts'; import Button from '@commonStyle/Button.ts';
import Input from '@commonStyle/Input.ts'; import Input from '@commonStyle/Input.ts';
// Types // Types
import { Nullable } from '@type/common.ts';
import { IRoom } from '@type/rooms.ts'; import { IRoom } from '@type/rooms.ts';
// Constants // Constants
import { ADD_SUCCESS, EDIT_SUCCESS } from '@constant/messages.ts';
import { import {
INVALID_DISCOUNT,
INVALID_FIELD, INVALID_FIELD,
REQUIRED_FIELD_ERROR, REQUIRED_FIELD_ERROR,
} from '@constant/formValidateMessage.ts'; } from '@constant/formValidateMessage.ts';
import { INIT_VALUE_ROOM_FORM, REGEX } from '../../constants/commons.ts'; import { REGEX } from '../../constants/commons.ts';
// Helpers // Helpers
import { import { isValidRegex, isValidString } from '@helper/validators.ts';
isValidDiscount,
isValidRegex,
isValidString,
} from '@helper/validators.ts';
// Components // Components
import FormRow from '@component/LabelControl/index.tsx'; import FormRow from '@component/LabelControl/index.tsx';
import Form from '@component/Form/index.tsx'; import Form from '@component/Form/index.tsx';
// Services // Hooks
import { addRoom, updateRoom } from '@service/roomServices.ts'; import { useForm } from 'react-hook-form';
import { useCreateRoom } from '@hook/rooms/useCreateRoom.ts';
import { useUpdateRoom } from '@hook/rooms/useUpdateRoom.ts';
const FormBtn = styled(Button)` const FormBtn = styled(Button)`
width: 100%; width: 100%;
@@ -48,65 +38,51 @@ const FormBtn = styled(Button)`
interface IRoomFormProp { interface IRoomFormProp {
onCloseModal?: () => void; onCloseModal?: () => void;
reload?: boolean; room?: IRoom;
setReload?: Dispatch<SetStateAction<boolean>>;
roomEdit?: Nullable<IRoom>;
} }
const RoomForm = ({ const RoomForm = ({ onCloseModal, room }: IRoomFormProp) => {
onCloseModal, const { isCreating, createRoom } = useCreateRoom();
reload, const { isUpdating, updateRoom } = useUpdateRoom();
setReload, const isLoading = isCreating || isUpdating;
roomEdit, const { id: editId, ...editValues } = { ...room };
}: IRoomFormProp) => { const formMethods = useForm<IRoom>({
const formMethods = useForm<IRoom>(); defaultValues: editId
? editValues
: {},
});
const { const {
register, register,
handleSubmit, handleSubmit,
reset, reset,
formState: { errors, isDirty, isValid, isSubmitting }, formState: { errors, isDirty, isValid },
trigger, trigger,
} = formMethods; } = formMethods;
useEffect(() => {
if (roomEdit) {
reset(roomEdit);
} else {
reset(INIT_VALUE_ROOM_FORM);
}
}, [roomEdit, reset]);
// Submit form // Submit form
const onSubmit = async (room: IRoom) => { const onSubmit = async (newRoom: IRoom) => {
// Calculate final price if (!editId) {
room.finalPrice = room.price - (room.price * room.discount) / 100;
if (!roomEdit) {
// Add request // Add request
const response = await addRoom(room); createRoom(newRoom, {
onSuccess: () => {
if (response) { reset();
toast.success(ADD_SUCCESS); onCloseModal?.();
} },
});
} else { } else {
// Edit request // Edit request
const response = await updateRoom(room); newRoom.id = editId!;
updateRoom(newRoom, {
if (response) { onSuccess: () => {
toast.success(EDIT_SUCCESS); reset();
} onCloseModal?.();
},
});
} }
// Reload table data
setReload!(!reload);
reset();
onCloseModal!();
}; };
return ( return (
<Form onSubmit={handleSubmit(onSubmit)}> <Form onSubmit={handleSubmit(onSubmit)}>
<Input type="hidden" id="id" {...register('id')} />
<FormRow label="Name" error={errors?.name?.message}> <FormRow label="Name" error={errors?.name?.message}>
<Input <Input
type="text" type="text"
@@ -114,7 +90,8 @@ const RoomForm = ({
{...register('name', { {...register('name', {
required: REQUIRED_FIELD_ERROR, required: REQUIRED_FIELD_ERROR,
validate: { validate: {
checkValidRoomName: (value) => isValidString(value) || INVALID_FIELD, checkValidRoomName: (value) =>
isValidString(value) || INVALID_FIELD,
}, },
onChange: () => trigger('name'), onChange: () => trigger('name'),
})} })}
@@ -130,41 +107,27 @@ const RoomForm = ({
required: REQUIRED_FIELD_ERROR, required: REQUIRED_FIELD_ERROR,
validate: { validate: {
checkPrice: (v) => checkPrice: (v) =>
isValidRegex(new RegExp(REGEX.NUMBER), v.toString()) || INVALID_FIELD, isValidRegex(new RegExp(REGEX.NUMBER), v.toString()) ||
INVALID_FIELD,
}, },
onChange: () => trigger('price'), onChange: () => trigger('price'),
})} })}
/> />
</FormRow> </FormRow>
<FormRow label="discount" error={errors?.discount?.message}>
<Input
type="text"
id="phone"
{...register('discount', {
valueAsNumber: true,
required: REQUIRED_FIELD_ERROR,
validate: {
checkDiscount: (v) => isValidDiscount(v) || INVALID_DISCOUNT,
},
onChange: () => trigger('discount'),
})}
/>
</FormRow>
<Form.Action> <Form.Action>
<FormBtn <FormBtn
type="submit" type="submit"
name="submit" name="submit"
disabled={!isDirty || !isValid || isSubmitting} disabled={!isDirty || !isValid || isLoading}
> >
{ {
!roomEdit !editId
? 'Add' ? 'Add'
: 'Save' : 'Save'
} }
</FormBtn> </FormBtn>
<FormBtn type="button" styled="secondary" onClick={onCloseModal}> <FormBtn type="button" variations="secondary" onClick={onCloseModal}>
Close Close
</FormBtn> </FormBtn>
</Form.Action> </Form.Action>
+45 -42
View File
@@ -1,13 +1,6 @@
import { Dispatch, SetStateAction } from 'react'; import { useCallback, useMemo } from 'react';
import toast from 'react-hot-toast';
// Constants
import { ROOM_PATH } from '@constant/path';
import { STATUS_CODE } from '@constant/responseStatus';
import { CONFIRM_DELETE, DELETE_SUCCESS } from '@constant/messages';
// Helpers // Helpers
import { sendRequest } from '@helper/sendRequest';
import { formatCurrency } from '@helper/helper'; import { formatCurrency } from '@helper/helper';
// Types // Types
@@ -20,38 +13,47 @@ import Table from '@component/Table';
import Menus from '@component/Menus'; import Menus from '@component/Menus';
import { RiEditBoxFill } from 'react-icons/ri'; import { RiEditBoxFill } from 'react-icons/ri';
import { HiTrash } from 'react-icons/hi'; import { HiTrash } from 'react-icons/hi';
import ConfirmMessage from '@component/ConfirmMessage';
// Hooks
import { useDeleteRoom } from '@hook/rooms/useDeleteRoom';
// Constants
import { FORM } from '@constant/commons';
interface IRoomRow { interface IRoomRow {
room: IRoom; room: IRoom;
reload: boolean;
setReload: Dispatch<SetStateAction<boolean>>;
} }
const RoomRow = ({ room, reload, setReload }: IRoomRow) => { const RoomRow = ({ room }: IRoomRow) => {
const handleDelete = async (room: IRoom) => { const { id, name, price, status } = room;
if (confirm(CONFIRM_DELETE)) { const { isDeleting, deleteRoom } = useDeleteRoom();
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 const statusText = status
? 'Unavailable' ? 'Unavailable'
: 'Available'; : 'Available';
const formattedPrice = useMemo(() => formatCurrency(price), [price]);
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 ( return (
<Table.Row> <Table.Row>
<div>{id}</div> <div>{id}</div>
<div>{name}</div> <div>{name}</div>
<div>{formatCurrency(finalPrice)}</div> <div>{formattedPrice}</div>
<div>{statusText}</div> <div>{statusText}</div>
<div> <div>
@@ -61,24 +63,25 @@ const RoomRow = ({ room, reload, setReload }: IRoomRow) => {
<Menus.List id={id.toString()}> <Menus.List id={id.toString()}>
<Modal.Open <Modal.Open
modalName="edit" modalName={FORM.EDIT}
renderChildren={(onCloseModal) => ( renderChildren={renderEditBtn}
<Menus.Button onClick={onCloseModal} icon={<RiEditBoxFill />}> />
Edit <Modal.Open
</Menus.Button> modalName={FORM.DELETE}
)} renderChildren={renderDeleteBtn}
/> />
<Menus.Button
icon={<HiTrash />}
onClick={() => handleDelete(room)}
>
Delete
</Menus.Button>
</Menus.List> </Menus.List>
<Modal.Window name="edit" title="Edit Room"> <Modal.Window name={FORM.EDIT} title="Edit Room">
<RoomForm roomEdit={room} setReload={setReload} reload={reload} /> <RoomForm room={room} />
</Modal.Window>
<Modal.Window name={FORM.DELETE} title="Delete Room">
<ConfirmMessage
disabled={isDeleting}
message={`Are you sure to delete ${name}?`}
onConfirm={() => deleteRoom(id)}
/>
</Modal.Window> </Modal.Window>
</Menus.Menu> </Menus.Menu>
</Modal> </Modal>
+3 -9
View File
@@ -1,20 +1,14 @@
import { useState } from 'react';
// Components // Components
import RoomTable from './RomTable'; import RoomTable from './RomTable';
import RoomForm from './RoomForm'; import RoomForm from './RoomForm';
import Modal from '@component/Modal';
// Styled // Styled
import { StyledRoom, Title } from './styled'; import { StyledRoom, Title } from './styled';
import Direction from '@commonStyle/Direction.ts'; import Direction from '@commonStyle/Direction.ts';
import Button from '@commonStyle/Button'; import Button from '@commonStyle/Button';
// Types
import Modal from '@component/Modal';
const Room = () => { const Room = () => {
const [reload, setReload] = useState(true);
return ( return (
<> <>
<StyledRoom> <StyledRoom>
@@ -29,12 +23,12 @@ const Room = () => {
)} )}
/> />
<Modal.Window name="room-form" title="Add form"> <Modal.Window name="room-form" title="Add form">
<RoomForm setReload={setReload} reload={reload} /> <RoomForm />
</Modal.Window> </Modal.Window>
</Modal> </Modal>
</Direction> </Direction>
<RoomTable reload={reload} setReload={setReload} /> <RoomTable />
</StyledRoom> </StyledRoom>
</> </>
); );
+39 -144
View File
@@ -1,14 +1,7 @@
import {
Dispatch,
SetStateAction,
useCallback,
useEffect,
useState,
} from 'react';
import toast from 'react-hot-toast';
// Hooks // Hooks
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
import { useCreateUser } from '@hook/users/useCreateUser.ts';
import { useUpdateUser } from '@hook/users/useUpdateUser.ts';
// Styled // Styled
import Input from '@commonStyle/Input.ts'; import Input from '@commonStyle/Input.ts';
@@ -16,139 +9,73 @@ import Input from '@commonStyle/Input.ts';
// Components // Components
import Form from '@component/Form/index.tsx'; import Form from '@component/Form/index.tsx';
import FormRow from '@component/LabelControl/index.tsx'; import FormRow from '@component/LabelControl/index.tsx';
import Select, { ISelectOptions } from '@component/Select/index.tsx';
// Helpers // Helpers
import { isEmptyObj, isValidRegex } from '@helper/validators.ts'; import { isValidRegex } from '@helper/validators.ts';
// Constants
import { ADD_SUCCESS, EDIT_SUCCESS } from '@constant/messages.ts';
import { import {
INVALID_FIELD, INVALID_FIELD,
INVALID_PHONE, INVALID_PHONE,
REQUIRED_FIELD_ERROR, REQUIRED_FIELD_ERROR,
} from '@constant/formValidateMessage.ts'; } from '@constant/formValidateMessage.ts';
import { INIT_VALUE_USER_FORM, REGEX } from '@constant/commons.ts'; import { REGEX } from '@constant/commons.ts';
// Styled // Styled
import { FormBtn } from './styled.ts'; import { FormBtn } from './styled.ts';
// Services
import { getAllRoom, updateRoomStatus } from '@service/roomServices.ts';
import { createUser, updateUser } from '@service/userServices.ts';
// Types // Types
import { Nullable } from '@type/common.ts';
import { IUser } from '@type/users.ts'; import { IUser } from '@type/users.ts';
import { IRoom } from '@type/rooms.ts';
interface IUserFormProp { interface IUserFormProp {
onCloseModal?: () => void; onCloseModal?: () => void;
reload: boolean; user?: IUser;
setReload: Dispatch<SetStateAction<boolean>>;
user?: Nullable<IUser>;
} }
const UserForm = ({ onCloseModal, reload, setReload, user }: IUserFormProp) => { const UserForm = ({ onCloseModal, user }: IUserFormProp) => {
const formMethods = useForm<IUser>(); const { isCreating, createUser } = useCreateUser();
const { isUpdating, updateUser } = useUpdateUser();
const isLoading = isCreating || isUpdating;
const {id: editId, ...editValues} = {...user};
const formMethods = useForm<IUser>({
defaultValues: editId
? editValues
: {}
});
const { const {
register, register,
handleSubmit, handleSubmit,
reset, reset,
formState: { errors, isDirty, isValid, isSubmitting }, formState: { errors, isDirty, isValid },
trigger, trigger,
} = formMethods; } = formMethods;
const [rooms, setRooms] = useState<IRoom[]>([]);
const [options, setOptions] = useState<ISelectOptions[]>();
// Init value when edit form and load options
useEffect(() => {
const load = async () => {
const options: ISelectOptions[] = [];
const tempUser = !user ? { roomId: 0 } : { ...user };
// Load and set default options room
if (rooms.length > 0) {
// Init first options
options.push({
label: '---Select---',
value: '0',
});
rooms.forEach((item) => {
if (!item.status || tempUser?.roomId === item.id)
options.push({
label: item.name!,
value: item.id!.toString(),
});
});
}
if (options.length > 0) {
setOptions(options);
}
if (!isEmptyObj(tempUser)) {
// Init value
reset(tempUser);
} else {
reset(INIT_VALUE_USER_FORM);
}
};
load();
}, [reset, user, rooms]);
// Submit form // Submit form
const onSubmit = useCallback( const onSubmit =
async (newUser: IUser) => { (newUser: IUser) => {
if (!user) { if (!user) {
// Add request // Add request
const response = await createUser(newUser); createUser(newUser, {
onSuccess: () => {
if (response) { reset();
toast.success(ADD_SUCCESS); onCloseModal?.();
} },
});
// Update room status
updateRoomStatus(newUser.roomId, true);
} else { } else {
// Edit request // Edit request
const response = await updateUser(newUser); newUser.id = editId!;
updateUser(newUser, {
if (response) { onSuccess: () => {
toast.success(EDIT_SUCCESS); reset();
} onCloseModal?.();
}
// Update room status });
updateRoomStatus(user!.roomId, true, newUser.roomId);
}
// Reload table data
setReload(!reload);
reset();
onCloseModal!();
},
[onCloseModal, reload, reset, setReload, user]
);
// Load all rooms
useEffect(() => {
const loadRoom = async () => {
const rooms = await getAllRoom();
if (rooms) {
setRooms(rooms);
} }
}; };
loadRoom();
}, [onSubmit]);
return ( return (
<FormProvider {...formMethods}> <FormProvider {...formMethods}>
<Form onSubmit={handleSubmit(onSubmit)}> <Form onSubmit={handleSubmit(onSubmit)}>
<Input type="hidden" id="id" {...register('id')} />
<FormRow label="Full Name" error={errors?.name?.message}> <FormRow label="Full Name" error={errors?.name?.message}>
<Input <Input
type="text" type="text"
@@ -164,25 +91,6 @@ const UserForm = ({ onCloseModal, reload, setReload, user }: IUserFormProp) => {
/> />
</FormRow> </FormRow>
<FormRow
label="Identified Code"
error={errors?.identifiedCode?.message}
>
<Input
type="text"
id="identifiedCode"
{...register('identifiedCode', {
required: REQUIRED_FIELD_ERROR,
validate: {
checkIdentifiedCode: (v) =>
isValidRegex(new RegExp(REGEX.NUMBER), v.toString()) ||
INVALID_FIELD,
},
onChange: () => trigger('identifiedCode'),
})}
/>
</FormRow>
<FormRow label="Phone" error={errors?.phone?.message}> <FormRow label="Phone" error={errors?.phone?.message}>
<Input <Input
type="text" type="text"
@@ -198,32 +106,19 @@ const UserForm = ({ onCloseModal, reload, setReload, user }: IUserFormProp) => {
/> />
</FormRow> </FormRow>
<FormRow label="Room">
{options && options.length > 1 ? (
<Select
id="roomId"
options={options!}
ariaLabel="RoomId"
optionsConfigForm={{
valueAsNumber: true,
onChange: () => trigger('roomId'),
validate: (v) => v !== 0,
}}
/>
) : (
<p>No room available!</p>
)}
</FormRow>
<Form.Action> <Form.Action>
<FormBtn <FormBtn
type="submit" type="submit"
name="submit" name="submit"
disabled={!isDirty || !isValid || isSubmitting} disabled={!isDirty || !isValid || isLoading}
> >
{!user ? 'Add' : 'Save'} {
!user
? 'Add'
: 'Save'
}
</FormBtn> </FormBtn>
<FormBtn type="button" styled="secondary" onClick={onCloseModal}> <FormBtn type="button" variations="secondary" onClick={onCloseModal}>
Close Close
</FormBtn> </FormBtn>
</Form.Action> </Form.Action>
+2 -6
View File
@@ -1,5 +1,3 @@
import { Dispatch, SetStateAction } from 'react';
// Components // Components
import { RiEditBoxFill } from 'react-icons/ri'; import { RiEditBoxFill } from 'react-icons/ri';
import Modal from '@component/Modal'; import Modal from '@component/Modal';
@@ -13,11 +11,9 @@ import { IUser } from '@type/users';
interface IUserRow { interface IUserRow {
user: IUser; user: IUser;
reload: boolean;
setReload: Dispatch<SetStateAction<boolean>>;
} }
const UserRow = ({ user, reload, setReload }: IUserRow) => { const UserRow = ({ user }: IUserRow) => {
const { id, name, phone } = user; const { id, name, phone } = user;
return ( return (
@@ -42,7 +38,7 @@ const UserRow = ({ user, reload, setReload }: IUserRow) => {
</Menus.List> </Menus.List>
<Modal.Window name="edit" title="Edit user"> <Modal.Window name="edit" title="Edit user">
<UserForm user={user} setReload={setReload} reload={reload} /> <UserForm user={user} />
</Modal.Window> </Modal.Window>
</Menus.Menu> </Menus.Menu>
</Modal> </Modal>
+7 -40
View File
@@ -1,5 +1,4 @@
import { useSearchParams } from 'react-router-dom'; import { useCallback } from 'react';
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
// Components // Components
import Menus from '@component/Menus'; import Menus from '@component/Menus';
@@ -21,50 +20,21 @@ import Direction from '@commonStyle/Direction';
import { StyledOperationTable } from './styled'; import { StyledOperationTable } from './styled';
import Spinner from '@commonStyle/Spinner'; import Spinner from '@commonStyle/Spinner';
import { useQuery } from '@tanstack/react-query'; // Hooks
import { getAllUsers } from '@service/userServices'; import { useUsers } from '@hook/users/useUsers';
import toast from 'react-hot-toast';
interface IUserTable { const UserTable = () => {
reload: boolean;
setReload: Dispatch<SetStateAction<boolean>>;
}
const UserTable = ({ reload, setReload }: IUserTable) => {
const columnName = ['Id', 'Name', 'Phone']; const columnName = ['Id', 'Name', 'Phone'];
const { isLoading, users } = useUsers();
const [phoneSearch, setPhoneSearch] = useState('');
const [searchParams] = useSearchParams();
const sortByValue = searchParams.get('sortBy')
? searchParams.get('sortBy')!
: 'id';
const orderByValue = searchParams.get('orderBy')
? searchParams.get('orderBy')!
: 'asc';
const {
isLoading,
data: users,
error,
} = useQuery({
queryKey: ['cabins', sortByValue, orderByValue, phoneSearch],
queryFn: () => getAllUsers(sortByValue, orderByValue, phoneSearch),
});
if(error) {
toast.error(error.message);
}
const renderUserRow = useCallback( const renderUserRow = useCallback(
(user: IUser) => ( (user: IUser) => (
<UserRow <UserRow
user={user} user={user}
key={user.id} key={user.id}
reload={reload}
setReload={setReload}
/> />
), ),
[reload, setReload] []
); );
return ( return (
@@ -74,10 +44,7 @@ const UserTable = ({ reload, setReload }: IUserTable) => {
<OrderBy options={ORDERBY_OPTIONS} /> <OrderBy options={ORDERBY_OPTIONS} />
<SortBy options={USER_PAGE.SORTBY_OPTIONS} /> <SortBy options={USER_PAGE.SORTBY_OPTIONS} />
<Search <Search setPlaceHolder="Search by phone..." />
setValueSearch={setPhoneSearch}
setPlaceHolder="Search by phone..."
/>
</StyledOperationTable> </StyledOperationTable>
{isLoading && <Spinner />} {isLoading && <Spinner />}
+2 -5
View File
@@ -1,5 +1,3 @@
import { useState } from 'react';
// Components // Components
import UserTable from './UserTable'; import UserTable from './UserTable';
@@ -17,7 +15,6 @@ import { FORM } from '@constant/commons';
const User = () => { const User = () => {
const TITLE = 'Add user'; const TITLE = 'Add user';
const [reload, setReload] = useState(true);
return ( return (
<> <>
@@ -33,12 +30,12 @@ const User = () => {
)} )}
/> />
<Modal.Window name={FORM.USER} title={TITLE}> <Modal.Window name={FORM.USER} title={TITLE}>
<UserForm setReload={setReload} reload={reload} /> <UserForm />
</Modal.Window> </Modal.Window>
</Modal> </Modal>
</Direction> </Direction>
<UserTable reload={reload} setReload={setReload} /> <UserTable />
</StyledUser> </StyledUser>
</> </>
); );
+51 -144
View File
@@ -1,173 +1,80 @@
import toast from 'react-hot-toast';
// Types // Types
import { Nullable } from '@type/common';
import { IResponse } from '@type/responses';
import { IRoom } from '@type/rooms'; import { IRoom } from '@type/rooms';
// Helpers // Services
import { sendRequest } from '@helper/sendRequest'; import supabase from './supabaseService';
import { errorMsg } from '@helper/helper';
// Constants // Constants
import { STATUS_CODE, RESPONSE_MESSAGE } from '@constant/responseStatus'; const ROOMS_TABLE = 'rooms';
import { ROOM_PATH } from '@constant/path'; const ERROR_FETCHING = "Can't fetch room data!";
const ERROR_UPDATE_ROOM = "Can't update room!";
const ERROR_CREATE_ROOM = "Can't create room!";
const ERROR_DELETE_ROOM = "Can't delete room!";
/** /**
* Get all rooms from server * Get all rooms from database
* @returns Return all rooms in server * @returns Return all rooms in database
*/ */
const getAllRoom = async (): Promise<Nullable<IRoom[]>> => { const getAllRooms = async (
try { sortBy: string,
const response = await sendRequest<IRoom[]>(ROOM_PATH); orderBy: string,
roomName: string
): Promise<IRoom[]> => {
const { data, error } = await supabase
.from(ROOMS_TABLE)
.select('*')
.order(sortBy, { ascending: orderBy === 'asc' })
.like('name', `%${roomName}%`);
if (response.statusCode === STATUS_CODE.OK) { if (error) {
const rooms = response.data!; console.error(error.message);
throw new Error(ERROR_FETCHING);
return rooms;
} else {
throw new Error(errorMsg(response.statusCode, response.msg));
}
} catch (error) {
if (error instanceof Error) {
toast.error(error.message);
}
} }
return null; return data;
}; };
/**
* Get room by id
* @param roomId The id room need to be get
* @returns Return the room object depend on room id
*/
const getRoom = async (roomId: number): Promise<Nullable<IRoom>> => {
try {
const response = await sendRequest<IRoom>(ROOM_PATH + '/' + roomId);
if (response.statusCode !== STATUS_CODE.OK) {
throw new Error(errorMsg(response.statusCode, response.msg));
}
const rooms = response.data!;
return rooms;
} catch (error) {
if (error instanceof Error) {
toast.error(error.message);
}
}
return null;
};
/** /**
* Update room into server * Update room into database
* @param room Room object need to be updated * @param room Room object need to be updated
* @returns The response object
*/ */
const updateRoom = async (room: IRoom): Promise<Nullable<IResponse<IRoom>>> => { const updateRoom = async (room: IRoom): Promise<void> => {
try { const { error } = await supabase.from(ROOMS_TABLE).update(room).eq("id", room.id);
const response = await sendRequest<IRoom>(
ROOM_PATH + '/' + room.id,
'PUT',
JSON.stringify(room)
);
return response; if(error) {
} catch (error: unknown) { console.error(error.message);
if (error instanceof Error) { throw new Error(ERROR_UPDATE_ROOM);
toast.error(error.message);
}
} }
return null;
}; };
/** /**
* Update room status * Add room to database
* @param roomId The id room need to be updated
* @param status Status of room
* @param roomIdNew The new id room need to be updated
* @returns Return the response object
*/
const updateRoomStatus = async (
roomId: number,
status: boolean,
roomIdNew?: number
): Promise<Nullable<IResponse<IRoom>>> => {
try {
if (!roomIdNew) {
const response = await sendRequest<IRoom>(
ROOM_PATH + '/' + roomId,
'PATCH',
JSON.stringify({ status: status })
);
return response;
}
// Update new room status
const resNewRoom = await sendRequest<IRoom>(
ROOM_PATH + '/' + roomIdNew,
'PATCH',
JSON.stringify({ status: status })
);
// Update old room status;
const resOldRoom = await sendRequest<IRoom>(
ROOM_PATH + '/' + roomId,
'PATCH',
JSON.stringify({ status: !status })
);
if (
resNewRoom.statusCode === STATUS_CODE.OK &&
resOldRoom.statusCode === STATUS_CODE.OK
) {
return {
statusCode: STATUS_CODE.OK,
msg: RESPONSE_MESSAGE.UPDATE_SUCCESS,
};
}
} catch (error) {
if (error instanceof Error) {
toast.error(error.message);
}
}
return null;
};
/**
* Add room to server
* @param room The room object need to be add * @param room The room object need to be add
* @returns The response object if complete or null
*/ */
const addRoom = async (room: IRoom): Promise<Nullable<IResponse<IRoom>>> => { const createRoom = async (room: IRoom): Promise<void> => {
try { // Set default status
// Set default status room room.status = false;
room.status = false;
const response = await sendRequest<IRoom>( const { error } = await supabase.from(ROOMS_TABLE).insert([room]);
ROOM_PATH,
'POST',
JSON.stringify(room)
);
if (response.statusCode !== STATUS_CODE.CREATE) { if(error) {
throw new Error(errorMsg(response.statusCode, response.msg)); console.error(error.message);
} throw new Error(ERROR_CREATE_ROOM);
return response;
} catch (error: unknown) {
if (error instanceof Error) {
toast.error(error.message);
}
} }
return null;
}; };
export { getRoom, updateRoom, updateRoomStatus, getAllRoom, addRoom }; /**
* Delete room in database
* @param idRoom The id of room need to delete
*/
const deleteRoom = async(idRoom: number) => {
const { error } = await supabase.from(ROOMS_TABLE).delete().eq('id', idRoom);
if(error) {
console.error(error.message);
throw new Error(ERROR_DELETE_ROOM);
}
}
export { getAllRooms, updateRoom, createRoom, deleteRoom };
@@ -0,0 +1,9 @@
import { createClient } from "@supabase/supabase-js";
import { Database } from "@type/supabase";
// Constants
import { supabaseKey, supabaseUrl } from "@constant/config";
const supabase = createClient<Database>(supabaseUrl, supabaseKey!);
export default supabase;
+25 -73
View File
@@ -1,96 +1,47 @@
import toast from 'react-hot-toast';
// Constants
import { USER_PATH } from '@constant/path';
import { STATUS_CODE } from '@constant/responseStatus';
// Types // Types
import { Nullable } from '@type/common';
import { IResponse } from '@type/responses';
import { IUser } from '@type/users'; import { IUser } from '@type/users';
// Helpers // Services
import { sendRequest } from '@helper/sendRequest'; import supabase from './supabaseService';
import { errorMsg } from '@helper/helper';
import supabase from '@constant/supabaseConfig';
const USERS_TABLE = 'users'; const USERS_TABLE = 'users';
const ERROR_FETCHING = "Users can't be loaded!"; const ERROR_FETCHING = "Users can't be loaded!";
const ERROR_CREATE_USER = "Can't create user!";
const ERROR_UPDATE_USER = "Can't update user!";
/** /**
* Create user to the server * Create user to the database
* @param user The user object need to be created * @param user The user object need to be created
* @returns The IResponse object if success or null
*/ */
const createUser = async (user: IUser): Promise<Nullable<IResponse<IUser>>> => { const createUser = async (user: IUser): Promise<void> => {
// try { const { error } = await supabase.from(USERS_TABLE).insert([user]);
// const response = await sendRequest<IUser>(
// USER_PATH,
// 'POST',
// JSON.stringify(user)
// );
// if (response.statusCode !== STATUS_CODE.CREATE) { if(error) {
// throw new Error(errorMsg(response.statusCode, response.msg)); console.error(error.message);
// } throw new Error(ERROR_CREATE_USER);
}
// return response;
// } catch (error: unknown) {
// if (error instanceof Error) {
// toast.error(error.message);
// }
// }
return null;
}; };
/** /**
* Update the user to the server * Update the user to the database
* @param user The user object need to be updated * @param user The user object need to be updated
* @returns The IResponse object if update success or null
*/ */
const updateUser = async (user: IUser): Promise<Nullable<IResponse<IUser>>> => { const updateUser = async (user: IUser): Promise<void> => {
// try { const { error } = await supabase.from(USERS_TABLE).update(user).eq("id", user.id);
// const response = await sendRequest<IUser>(
// USER_PATH + '/' + user.id,
// 'PUT',
// JSON.stringify(user)
// );
// if (response.statusCode !== STATUS_CODE.OK) { if(error) {
// throw new Error(errorMsg(response.statusCode, response.msg)); console.error(error.message);
// } throw new Error(ERROR_UPDATE_USER);
}
// return response;
// } catch (error: unknown) {
// if (error instanceof Error) {
// toast.error(error.message);
// }
// }
return null;
}; };
/** /**
* Checkout user * Return data of users from database
* @param user The user need to be checkout * @param sortBy Sort by column
* @returns The IResponse object if checkout success or not * @param orderBy Order by ascending or descending
* @param phoneSearch The phone need to be search
* @returns The data of users from database
*/ */
const checkOutUser = async (
user: IUser
): Promise<Nullable<IResponse<IUser>>> => {
// const tempUser = user;
// if (tempUser) {
// tempUser.roomId = 0;
// const resUpdateUser = await updateUser(tempUser);
// return resUpdateUser;
// }
return null;
};
const getAllUsers = async ( const getAllUsers = async (
sortBy: string, sortBy: string,
orderBy: string, orderBy: string,
@@ -103,10 +54,11 @@ const getAllUsers = async (
.like('phone', `%${phoneSearch}%`); .like('phone', `%${phoneSearch}%`);
if (error) { if (error) {
console.error(error.message);
throw new Error(ERROR_FETCHING); throw new Error(ERROR_FETCHING);
} }
return data; return data;
}; };
export { updateUser, checkOutUser, createUser, getAllUsers }; export { updateUser, createUser, getAllUsers };
@@ -6,6 +6,7 @@
--secondary-btn-color: #d1d1d1; --secondary-btn-color: #d1d1d1;
--disabled-btn-color: #7f82a6; --disabled-btn-color: #7f82a6;
--danger-btn-color: #ff4f4f;
--hover-background-color: #f3f4f6; --hover-background-color: #f3f4f6;
--hover-dark-background-color: #d0d0d0; --hover-dark-background-color: #d0d0d0;
-7
View File
@@ -1,7 +0,0 @@
interface IResponse<T> {
statusCode: number;
msg: string;
data?: T;
}
export type { IResponse };
-2
View File
@@ -2,8 +2,6 @@ interface IRoom {
id: number; id: number;
name: string; name: string;
price: number; price: number;
discount: number;
finalPrice: number;
status: boolean; status: boolean;
} }