mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 20:01:59 +00:00
Refactor code
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
- Introduce: Build a hotel management web application using HTML5, CSS3, Typescript, json-server, React and compile by Vite.
|
- Introduce: Build a hotel management web application using HTML5, CSS3, Typescript, json-server, React and compile by Vite.
|
||||||
- Plan: [Link](https://docs.google.com/document/d/1t2kx17iGCceEe3EbaKO6-3g70ejOJ1PAu9u3KXSKWi4/edit?usp=sharing)
|
- Plan: [Link](https://docs.google.com/document/d/1t2kx17iGCceEe3EbaKO6-3g70ejOJ1PAu9u3KXSKWi4/edit?usp=sharing)
|
||||||
- Deploy: _Update later_
|
- Deploy: [Link](https://hotel-management.loiphan.com/)
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
## Technical
|
## Technical
|
||||||
|
|
||||||
- [HTML5](https://www.tutorialspoint.com/html5/html5_overview.htm): CSS (Cascading Style Sheets) consist of a group of formatting rules that you use to control the layout and appearance of the content on a web page.
|
- [HTML5](https://www.tutorialspoint.com/html5/html5_overview.htm): HTML5 is the latest standard of Hypertext Markup Language, the code that describes the structure and presentation of web pages.
|
||||||
- [CSS3](https://www.htmlgoodies.com/html5/an-overview-of-css3/): Cascading Style Sheets (CSS) is a language used to illustrate a document’s look, style, and format in any markup language. In simple words, it is used to style and organize the layout of Web pages. CSS3 is the latest version of an earlier CSS version, CSS2.
|
- [CSS3](https://www.htmlgoodies.com/html5/an-overview-of-css3/): Cascading Style Sheets (CSS) is a language used to illustrate a document’s look, style, and format in any markup language. In simple words, it is used to style and organize the layout of Web pages. CSS3 is the latest version of an earlier CSS version, CSS2.
|
||||||
- [Typescript](https://www.typescriptlang.org/): TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.
|
- [Typescript](https://www.typescriptlang.org/): TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.
|
||||||
- [React](https://react.dev/): React is a framework that employs Webpack to automatically compile React, JSX, and ES6 code while handling CSS file prefixes. React is a JavaScript-based UI development library. Although React is a library rather than a language, it is widely used in web development.
|
- [React](https://react.dev/): React is a framework that employs Webpack to automatically compile React, JSX, and ES6 code while handling CSS file prefixes. React is a JavaScript-based UI development library. Although React is a library rather than a language, it is widely used in web development.
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
- Timeline:
|
- Timeline:
|
||||||
- Estimate: 11 days
|
- Estimate: 11 days
|
||||||
- Actual: -- days.
|
- Actual: 13 days.
|
||||||
- Calendar:
|
- Calendar:
|
||||||
- Start: 2023/10/16
|
- Start: 2023/10/16
|
||||||
- End: 2023/10/30
|
- End: 2023/10/30
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import { Main, StyledAppLayout } from './styled';
|
|||||||
const AppLayout = () => {
|
const AppLayout = () => {
|
||||||
return (
|
return (
|
||||||
<StyledAppLayout>
|
<StyledAppLayout>
|
||||||
<Header />
|
<Header accountName='Admin'/>
|
||||||
<Sidebar />
|
<Sidebar heading='Hotel Management'/>
|
||||||
<Main>
|
<Main>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</Main>
|
</Main>
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
import { forwardRef } from 'react';
|
import { forwardRef } from 'react';
|
||||||
|
|
||||||
// Components
|
|
||||||
import { IDialogProps } from '../../globals/interfaces';
|
|
||||||
|
|
||||||
// Styled
|
// Styled
|
||||||
import { StyledBody, StyledDialog, StyledTitle } from './styled';
|
import { StyledBody, StyledDialog, StyledTitle } from './styled';
|
||||||
|
|
||||||
|
export interface IDialogProps<T> {
|
||||||
|
title?: string;
|
||||||
|
children?: JSX.Element[] | JSX.Element;
|
||||||
|
onClose?: () => void;
|
||||||
|
reload?: boolean;
|
||||||
|
setReload?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
ref?: React.MutableRefObject<HTMLDialogElement | undefined>;
|
||||||
|
data?: T | null;
|
||||||
|
isAdd?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const Dialog = forwardRef((props, ref) => {
|
const Dialog = forwardRef((props, ref) => {
|
||||||
const { title, children, onClose } = props as IDialogProps<unknown>;
|
const { title, children, onClose } = props as IDialogProps<unknown>;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -4,10 +4,14 @@ import HeaderMenu from '../HeaderMenu';
|
|||||||
// Styled
|
// Styled
|
||||||
import { StyledHeader } from './styled';
|
import { StyledHeader } from './styled';
|
||||||
|
|
||||||
const Header = () => {
|
interface IHeader {
|
||||||
|
accountName: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Header = ({accountName}: IHeader) => {
|
||||||
return (
|
return (
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
<p>Hi, Admin!</p>
|
<p>Hi, {accountName}!</p>
|
||||||
<HeaderMenu />
|
<HeaderMenu />
|
||||||
</StyledHeader>
|
</StyledHeader>
|
||||||
);
|
);
|
||||||
|
|||||||
+6
-4
@@ -5,12 +5,15 @@ import { HiEllipsisVertical } from 'react-icons/hi2';
|
|||||||
import { useOutsideClick } from '../../hooks/useOutsideClick';
|
import { useOutsideClick } from '../../hooks/useOutsideClick';
|
||||||
import { StyledMenu, StyledButton, StyledList, StyledToggle } from './styled';
|
import { StyledMenu, StyledButton, StyledList, StyledToggle } from './styled';
|
||||||
|
|
||||||
// Interfaces
|
|
||||||
import { IButton } from '../../globals/interfaces';
|
|
||||||
|
|
||||||
// Contexts
|
// Contexts
|
||||||
import MenusContext from '../../contexts/MenuContext';
|
import MenusContext from '../../contexts/MenuContext';
|
||||||
|
|
||||||
|
interface IButton {
|
||||||
|
children?: string;
|
||||||
|
icon?: JSX.Element;
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
const Menus = ({ children }: { children: JSX.Element }) => {
|
const Menus = ({ children }: { children: JSX.Element }) => {
|
||||||
const [openId, setOpenId] = useState('');
|
const [openId, setOpenId] = useState('');
|
||||||
|
|
||||||
@@ -30,7 +33,6 @@ const Toggle = ({ id }: { id: string }): React.JSX.Element => {
|
|||||||
const handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
const handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
openId === '' || openId !== id
|
openId === '' || openId !== id
|
||||||
? open!(id)
|
? open!(id)
|
||||||
: close!();
|
: close!();
|
||||||
@@ -6,18 +6,21 @@ import { BsHouseDoorFill } from 'react-icons/bs';
|
|||||||
// Styled
|
// Styled
|
||||||
import { StyledNav, StyledNavLink } from './styled';
|
import { StyledNav, StyledNavLink } from './styled';
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import { DASHBOARD, ROOM, USER } from '../../constants/path';
|
||||||
|
|
||||||
const Nav = () => {
|
const Nav = () => {
|
||||||
return (
|
return (
|
||||||
<StyledNav>
|
<StyledNav>
|
||||||
<StyledNavLink to={'/dashboard'}>
|
<StyledNavLink to={DASHBOARD}>
|
||||||
<MdSpaceDashboard />
|
<MdSpaceDashboard />
|
||||||
<span>Dashboard</span>
|
<span>Dashboard</span>
|
||||||
</StyledNavLink>
|
</StyledNavLink>
|
||||||
<StyledNavLink to={'/user'}>
|
<StyledNavLink to={USER}>
|
||||||
<HiUsers />
|
<HiUsers />
|
||||||
<span>User</span>
|
<span>User</span>
|
||||||
</StyledNavLink>
|
</StyledNavLink>
|
||||||
<StyledNavLink to={'/room'}>
|
<StyledNavLink to={ROOM}>
|
||||||
<BsHouseDoorFill />
|
<BsHouseDoorFill />
|
||||||
<span>Room</span>
|
<span>Room</span>
|
||||||
</StyledNavLink>
|
</StyledNavLink>
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { ISelectOptions } from '../../globals/interfaces';
|
|
||||||
import { StyledSelect } from './styled';
|
import { StyledSelect } from './styled';
|
||||||
|
|
||||||
|
export interface ISelectOptions {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
interface ISelect {
|
interface ISelect {
|
||||||
options: ISelectOptions[];
|
options: ISelectOptions[];
|
||||||
value: string;
|
value: string;
|
||||||
|
|||||||
@@ -4,10 +4,14 @@ import Nav from '../Nav';
|
|||||||
// Styled
|
// Styled
|
||||||
import { Heading, StyledSidebar } from './styled';
|
import { Heading, StyledSidebar } from './styled';
|
||||||
|
|
||||||
const Sidebar = () => {
|
interface ISidebar {
|
||||||
|
heading: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Sidebar = ({heading}: ISidebar) => {
|
||||||
return (
|
return (
|
||||||
<StyledSidebar>
|
<StyledSidebar>
|
||||||
<Heading>Hotel Management</Heading>
|
<Heading>{heading}</Heading>
|
||||||
<Nav />
|
<Nav />
|
||||||
</StyledSidebar>
|
</StyledSidebar>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Select from '../Select';
|
import Select from '../Select';
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
interface ISortByProps {
|
interface ISortByProps {
|
||||||
options: {
|
options: {
|
||||||
|
|||||||
@@ -3,24 +3,31 @@ import { ReactNode, useContext } from 'react';
|
|||||||
// Components
|
// Components
|
||||||
import { StyledBody, StyledHeader, StyledRow, StyledTable } from './styled';
|
import { StyledBody, StyledHeader, StyledRow, StyledTable } from './styled';
|
||||||
|
|
||||||
// Interfaces
|
|
||||||
import { ITable, ITableBody } from '../../globals/interfaces';
|
|
||||||
|
|
||||||
// Contexts
|
// Contexts
|
||||||
import TableContext from '../../contexts/TableContext';
|
import TableContext from '../../contexts/TableContext';
|
||||||
|
|
||||||
|
interface ITable {
|
||||||
|
columns?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ITableBody<T> {
|
||||||
|
data?: T[];
|
||||||
|
render?: (value: T) => JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
type CallbackMapFunc<T> = (value: T, index: number, array: T[]) => ReactNode;
|
type CallbackMapFunc<T> = (value: T, index: number, array: T[]) => ReactNode;
|
||||||
|
|
||||||
const Table = ({ columns, children }: ITable) => {
|
const Table = ({ columns, children }: ITable) => {
|
||||||
return (
|
return (
|
||||||
<TableContext.Provider value={columns!}>
|
<TableContext.Provider value={{ columns }}>
|
||||||
<StyledTable>{children}</StyledTable>
|
<StyledTable>{children}</StyledTable>
|
||||||
</TableContext.Provider>
|
</TableContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Header = ({ children }: ITable) => {
|
const Header = ({ children }: ITable) => {
|
||||||
const columns = useContext(TableContext);
|
const { columns } = useContext(TableContext);
|
||||||
return <StyledHeader columns={columns}>{children}</StyledHeader>;
|
return <StyledHeader columns={columns}>{children}</StyledHeader>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,7 +40,7 @@ const Body = <T,>({ data, render }: ITableBody<T>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Row = ({ children }: ITable) => {
|
const Row = ({ children }: ITable) => {
|
||||||
const columns = useContext(TableContext);
|
const { columns } = useContext(TableContext);
|
||||||
return <StyledRow columns={columns}>{children}</StyledRow>;
|
return <StyledRow columns={columns}>{children}</StyledRow>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,46 @@
|
|||||||
import { Toaster } from 'react-hot-toast';
|
import { DefaultToastOptions, ToastOptions, ToastPosition, Toaster } from 'react-hot-toast';
|
||||||
|
import { CSSProperties } from 'styled-components';
|
||||||
|
|
||||||
const Toast = () => {
|
interface IToast {
|
||||||
|
position: ToastPosition;
|
||||||
|
gutter: number;
|
||||||
|
containerStyle: CSSProperties;
|
||||||
|
toastOptions: DefaultToastOptions;
|
||||||
|
success: ToastOptions;
|
||||||
|
error: ToastOptions;
|
||||||
|
style: CSSProperties | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Toast = ({position, gutter, containerStyle, success, error, style}: IToast) => {
|
||||||
return (
|
return (
|
||||||
<Toaster
|
<Toaster
|
||||||
position="top-center"
|
position={position}
|
||||||
gutter={12}
|
gutter={gutter}
|
||||||
containerStyle={{ margin: '8px', zIndex: 1 }}
|
containerStyle={containerStyle}
|
||||||
toastOptions={{
|
toastOptions={{
|
||||||
success: {
|
success,
|
||||||
duration: 3000,
|
error,
|
||||||
},
|
style
|
||||||
error: {
|
|
||||||
duration: 5000,
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
fontSize: '16px',
|
|
||||||
maxWidth: '500px',
|
|
||||||
padding: '16px 24px',
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Toast.defaultProps = {
|
||||||
|
position: "top-center",
|
||||||
|
gutter: 12,
|
||||||
|
containerStyle: { margin: '8px', zIndex: 1 },
|
||||||
|
success: {
|
||||||
|
duration: 3000,
|
||||||
|
},
|
||||||
|
error:{
|
||||||
|
duration: 5000,
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fontSize: '16px',
|
||||||
|
maxWidth: '500px',
|
||||||
|
padding: '16px 24px',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default Toast;
|
export default Toast;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export const DASHBOARD: string = '/dashboard';
|
export const DASHBOARD = '/dashboard';
|
||||||
export const USER: string = '/user';
|
export const USER = '/user';
|
||||||
export const ROOM: string = '/room';
|
export const ROOM = '/room';
|
||||||
export const OTHER_PATH: string = '*';
|
export const OTHER_PATH = '*';
|
||||||
export const BASE_URL: string = 'https://hotel-management-api.loiphan.com/';
|
export const BASE_URL = 'https://hotel-management-api.loiphan.com/';
|
||||||
export const USER_PATH = 'users';
|
export const USER_PATH = 'users';
|
||||||
export const ROOM_PATH = 'rooms';
|
export const ROOM_PATH = 'rooms';
|
||||||
|
|||||||
@@ -74,7 +74,16 @@ const ROOM_PAGE = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const INITIAL_STATE_SCHEMA = {
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
identifiedCode: '',
|
||||||
|
phone: '',
|
||||||
|
roomId: 'undefined',
|
||||||
|
address: '',
|
||||||
|
};
|
||||||
|
|
||||||
const VALUE = 'value';
|
const VALUE = 'value';
|
||||||
const ERROR = 'error';
|
const ERROR = 'error';
|
||||||
|
|
||||||
export { USER_PAGE, ROOM_PAGE, VALUE, ERROR };
|
export { USER_PAGE, ROOM_PAGE, VALUE, ERROR, INITIAL_STATE_SCHEMA };
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
// Interfaces
|
interface IMenusContext {
|
||||||
import { IMenusContext } from '../globals/interfaces';
|
openId?: string;
|
||||||
|
close?: () => void;
|
||||||
|
open?: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
}
|
||||||
|
|
||||||
const MenusContext = createContext<IMenusContext>({});
|
const MenusContext = createContext<IMenusContext>({});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
const TableContext = createContext('');
|
interface ITableContext {
|
||||||
|
columns?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TableContext = createContext<ITableContext>({});
|
||||||
|
|
||||||
export default TableContext;
|
export default TableContext;
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
interface IMenusContext {
|
|
||||||
openId?: string;
|
|
||||||
close?: () => void;
|
|
||||||
open?: React.Dispatch<React.SetStateAction<string>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IButton {
|
|
||||||
children?: string;
|
|
||||||
icon?: JSX.Element;
|
|
||||||
onClick?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ITable {
|
|
||||||
columns?: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ITableBody<T> {
|
|
||||||
data?: T[];
|
|
||||||
render?: (value: T) => JSX.Element;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ISelectOptions {
|
|
||||||
value: string;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IDialogProps<T> {
|
|
||||||
title?: string;
|
|
||||||
children?: JSX.Element[] | JSX.Element;
|
|
||||||
onClose?: () => void;
|
|
||||||
reload?: boolean;
|
|
||||||
setReload?: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
ref?: React.MutableRefObject<HTMLDialogElement | undefined>;
|
|
||||||
data?: T | null;
|
|
||||||
isAdd?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type {
|
|
||||||
IMenusContext,
|
|
||||||
IButton,
|
|
||||||
ITable,
|
|
||||||
ITableBody,
|
|
||||||
IDialogProps,
|
|
||||||
ISelectOptions,
|
|
||||||
};
|
|
||||||
@@ -124,7 +124,7 @@ const searchQuery = (
|
|||||||
columnSearch: string,
|
columnSearch: string,
|
||||||
keySearch: string,
|
keySearch: string,
|
||||||
sort: string,
|
sort: string,
|
||||||
order: string,
|
order: string
|
||||||
) => {
|
) => {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const phoneParams = keySearch
|
const phoneParams = keySearch
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ const isValidNumber = (value: string): boolean => {
|
|||||||
* @returns A boolean indicating whether or not the argument has valid
|
* @returns A boolean indicating whether or not the argument has valid
|
||||||
*/
|
*/
|
||||||
const isValidDiscount = (value: string): boolean => {
|
const isValidDiscount = (value: string): boolean => {
|
||||||
console.log(Boolean(+value >= 0 && +value <= 100));
|
|
||||||
return +value >= 0 && +value <= 100;
|
return +value >= 0 && +value <= 100;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { DEFAULT_ORDER_BY, DEFAULT_SORT_BY } from '../constants/config';
|
|||||||
import { searchQuery } from '../helpers/utils';
|
import { searchQuery } from '../helpers/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* The function use to fetch data on server API.
|
||||||
* @param path The path of url
|
* @param path The path of url
|
||||||
* @param columnSearch Column need to search
|
* @param columnSearch Column need to search
|
||||||
* @param keyWord The key word to search
|
* @param keyWord The key word to search
|
||||||
@@ -23,7 +23,7 @@ const useFetch = (
|
|||||||
keyWord: string = '',
|
keyWord: string = '',
|
||||||
tempSortBy?: string,
|
tempSortBy?: string,
|
||||||
tempOrderBy?: string,
|
tempOrderBy?: string,
|
||||||
reload?: boolean,
|
reload?: boolean
|
||||||
) => {
|
) => {
|
||||||
const [data, setData] = useState(null);
|
const [data, setData] = useState(null);
|
||||||
const [isPending, setIsPending] = useState(false);
|
const [isPending, setIsPending] = useState(false);
|
||||||
@@ -56,7 +56,7 @@ const useFetch = (
|
|||||||
|
|
||||||
if (!response.ok)
|
if (!response.ok)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Error code: ${response.status} \n Messages: ${response.statusText}`,
|
`Error code: ${response.status} \n Messages: ${response.statusText}`
|
||||||
);
|
);
|
||||||
|
|
||||||
setIsPending(false);
|
setIsPending(false);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { getPropValues, isObject, isRequired } from '../helpers/utils';
|
|||||||
import { TKeyValue, TValidator } from '../globals/types';
|
import { TKeyValue, TValidator } from '../globals/types';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { ERROR, VALUE } from '../constants/variables';
|
import { ERROR, INITIAL_STATE_SCHEMA, VALUE } from '../constants/variables';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom hooks to validate your Form...
|
* Custom hooks to validate your Form...
|
||||||
@@ -21,11 +21,11 @@ const useForm = (
|
|||||||
stateSchema = {},
|
stateSchema = {},
|
||||||
stateValidatorSchema = {} as TValidator,
|
stateValidatorSchema = {} as TValidator,
|
||||||
submitFormCallback: (values: TKeyValue) => void,
|
submitFormCallback: (values: TKeyValue) => void,
|
||||||
initialValue: string = '',
|
initialValue: string = ''
|
||||||
) => {
|
) => {
|
||||||
const [values, setValues] = useState(getPropValues(stateSchema, VALUE));
|
const [values, setValues] = useState<TKeyValue>(INITIAL_STATE_SCHEMA);
|
||||||
const [errors, setErrors] = useState(getPropValues(stateSchema, ERROR));
|
const [errors, setErrors] = useState(getPropValues(stateSchema, ERROR));
|
||||||
const [dirty, setDirty] = useState(getPropValues(stateSchema));
|
const [valid, isValid] = useState(getPropValues(stateSchema));
|
||||||
const [disable, setDisable] = useState(true);
|
const [disable, setDisable] = useState(true);
|
||||||
const [isDirty, setIsDirty] = useState(false);
|
const [isDirty, setIsDirty] = useState(false);
|
||||||
|
|
||||||
@@ -33,7 +33,8 @@ const useForm = (
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setInitialErrorState(initialValue);
|
setInitialErrorState(initialValue);
|
||||||
setDisable(true);
|
setDisable(true);
|
||||||
setDirty(getPropValues(stateSchema));
|
isValid(getPropValues(stateSchema));
|
||||||
|
setValues(getPropValues(stateSchema, VALUE));
|
||||||
|
|
||||||
// If initial value true, setValues again from stateSchema
|
// If initial value true, setValues again from stateSchema
|
||||||
// and enabled button
|
// and enabled button
|
||||||
@@ -56,7 +57,7 @@ const useForm = (
|
|||||||
let error = '';
|
let error = '';
|
||||||
|
|
||||||
// Skip check id field
|
// Skip check id field
|
||||||
if (name !== 'id') {
|
if (name !== 'id' && name !== 'roomId') {
|
||||||
error = isRequired(value, field!.required);
|
error = isRequired(value, field!.required);
|
||||||
|
|
||||||
if (isObject(field['validator']) && error === '') {
|
if (isObject(field['validator']) && error === '') {
|
||||||
@@ -72,7 +73,7 @@ const useForm = (
|
|||||||
|
|
||||||
return error;
|
return error;
|
||||||
},
|
},
|
||||||
[stateValidatorSchema],
|
[stateValidatorSchema]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set Initial Error State
|
// Set Initial Error State
|
||||||
@@ -85,10 +86,10 @@ const useForm = (
|
|||||||
[name]: !initialValue // Skip error when initialValue have values
|
[name]: !initialValue // Skip error when initialValue have values
|
||||||
? validateFormFields(name, values[name] as string)
|
? validateFormFields(name, values[name] as string)
|
||||||
: '',
|
: '',
|
||||||
})),
|
}))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[errors, values, validateFormFields],
|
[errors, values, validateFormFields]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Used to disable submit button if there's a value in errors
|
// Used to disable submit button if there's a value in errors
|
||||||
@@ -97,7 +98,7 @@ const useForm = (
|
|||||||
// in every re-render in component
|
// in every re-render in component
|
||||||
const validateErrorState = useCallback(
|
const validateErrorState = useCallback(
|
||||||
() => Object.values(errors).some((error) => error),
|
() => Object.values(errors).some((error) => error),
|
||||||
[errors],
|
[errors]
|
||||||
);
|
);
|
||||||
|
|
||||||
// For every changed in our state this will be fired
|
// For every changed in our state this will be fired
|
||||||
@@ -113,7 +114,7 @@ const useForm = (
|
|||||||
(
|
(
|
||||||
event: ChangeEvent<
|
event: ChangeEvent<
|
||||||
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||||
>,
|
>
|
||||||
) => {
|
) => {
|
||||||
setIsDirty(true);
|
setIsDirty(true);
|
||||||
|
|
||||||
@@ -133,9 +134,9 @@ const useForm = (
|
|||||||
|
|
||||||
setValues((prevState) => ({ ...prevState, [name]: value }));
|
setValues((prevState) => ({ ...prevState, [name]: value }));
|
||||||
setErrors((prevState) => ({ ...prevState, [name]: error }));
|
setErrors((prevState) => ({ ...prevState, [name]: error }));
|
||||||
setDirty((prevState) => ({ ...prevState, [name]: true }));
|
isValid((prevState) => ({ ...prevState, [name]: true }));
|
||||||
},
|
},
|
||||||
[validateFormFields],
|
[validateFormFields]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnSubmit = useCallback(
|
const handleOnSubmit = useCallback(
|
||||||
@@ -151,7 +152,7 @@ const useForm = (
|
|||||||
setDisable(true);
|
setDisable(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[validateErrorState, submitFormCallback, values],
|
[validateErrorState, submitFormCallback, values]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -162,7 +163,7 @@ const useForm = (
|
|||||||
disable,
|
disable,
|
||||||
setValues,
|
setValues,
|
||||||
setErrors,
|
setErrors,
|
||||||
dirty,
|
valid,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { forwardRef, useEffect } from 'react';
|
import { forwardRef, useEffect } from 'react';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Dialog from '../../components/Dialog';
|
import Dialog, { IDialogProps } from '../../components/Dialog';
|
||||||
import RoomForm from './Form';
|
import RoomForm from './Form';
|
||||||
|
|
||||||
// Interfaces
|
|
||||||
import { IDialogProps } from '../../globals/interfaces';
|
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { TRoom } from '../../globals/types';
|
import { TRoom } from '../../globals/types';
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import TextArea from '../../commons/styles/TextArea.ts';
|
|||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Form from '../../components/Form/index.tsx';
|
import Form from '../../components/Form/index.tsx';
|
||||||
import FormRow from '../../components/FormRow/index.tsx';
|
import FormRow from '../../components/LabelControl/index.tsx';
|
||||||
import Button from '../../commons/styles/Button.ts';
|
import Button from '../../commons/styles/Button.ts';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
@@ -170,7 +170,7 @@ const RoomForm = ({
|
|||||||
const response = await sendRequest(
|
const response = await sendRequest(
|
||||||
ROOM_PATH,
|
ROOM_PATH,
|
||||||
JSON.stringify(data),
|
JSON.stringify(data),
|
||||||
'POST',
|
'POST'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode === STATUS_CODE.CREATE) {
|
if (response.statusCode === STATUS_CODE.CREATE) {
|
||||||
@@ -185,7 +185,7 @@ const RoomForm = ({
|
|||||||
const response = await sendRequest(
|
const response = await sendRequest(
|
||||||
ROOM_PATH + `/${room!.id}`,
|
ROOM_PATH + `/${room!.id}`,
|
||||||
JSON.stringify(data),
|
JSON.stringify(data),
|
||||||
'PUT',
|
'PUT'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == STATUS_CODE.OK) {
|
if (response.statusCode == STATUS_CODE.OK) {
|
||||||
@@ -215,7 +215,7 @@ const RoomForm = ({
|
|||||||
const {
|
const {
|
||||||
values,
|
values,
|
||||||
errors,
|
errors,
|
||||||
dirty,
|
valid,
|
||||||
handleOnChange,
|
handleOnChange,
|
||||||
handleOnSubmit,
|
handleOnSubmit,
|
||||||
disable } =
|
disable } =
|
||||||
@@ -257,7 +257,7 @@ const RoomForm = ({
|
|||||||
label="Name"
|
label="Name"
|
||||||
error={
|
error={
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
errors.name && dirty.name
|
errors.name && valid.name
|
||||||
? (errors.name as string)
|
? (errors.name as string)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
@@ -274,7 +274,7 @@ const RoomForm = ({
|
|||||||
label="Amount"
|
label="Amount"
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
error={
|
error={
|
||||||
errors.amount && dirty.amount
|
errors.amount && valid.amount
|
||||||
? (errors.amount as string)
|
? (errors.amount as string)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
@@ -291,7 +291,7 @@ const RoomForm = ({
|
|||||||
label="Price"
|
label="Price"
|
||||||
error={
|
error={
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
errors.price && dirty.price
|
errors.price && valid.price
|
||||||
? (errors.price as string)
|
? (errors.price as string)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
@@ -308,7 +308,7 @@ const RoomForm = ({
|
|||||||
label="Discount"
|
label="Discount"
|
||||||
error={
|
error={
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
errors.discount && dirty.discount
|
errors.discount && valid.discount
|
||||||
? (errors.discount as string)
|
? (errors.discount as string)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
@@ -334,7 +334,7 @@ const RoomForm = ({
|
|||||||
label="Description"
|
label="Description"
|
||||||
error={
|
error={
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
errors.description && dirty.description
|
errors.description && valid.description
|
||||||
? (errors.description as string)
|
? (errors.description as string)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import toast from 'react-hot-toast';
|
|||||||
import { HiSquare2Stack } from 'react-icons/hi2';
|
import { HiSquare2Stack } from 'react-icons/hi2';
|
||||||
import { HiTrash } from 'react-icons/hi';
|
import { HiTrash } from 'react-icons/hi';
|
||||||
import { StyledOperationTable } from './styled';
|
import { StyledOperationTable } from './styled';
|
||||||
import Menus from '../../components/Menus/Menus';
|
import Menus from '../../components/Menus';
|
||||||
import Table from '../../components/Table';
|
import Table from '../../components/Table';
|
||||||
import Direction from '../../commons/styles/Direction';
|
import Direction from '../../commons/styles/Direction';
|
||||||
import Message from '../../components/Message';
|
import Message from '../../components/Message';
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import { forwardRef, useEffect } from 'react';
|
import { forwardRef, useEffect } from 'react';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Dialog from '../../components/Dialog';
|
import Dialog, { IDialogProps } from '../../components/Dialog';
|
||||||
import UserForm from './Form';
|
import UserForm from './Form';
|
||||||
|
|
||||||
// Interfaces
|
|
||||||
import { IDialogProps } from '../../globals/interfaces';
|
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { TUser } from '../../globals/types';
|
import { TUser } from '../../globals/types';
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import TextArea from '../../commons/styles/TextArea';
|
|||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Form from '../../components/Form';
|
import Form from '../../components/Form';
|
||||||
import FormRow from '../../components/FormRow';
|
import FormRow from '../../components/LabelControl/index.tsx';
|
||||||
import Button from '../../commons/styles/Button.ts';
|
import Button from '../../commons/styles/Button.ts';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
@@ -39,12 +39,9 @@ import {
|
|||||||
errorMsg,
|
errorMsg,
|
||||||
} from '../../constants/messages.ts';
|
} from '../../constants/messages.ts';
|
||||||
import { USER_PATH } from '../../constants/path.ts';
|
import { USER_PATH } from '../../constants/path.ts';
|
||||||
import Select from '../../components/Select/index.tsx';
|
import Select, { ISelectOptions } from '../../components/Select/index.tsx';
|
||||||
import { useFetch } from '../../hooks/useFetch.ts';
|
import { useFetch } from '../../hooks/useFetch.ts';
|
||||||
|
|
||||||
// Interfaces
|
|
||||||
import { ISelectOptions } from '../../globals/interfaces.ts';
|
|
||||||
|
|
||||||
const FormBtn = styled(Button)`
|
const FormBtn = styled(Button)`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
@@ -130,7 +127,7 @@ const UserForm = ({
|
|||||||
error: '',
|
error: '',
|
||||||
},
|
},
|
||||||
roomId: {
|
roomId: {
|
||||||
value: roomIdValue || '',
|
value: roomIdValue || '' + (options && options[0].value),
|
||||||
error: '' ,
|
error: '' ,
|
||||||
},
|
},
|
||||||
address: {
|
address: {
|
||||||
@@ -181,7 +178,7 @@ const UserForm = ({
|
|||||||
const response = await sendRequest(
|
const response = await sendRequest(
|
||||||
USER_PATH,
|
USER_PATH,
|
||||||
JSON.stringify(data),
|
JSON.stringify(data),
|
||||||
'POST',
|
'POST'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode === STATUS_CODE.CREATE) {
|
if (response.statusCode === STATUS_CODE.CREATE) {
|
||||||
@@ -196,7 +193,7 @@ const UserForm = ({
|
|||||||
const response = await sendRequest(
|
const response = await sendRequest(
|
||||||
USER_PATH + `/${user!.id}`,
|
USER_PATH + `/${user!.id}`,
|
||||||
JSON.stringify(data),
|
JSON.stringify(data),
|
||||||
'PUT',
|
'PUT'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == STATUS_CODE.OK) {
|
if (response.statusCode == STATUS_CODE.OK) {
|
||||||
@@ -226,7 +223,7 @@ const UserForm = ({
|
|||||||
const {
|
const {
|
||||||
values,
|
values,
|
||||||
errors,
|
errors,
|
||||||
dirty,
|
valid,
|
||||||
handleOnChange,
|
handleOnChange,
|
||||||
handleOnSubmit,
|
handleOnSubmit,
|
||||||
disable } =
|
disable } =
|
||||||
@@ -266,7 +263,7 @@ const UserForm = ({
|
|||||||
label="Full Name"
|
label="Full Name"
|
||||||
error={
|
error={
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
errors.name && dirty.name
|
errors.name && valid.name
|
||||||
? (errors.name as string)
|
? (errors.name as string)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
@@ -282,7 +279,7 @@ const UserForm = ({
|
|||||||
<FormRow
|
<FormRow
|
||||||
label="Identified Code"
|
label="Identified Code"
|
||||||
error={
|
error={
|
||||||
errors.identifiedCode && dirty.identifiedCode
|
errors.identifiedCode && valid.identifiedCode
|
||||||
? (errors.identifiedCode as string)
|
? (errors.identifiedCode as string)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
@@ -299,7 +296,7 @@ const UserForm = ({
|
|||||||
label="Phone"
|
label="Phone"
|
||||||
error={
|
error={
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
errors.phone && dirty.phone
|
errors.phone && valid.phone
|
||||||
? (errors.phone as string)
|
? (errors.phone as string)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
@@ -316,17 +313,11 @@ const UserForm = ({
|
|||||||
label="Room"
|
label="Room"
|
||||||
error={
|
error={
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
errors.roomId && dirty.roomId
|
errors.roomId && valid.roomId
|
||||||
? (errors.roomId as string)
|
? (errors.roomId as string)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{/* <Input
|
|
||||||
type="text"
|
|
||||||
name="roomId"
|
|
||||||
value={roomId as string}
|
|
||||||
onChange={handleOnChange}
|
|
||||||
/> */}
|
|
||||||
<Select
|
<Select
|
||||||
name="roomId"
|
name="roomId"
|
||||||
value={roomId as string}
|
value={roomId as string}
|
||||||
@@ -339,7 +330,7 @@ const UserForm = ({
|
|||||||
label="Address"
|
label="Address"
|
||||||
error={
|
error={
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
errors.address && dirty.address
|
errors.address && valid.address
|
||||||
? (errors.address as string)
|
? (errors.address as string)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import toast from 'react-hot-toast';
|
|||||||
import { HiSquare2Stack } from 'react-icons/hi2';
|
import { HiSquare2Stack } from 'react-icons/hi2';
|
||||||
import { HiTrash } from 'react-icons/hi';
|
import { HiTrash } from 'react-icons/hi';
|
||||||
import { StyledOperationTable } from './styled';
|
import { StyledOperationTable } from './styled';
|
||||||
import Menus from '../../components/Menus/Menus';
|
import Menus from '../../components/Menus';
|
||||||
import Table from '../../components/Table';
|
import Table from '../../components/Table';
|
||||||
import Direction from '../../commons/styles/Direction';
|
import Direction from '../../commons/styles/Direction';
|
||||||
import Message from '../../components/Message';
|
import Message from '../../components/Message';
|
||||||
|
|||||||
Reference in New Issue
Block a user