From 117723df8c831117617d20440178fb256f11feb1 Mon Sep 17 00:00:00 2001 From: Loi Phan Date: Wed, 1 Nov 2023 09:49:30 +0700 Subject: [PATCH 1/4] Update component and optimized code --- hotel-management/src/commons/styles/Input.ts | 19 ++++++++- .../src/components/Dialog/index.tsx | 4 +- hotel-management/src/components/Search.tsx | 11 ++--- .../src/components/Table/styled.ts | 1 + hotel-management/src/constants/path.ts | 1 + hotel-management/src/constants/variables.ts | 42 ++++++++++++++++++- hotel-management/src/globals/interfaces.ts | 7 +--- hotel-management/src/globals/types.ts | 17 +++++++- hotel-management/src/helpers/utils.ts | 33 +++++++++++---- hotel-management/src/helpers/validators.ts | 18 +++++--- 10 files changed, 124 insertions(+), 29 deletions(-) diff --git a/hotel-management/src/commons/styles/Input.ts b/hotel-management/src/commons/styles/Input.ts index f1350ab..0826c7e 100644 --- a/hotel-management/src/commons/styles/Input.ts +++ b/hotel-management/src/commons/styles/Input.ts @@ -1,10 +1,25 @@ -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; // Styled import CommonInput from './CommonInput'; -const Input = styled.input` +interface IInputTyped { + type?: 'text' | 'checkbox' | 'hidden'; +} + +const Input = styled.input` ${CommonInput} + + ${(props) => + props.type === 'checkbox' && + css` + width: 20px; + height: 20px; + `} `; +Input.defaultProps = { + type: 'text', +}; + export default Input; diff --git a/hotel-management/src/components/Dialog/index.tsx b/hotel-management/src/components/Dialog/index.tsx index 8e73da6..1bedac9 100644 --- a/hotel-management/src/components/Dialog/index.tsx +++ b/hotel-management/src/components/Dialog/index.tsx @@ -7,7 +7,7 @@ import { IDialogProps } from '../../globals/interfaces'; import { StyledBody, StyledDialog, StyledTitle } from './styled'; const Dialog = forwardRef((props, ref) => { - const { title, children, onClose } = props as IDialogProps; + const { title, children, onClose } = props as IDialogProps; return ( | undefined} @@ -17,6 +17,6 @@ const Dialog = forwardRef((props, ref) => { {children} ); -}) as React.FC; +}) as React.FC>; export default Dialog; diff --git a/hotel-management/src/components/Search.tsx b/hotel-management/src/components/Search.tsx index 71538f5..ddc1585 100644 --- a/hotel-management/src/components/Search.tsx +++ b/hotel-management/src/components/Search.tsx @@ -9,24 +9,25 @@ const StyledSearch = styled.input` `; interface ISearch { - setPhoneSearch: React.Dispatch>; + setPlaceHolder: string; + setValueSearch: React.Dispatch>; } -const Search = ({ setPhoneSearch }: ISearch) => { +const Search = ({ setValueSearch, setPlaceHolder }: ISearch) => { const [query, setQuery] = useState(''); useEffect(() => { const timeOut = setTimeout(() => { - setPhoneSearch(query); + setValueSearch(query); }, 500); return () => clearTimeout(timeOut); - }, [setPhoneSearch, query]); + }, [setValueSearch, query]); return ( setQuery(e.target.value)} - placeholder="Search by phone..." + placeholder={setPlaceHolder} /> ); }; diff --git a/hotel-management/src/components/Table/styled.ts b/hotel-management/src/components/Table/styled.ts index 739542f..5e89ff2 100644 --- a/hotel-management/src/components/Table/styled.ts +++ b/hotel-management/src/components/Table/styled.ts @@ -15,6 +15,7 @@ const CommonRow = styled.div` grid-template-columns: ${(props) => props.columns}; column-gap: 10px; align-items: center; + justify-items: center; `; const StyledBody = styled.div` diff --git a/hotel-management/src/constants/path.ts b/hotel-management/src/constants/path.ts index d4bd6b1..2f54b15 100644 --- a/hotel-management/src/constants/path.ts +++ b/hotel-management/src/constants/path.ts @@ -4,3 +4,4 @@ export const ROOM: string = '/room'; export const OTHER_PATH: string = '*'; export const BASE_URL: string = 'https://hotel-management-api.loiphan.com/'; export const USER_PATH = 'users'; +export const ROOM_PATH = 'rooms'; diff --git a/hotel-management/src/constants/variables.ts b/hotel-management/src/constants/variables.ts index d1fc6b2..1c03d65 100644 --- a/hotel-management/src/constants/variables.ts +++ b/hotel-management/src/constants/variables.ts @@ -34,4 +34,44 @@ const USER_PAGE = { ], }; -export { USER_PAGE }; +// prettier-ignore +const ROOM_PAGE = { + ORDERBY_OPTIONS: [ + { + value: 'asc', + label: 'Ascending', + }, + { + value: 'desc', + label: 'Descending', + }, + ], + SORTBY_OPTIONS: [ + { + value: 'id', + label: 'Sort by id', + }, + { + value: 'name', + label: 'Sort by name', + }, + { + value: 'amount', + label: 'Sort by amount', + }, + { + value: 'price', + label: 'Sort by price', + }, + { + value: 'discount', + label: 'Sort by discount', + }, + { + value: 'status', + label: 'Sort by status', + }, + ], +} + +export { USER_PAGE, ROOM_PAGE }; diff --git a/hotel-management/src/globals/interfaces.ts b/hotel-management/src/globals/interfaces.ts index ef16998..2d0726c 100644 --- a/hotel-management/src/globals/interfaces.ts +++ b/hotel-management/src/globals/interfaces.ts @@ -1,6 +1,3 @@ -// Types -import { TUser } from './types'; - interface IMenusContext { openId?: string; close?: () => void; @@ -23,14 +20,14 @@ interface ITableBody { render?: (value: T) => JSX.Element; } -interface IDialogProps { +interface IDialogProps { title?: string; children?: JSX.Element[] | JSX.Element; onClose?: () => void; reload?: boolean; setReload?: React.Dispatch>; ref?: React.MutableRefObject; - user?: TUser | null; + data?: T | null; isAdd?: boolean; } diff --git a/hotel-management/src/globals/types.ts b/hotel-management/src/globals/types.ts index 8514620..3937295 100644 --- a/hotel-management/src/globals/types.ts +++ b/hotel-management/src/globals/types.ts @@ -7,6 +7,16 @@ type TUser = { roomId: string; }; +type TRoom = { + id: string; + name: string; + amount: string; + price: string; + discount: string; + description: string; + status: boolean; +}; + type TStateSchema = { [key: string]: { value?: string; @@ -15,7 +25,11 @@ type TStateSchema = { }; type TKeyValue = { - [key: string]: string | undefined | boolean; + [key: string]: string | undefined | boolean | number; +}; + +export type Test = { + [key: string]: string; }; type TPropValues = 'value' | 'error' | boolean; @@ -37,6 +51,7 @@ type TResponse = { export type { TUser, + TRoom, TStateSchema, TKeyValue, TPropValues, diff --git a/hotel-management/src/helpers/utils.ts b/hotel-management/src/helpers/utils.ts index 6c3b585..bdba669 100644 --- a/hotel-management/src/helpers/utils.ts +++ b/hotel-management/src/helpers/utils.ts @@ -2,7 +2,7 @@ import { invalidFormatMsg } from '../constants/messages'; // Types -import { TKeyValue, TPropValues, TStateSchema, TUser } from '../globals/types'; +import { TKeyValue, TPropValues, TStateSchema, Test } from '../globals/types'; const VALUE = 'value'; const ERROR = 'error'; @@ -47,18 +47,35 @@ const addValidator = ({ validatorFunc, prop, required = true }: TValidator) => { }; }; -const getValueUser = (user: TUser | null = null, prop: string): string => { - if (user) { - return user[prop as keyof TUser]; +const getValueFromObj = (obj: T | null = null): Test => { + let result = {}; + + if (obj) { + for (const key of Object.keys(obj)) { + const tempValue = obj[key as keyof typeof obj]; + const value: string | boolean | number = + typeof tempValue === 'boolean' || + typeof tempValue === 'string' || + typeof tempValue === 'number' + ? tempValue + : ''; + + result = { ...result, [`${key}Value`]: value }; + } } - return ''; + return result; }; -const searchQuery = (phone: string, sort: string, order: string) => { +const searchQuery = ( + columnSearch: string, + phone: string, + sort: string, + order: string, +) => { // prettier-ignore const phoneParams = phone - ? 'phone_like=' + phone + ? `${columnSearch}_like=` + phone : ''; // prettier-ignore @@ -92,7 +109,7 @@ export { isObject, isRequired, getPropValues, - getValueUser, + getValueFromObj, addValidator, searchQuery, VALUE, diff --git a/hotel-management/src/helpers/validators.ts b/hotel-management/src/helpers/validators.ts index 4157039..a971715 100644 --- a/hotel-management/src/helpers/validators.ts +++ b/hotel-management/src/helpers/validators.ts @@ -1,5 +1,5 @@ -const isValidString = (value: string): boolean => { - return /^[a-zA-Z]{4,}(?: [a-zA-Z]+){0,2}$/gm.test(value); +const isValidName = (value: string): boolean => { + return /^[a-zA-Z]{2,}(?: [a-zA-Z]+){0,2}$/gm.test(value); }; const isValidNumber = (value: string): boolean => { @@ -10,8 +10,16 @@ const isValidPhoneNumber = (value: string): boolean => { return /(84|0[3|5|7|8|9])+([0-9]{8})\b/g.test(value); }; -const isValidAddress = (value: string): boolean => { - return value.trim().length >= 10; +const isValidString = (value: string): boolean => { + return value.trim().length >= 5; }; -export { isValidString, isValidNumber, isValidPhoneNumber, isValidAddress }; +const skipCheck = () => true; + +export { + isValidName, + isValidNumber, + isValidPhoneNumber, + isValidString, + skipCheck, +}; From e8457d8e2cb31cc80908b37684d7886d28edebf2 Mon Sep 17 00:00:00 2001 From: Loi Phan Date: Wed, 1 Nov 2023 11:11:29 +0700 Subject: [PATCH 2/4] Fix format code style --- hotel-management/src/constants/messages.ts | 2 ++ hotel-management/src/constants/variables.ts | 5 ++++- hotel-management/src/globals/types.ts | 5 +++-- hotel-management/src/helpers/utils.ts | 23 +++++++++------------ 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/hotel-management/src/constants/messages.ts b/hotel-management/src/constants/messages.ts index 7ddb73a..0b9531e 100644 --- a/hotel-management/src/constants/messages.ts +++ b/hotel-management/src/constants/messages.ts @@ -10,6 +10,7 @@ const ADD_SUCCESS = 'Add success'; const EDIT_SUCCESS = 'Edit success'; const CONFIRM_DELETE = 'Are you sure to delete it?'; const DELETE_SUCCESS = 'Delete success'; +const REQUIRED_FIELD_ERROR = 'This is required field'; export { invalidFormatMsg, @@ -18,4 +19,5 @@ export { EDIT_SUCCESS, CONFIRM_DELETE, DELETE_SUCCESS, + REQUIRED_FIELD_ERROR, }; diff --git a/hotel-management/src/constants/variables.ts b/hotel-management/src/constants/variables.ts index 1c03d65..b3b169a 100644 --- a/hotel-management/src/constants/variables.ts +++ b/hotel-management/src/constants/variables.ts @@ -74,4 +74,7 @@ const ROOM_PAGE = { ], } -export { USER_PAGE, ROOM_PAGE }; +const VALUE = 'value'; +const ERROR = 'error'; + +export { USER_PAGE, ROOM_PAGE, VALUE, ERROR }; diff --git a/hotel-management/src/globals/types.ts b/hotel-management/src/globals/types.ts index 3937295..d7176a0 100644 --- a/hotel-management/src/globals/types.ts +++ b/hotel-management/src/globals/types.ts @@ -25,10 +25,10 @@ type TStateSchema = { }; type TKeyValue = { - [key: string]: string | undefined | boolean | number; + [key: string]: boolean | undefined | string; }; -export type Test = { +type TKeyString = { [key: string]: string; }; @@ -54,6 +54,7 @@ export type { TRoom, TStateSchema, TKeyValue, + TKeyString, TPropValues, TValidator, TResponse, diff --git a/hotel-management/src/helpers/utils.ts b/hotel-management/src/helpers/utils.ts index bdba669..af150a9 100644 --- a/hotel-management/src/helpers/utils.ts +++ b/hotel-management/src/helpers/utils.ts @@ -1,12 +1,13 @@ // Constants -import { invalidFormatMsg } from '../constants/messages'; +import { REQUIRED_FIELD_ERROR, invalidFormatMsg } from '../constants/messages'; // Types -import { TKeyValue, TPropValues, TStateSchema, Test } from '../globals/types'; - -const VALUE = 'value'; -const ERROR = 'error'; -const REQUIRED_FIELD_ERROR = 'This is required field'; +import { + TKeyString, + TKeyValue, + TPropValues, + TStateSchema, +} from '../globals/types'; const isBool = (value: unknown) => { return typeof value === 'boolean'; @@ -47,20 +48,18 @@ const addValidator = ({ validatorFunc, prop, required = true }: TValidator) => { }; }; -const getValueFromObj = (obj: T | null = null): Test => { +const getValueFromObj = (obj: T | null = null): TKeyString => { let result = {}; if (obj) { for (const key of Object.keys(obj)) { const tempValue = obj[key as keyof typeof obj]; const value: string | boolean | number = - typeof tempValue === 'boolean' || - typeof tempValue === 'string' || - typeof tempValue === 'number' + typeof tempValue === 'boolean' || typeof tempValue === 'string' ? tempValue : ''; - result = { ...result, [`${key}Value`]: value }; + result = { ...result, [`${key}Value`]: value as string }; } } @@ -112,7 +111,5 @@ export { getValueFromObj, addValidator, searchQuery, - VALUE, - ERROR, REQUIRED_FIELD_ERROR, }; From 12a43f31c028f5500de72a98a5709f1441a64dae Mon Sep 17 00:00:00 2001 From: Loi Phan Date: Wed, 1 Nov 2023 11:18:05 +0700 Subject: [PATCH 3/4] Update comments --- hotel-management/src/components/SortBy.tsx | 2 ++ hotel-management/src/helpers/sendRequest.ts | 3 +++ hotel-management/src/helpers/utils.ts | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hotel-management/src/components/SortBy.tsx b/hotel-management/src/components/SortBy.tsx index 524366b..128b630 100644 --- a/hotel-management/src/components/SortBy.tsx +++ b/hotel-management/src/components/SortBy.tsx @@ -1,4 +1,6 @@ import { useSearchParams } from 'react-router-dom'; + +// Components import Select from './Select'; interface ISortByProps { diff --git a/hotel-management/src/helpers/sendRequest.ts b/hotel-management/src/helpers/sendRequest.ts index 4d2d264..a1c2f49 100644 --- a/hotel-management/src/helpers/sendRequest.ts +++ b/hotel-management/src/helpers/sendRequest.ts @@ -1,6 +1,9 @@ // Constants import { BASE_URL } from '../constants/path'; + +// Types import { TResponse } from '../globals/types'; + type TMethodRequest = 'GET' | 'POST' | 'PUT' | 'DELETE'; export const sendRequest = async ( diff --git a/hotel-management/src/helpers/utils.ts b/hotel-management/src/helpers/utils.ts index af150a9..4701694 100644 --- a/hotel-management/src/helpers/utils.ts +++ b/hotel-management/src/helpers/utils.ts @@ -59,7 +59,7 @@ const getValueFromObj = (obj: T | null = null): TKeyString => { ? tempValue : ''; - result = { ...result, [`${key}Value`]: value as string }; + result = { ...result, [`${key}Value`]: value }; } } From aaef0971ef0206810db96468c2467b8008ae44c5 Mon Sep 17 00:00:00 2001 From: Loi Phan Date: Wed, 1 Nov 2023 14:04:51 +0700 Subject: [PATCH 4/4] Add comments section for function --- hotel-management/src/helpers/sendRequest.ts | 7 ++++ hotel-management/src/helpers/utils.ts | 46 +++++++++++++++++++-- hotel-management/src/helpers/validators.ts | 24 +++++++++++ 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/hotel-management/src/helpers/sendRequest.ts b/hotel-management/src/helpers/sendRequest.ts index a1c2f49..4696504 100644 --- a/hotel-management/src/helpers/sendRequest.ts +++ b/hotel-management/src/helpers/sendRequest.ts @@ -6,6 +6,13 @@ import { TResponse } from '../globals/types'; type TMethodRequest = 'GET' | 'POST' | 'PUT' | 'DELETE'; +/** + * 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 ( path: string, body: BodyInit | null | undefined, diff --git a/hotel-management/src/helpers/utils.ts b/hotel-management/src/helpers/utils.ts index 4701694..1dcbbd7 100644 --- a/hotel-management/src/helpers/utils.ts +++ b/hotel-management/src/helpers/utils.ts @@ -9,19 +9,41 @@ import { TStateSchema, } from '../globals/types'; +/** + * The function check value has type boolean or not + * @param value The value need to checked + * @returns A boolean indicating whether or not the argument has type boolean + */ const isBool = (value: unknown) => { return typeof value === 'boolean'; }; +/** + * The function check value has type object or not + * @param value The value need to checked + * @returns A boolean indicating whether or not the argument has type object. + */ const isObject = (value: unknown) => { return typeof value === 'object' && value !== null; }; +/** + * Set required error for value + * @param value The value set required or not + * @param isRequired Set required for value + * @returns Return error text if value has required + */ const isRequired = (value: string | number, isRequired: unknown) => { if (!value && isRequired) return REQUIRED_FIELD_ERROR; return ''; }; +/** + * Get values from props + * @param stateSchema StateSchema value + * @param prop Prop value (Has 3 type: boolean | "value" | "error") + * @returns Return value object depend on props + */ const getPropValues = (stateSchema: TStateSchema, prop?: TPropValues) => { return Object.keys(stateSchema).reduce((field, key) => { field[key] = isBool(prop) @@ -38,6 +60,11 @@ type TValidator = { required?: boolean; }; +/** + * Create validator object + * @param param0 Pass TValidator object + * @returns An object contains condition validator + */ const addValidator = ({ validatorFunc, prop, required = true }: TValidator) => { return { required, @@ -48,6 +75,11 @@ const addValidator = ({ validatorFunc, prop, required = true }: TValidator) => { }; }; +/** + * Return the object contains values of object pass + * @param obj Object need to get value + * @returns The object contains value of object + */ const getValueFromObj = (obj: T | null = null): TKeyString => { let result = {}; @@ -66,15 +98,23 @@ const getValueFromObj = (obj: T | null = null): TKeyString => { return result; }; +/** + * Create query url for search + * @param columnSearch Column want to search + * @param keySearch Keyword search + * @param sort Sort by + * @param order Order by + * @returns Return query url + */ const searchQuery = ( columnSearch: string, - phone: string, + keySearch: string, sort: string, order: string, ) => { // prettier-ignore - const phoneParams = phone - ? `${columnSearch}_like=` + phone + const phoneParams = keySearch + ? `${columnSearch}_like=` + keySearch : ''; // prettier-ignore diff --git a/hotel-management/src/helpers/validators.ts b/hotel-management/src/helpers/validators.ts index a971715..560de79 100644 --- a/hotel-management/src/helpers/validators.ts +++ b/hotel-management/src/helpers/validators.ts @@ -1,19 +1,43 @@ +/** + * Check value is valid name or not + * @param value Values need to be checked + * @returns A boolean indicating whether or not the argument has valid + */ const isValidName = (value: string): boolean => { return /^[a-zA-Z]{2,}(?: [a-zA-Z]+){0,2}$/gm.test(value); }; +/** + * Check value is valid number or not + * @param value Value need to be checked + * @returns A boolean indicating whether or not the argument has valid + */ const isValidNumber = (value: string): boolean => { return /^[0-9]*$/.test(value); }; +/** + * Check value is valid phone number or not + * @param value Value need to be checked + * @returns A boolean indicating whether or not the argument has valid + */ const isValidPhoneNumber = (value: string): boolean => { return /(84|0[3|5|7|8|9])+([0-9]{8})\b/g.test(value); }; +/** + * Check value is valid string or not + * @param value Value need to be checked + * @returns A boolean indicating whether or not the argument has valid + */ const isValidString = (value: string): boolean => { return value.trim().length >= 5; }; +/** + * A method skip check value + * @returns Return true + */ const skipCheck = () => true; export {