Merge pull request #19 from Nez27/feat/update-component-and-optimzed-code

Update component and optimzed code
This commit is contained in:
Loi Phan
2023-11-01 14:15:01 +07:00
committed by GitHub
13 changed files with 211 additions and 37 deletions
+17 -2
View File
@@ -1,10 +1,25 @@
import styled from 'styled-components'; import styled, { css } from 'styled-components';
// Styled // Styled
import CommonInput from './CommonInput'; import CommonInput from './CommonInput';
const Input = styled.input` interface IInputTyped {
type?: 'text' | 'checkbox' | 'hidden';
}
const Input = styled.input<IInputTyped>`
${CommonInput} ${CommonInput}
${(props) =>
props.type === 'checkbox' &&
css`
width: 20px;
height: 20px;
`}
`; `;
Input.defaultProps = {
type: 'text',
};
export default Input; export default Input;
@@ -7,7 +7,7 @@ import { IDialogProps } from '../../globals/interfaces';
import { StyledBody, StyledDialog, StyledTitle } from './styled'; import { StyledBody, StyledDialog, StyledTitle } from './styled';
const Dialog = forwardRef((props, ref) => { const Dialog = forwardRef((props, ref) => {
const { title, children, onClose } = props as IDialogProps; const { title, children, onClose } = props as IDialogProps<unknown>;
return ( return (
<StyledDialog <StyledDialog
ref={ref as React.LegacyRef<HTMLDialogElement> | undefined} ref={ref as React.LegacyRef<HTMLDialogElement> | undefined}
@@ -17,6 +17,6 @@ const Dialog = forwardRef((props, ref) => {
<StyledBody>{children}</StyledBody> <StyledBody>{children}</StyledBody>
</StyledDialog> </StyledDialog>
); );
}) as React.FC<IDialogProps>; }) as React.FC<IDialogProps<unknown>>;
export default Dialog; export default Dialog;
+6 -5
View File
@@ -9,24 +9,25 @@ const StyledSearch = styled.input`
`; `;
interface ISearch { interface ISearch {
setPhoneSearch: React.Dispatch<React.SetStateAction<string>>; setPlaceHolder: string;
setValueSearch: React.Dispatch<React.SetStateAction<string>>;
} }
const Search = ({ setPhoneSearch }: ISearch) => { const Search = ({ setValueSearch, setPlaceHolder }: ISearch) => {
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
useEffect(() => { useEffect(() => {
const timeOut = setTimeout(() => { const timeOut = setTimeout(() => {
setPhoneSearch(query); setValueSearch(query);
}, 500); }, 500);
return () => clearTimeout(timeOut); return () => clearTimeout(timeOut);
}, [setPhoneSearch, query]); }, [setValueSearch, query]);
return ( return (
<StyledSearch <StyledSearch
onChange={(e) => setQuery(e.target.value)} onChange={(e) => setQuery(e.target.value)}
placeholder="Search by phone..." placeholder={setPlaceHolder}
/> />
); );
}; };
@@ -1,4 +1,6 @@
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
// Components
import Select from './Select'; import Select from './Select';
interface ISortByProps { interface ISortByProps {
@@ -15,6 +15,7 @@ const CommonRow = styled.div<ITable>`
grid-template-columns: ${(props) => props.columns}; grid-template-columns: ${(props) => props.columns};
column-gap: 10px; column-gap: 10px;
align-items: center; align-items: center;
justify-items: center;
`; `;
const StyledBody = styled.div` const StyledBody = styled.div`
@@ -10,6 +10,7 @@ const ADD_SUCCESS = 'Add success';
const EDIT_SUCCESS = 'Edit success'; const EDIT_SUCCESS = 'Edit success';
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 REQUIRED_FIELD_ERROR = 'This is required field';
export { export {
invalidFormatMsg, invalidFormatMsg,
@@ -18,4 +19,5 @@ export {
EDIT_SUCCESS, EDIT_SUCCESS,
CONFIRM_DELETE, CONFIRM_DELETE,
DELETE_SUCCESS, DELETE_SUCCESS,
REQUIRED_FIELD_ERROR,
}; };
+1
View File
@@ -4,3 +4,4 @@ export const ROOM: string = '/room';
export const OTHER_PATH: string = '*'; export const OTHER_PATH: string = '*';
export const BASE_URL: string = 'https://hotel-management-api.loiphan.com/'; export const BASE_URL: string = 'https://hotel-management-api.loiphan.com/';
export const USER_PATH = 'users'; export const USER_PATH = 'users';
export const ROOM_PATH = 'rooms';
+44 -1
View File
@@ -34,4 +34,47 @@ 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',
},
],
}
const VALUE = 'value';
const ERROR = 'error';
export { USER_PAGE, ROOM_PAGE, VALUE, ERROR };
+2 -5
View File
@@ -1,6 +1,3 @@
// Types
import { TUser } from './types';
interface IMenusContext { interface IMenusContext {
openId?: string; openId?: string;
close?: () => void; close?: () => void;
@@ -23,14 +20,14 @@ interface ITableBody<T> {
render?: (value: T) => JSX.Element; render?: (value: T) => JSX.Element;
} }
interface IDialogProps { interface IDialogProps<T> {
title?: string; title?: string;
children?: JSX.Element[] | JSX.Element; children?: JSX.Element[] | JSX.Element;
onClose?: () => void; onClose?: () => void;
reload?: boolean; reload?: boolean;
setReload?: React.Dispatch<React.SetStateAction<boolean>>; setReload?: React.Dispatch<React.SetStateAction<boolean>>;
ref?: React.MutableRefObject<HTMLDialogElement | undefined>; ref?: React.MutableRefObject<HTMLDialogElement | undefined>;
user?: TUser | null; data?: T | null;
isAdd?: boolean; isAdd?: boolean;
} }
+17 -1
View File
@@ -7,6 +7,16 @@ type TUser = {
roomId: string; roomId: string;
}; };
type TRoom = {
id: string;
name: string;
amount: string;
price: string;
discount: string;
description: string;
status: boolean;
};
type TStateSchema = { type TStateSchema = {
[key: string]: { [key: string]: {
value?: string; value?: string;
@@ -15,7 +25,11 @@ type TStateSchema = {
}; };
type TKeyValue = { type TKeyValue = {
[key: string]: string | undefined | boolean; [key: string]: boolean | undefined | string;
};
type TKeyString = {
[key: string]: string;
}; };
type TPropValues = 'value' | 'error' | boolean; type TPropValues = 'value' | 'error' | boolean;
@@ -37,8 +51,10 @@ type TResponse = {
export type { export type {
TUser, TUser,
TRoom,
TStateSchema, TStateSchema,
TKeyValue, TKeyValue,
TKeyString,
TPropValues, TPropValues,
TValidator, TValidator,
TResponse, TResponse,
@@ -1,8 +1,18 @@
// Constants // Constants
import { BASE_URL } from '../constants/path'; import { BASE_URL } from '../constants/path';
// Types
import { TResponse } from '../globals/types'; import { TResponse } from '../globals/types';
type TMethodRequest = 'GET' | 'POST' | 'PUT' | 'DELETE'; 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 ( export const sendRequest = async (
path: string, path: string,
body: BodyInit | null | undefined, body: BodyInit | null | undefined,
+70 -16
View File
@@ -1,26 +1,49 @@
// Constants // Constants
import { invalidFormatMsg } from '../constants/messages'; import { REQUIRED_FIELD_ERROR, invalidFormatMsg } from '../constants/messages';
// Types // Types
import { TKeyValue, TPropValues, TStateSchema, TUser } from '../globals/types'; import {
TKeyString,
const VALUE = 'value'; TKeyValue,
const ERROR = 'error'; TPropValues,
const REQUIRED_FIELD_ERROR = 'This is required field'; 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) => { const isBool = (value: unknown) => {
return typeof value === 'boolean'; 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) => { const isObject = (value: unknown) => {
return typeof value === 'object' && value !== null; 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) => { const isRequired = (value: string | number, isRequired: unknown) => {
if (!value && isRequired) return REQUIRED_FIELD_ERROR; if (!value && isRequired) return REQUIRED_FIELD_ERROR;
return ''; 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) => { const getPropValues = (stateSchema: TStateSchema, prop?: TPropValues) => {
return Object.keys(stateSchema).reduce((field, key) => { return Object.keys(stateSchema).reduce((field, key) => {
field[key] = isBool(prop) field[key] = isBool(prop)
@@ -37,6 +60,11 @@ type TValidator = {
required?: boolean; required?: boolean;
}; };
/**
* Create validator object
* @param param0 Pass TValidator object
* @returns An object contains condition validator
*/
const addValidator = ({ validatorFunc, prop, required = true }: TValidator) => { const addValidator = ({ validatorFunc, prop, required = true }: TValidator) => {
return { return {
required, required,
@@ -47,18 +75,46 @@ const addValidator = ({ validatorFunc, prop, required = true }: TValidator) => {
}; };
}; };
const getValueUser = (user: TUser | null = null, prop: string): string => { /**
if (user) { * Return the object contains values of object pass
return user[prop as keyof TUser]; * @param obj Object need to get value
* @returns The object contains value of object
*/
const getValueFromObj = <T>(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'
? tempValue
: '';
result = { ...result, [`${key}Value`]: value };
}
} }
return ''; return result;
}; };
const searchQuery = (phone: string, sort: string, order: string) => { /**
* 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,
keySearch: string,
sort: string,
order: string,
) => {
// prettier-ignore // prettier-ignore
const phoneParams = phone const phoneParams = keySearch
? 'phone_like=' + phone ? `${columnSearch}_like=` + keySearch
: ''; : '';
// prettier-ignore // prettier-ignore
@@ -92,10 +148,8 @@ export {
isObject, isObject,
isRequired, isRequired,
getPropValues, getPropValues,
getValueUser, getValueFromObj,
addValidator, addValidator,
searchQuery, searchQuery,
VALUE,
ERROR,
REQUIRED_FIELD_ERROR, REQUIRED_FIELD_ERROR,
}; };
+37 -5
View File
@@ -1,17 +1,49 @@
const isValidString = (value: string): boolean => { /**
return /^[a-zA-Z]{4,}(?: [a-zA-Z]+){0,2}$/gm.test(value); * 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 => { const isValidNumber = (value: string): boolean => {
return /^[0-9]*$/.test(value); 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 => { const isValidPhoneNumber = (value: string): boolean => {
return /(84|0[3|5|7|8|9])+([0-9]{8})\b/g.test(value); 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; * 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;
}; };
export { isValidString, isValidNumber, isValidPhoneNumber, isValidAddress }; /**
* A method skip check value
* @returns Return true
*/
const skipCheck = () => true;
export {
isValidName,
isValidNumber,
isValidPhoneNumber,
isValidString,
skipCheck,
};