Fix comments

This commit is contained in:
2023-11-08 17:46:26 +07:00
parent a99d81a411
commit 8fce206f7f
35 changed files with 471 additions and 342 deletions
+2 -1
View File
@@ -49,6 +49,7 @@
"husky": "^8.0.0", "husky": "^8.0.0",
"lint-staged": "^15.0.1", "lint-staged": "^15.0.1",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"vite": "^4.4.5" "vite": "^4.4.5",
"vite-tsconfig-paths": "^4.2.1"
} }
} }
+37
View File
@@ -70,6 +70,9 @@ devDependencies:
vite: vite:
specifier: ^4.4.5 specifier: ^4.4.5
version: 4.4.11 version: 4.4.11
vite-tsconfig-paths:
specifier: ^4.2.1
version: 4.2.1(typescript@5.2.2)(vite@4.4.11)
packages: packages:
@@ -1406,6 +1409,10 @@ packages:
slash: 3.0.0 slash: 3.0.0
dev: true dev: true
/globrex@0.1.2:
resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
dev: true
/goober@2.1.13(csstype@3.1.2): /goober@2.1.13(csstype@3.1.2):
resolution: {integrity: sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==} resolution: {integrity: sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==}
peerDependencies: peerDependencies:
@@ -2088,6 +2095,19 @@ packages:
typescript: 5.2.2 typescript: 5.2.2
dev: true dev: true
/tsconfck@2.1.2(typescript@5.2.2):
resolution: {integrity: sha512-ghqN1b0puy3MhhviwO2kGF8SeMDNhEbnKxjK7h6+fvY9JAxqvXi8y5NAHSQv687OVboS2uZIByzGd45/YxrRHg==}
engines: {node: ^14.13.1 || ^16 || >=18}
hasBin: true
peerDependencies:
typescript: ^4.3.5 || ^5.0.0
peerDependenciesMeta:
typescript:
optional: true
dependencies:
typescript: 5.2.2
dev: true
/tslib@2.6.2: /tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
dev: false dev: false
@@ -2132,6 +2152,23 @@ packages:
punycode: 2.3.0 punycode: 2.3.0
dev: true dev: true
/vite-tsconfig-paths@4.2.1(typescript@5.2.2)(vite@4.4.11):
resolution: {integrity: sha512-GNUI6ZgPqT3oervkvzU+qtys83+75N/OuDaQl7HmOqFTb0pjZsuARrRipsyJhJ3enqV8beI1xhGbToR4o78nSQ==}
peerDependencies:
vite: '*'
peerDependenciesMeta:
vite:
optional: true
dependencies:
debug: 4.3.4
globrex: 0.1.2
tsconfck: 2.1.2(typescript@5.2.2)
vite: 4.4.11
transitivePeerDependencies:
- supports-color
- typescript
dev: true
/vite@4.4.11: /vite@4.4.11:
resolution: {integrity: sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==} resolution: {integrity: sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
@@ -1,22 +0,0 @@
import styled from 'styled-components';
const ButtonIcon = styled.button`
background: none;
padding: 8px;
transition: all 0.2s;
border: none;
border-radius: var(--radius-md);
&:hover {
background-color: var(--hover-background-color);
}
& svg {
width: 25px;
height: 25px;
}
`;
export default ButtonIcon;
@@ -1,8 +1,8 @@
import { Outlet } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
// Components // Components
import Header from '../Header'; import Header from '@component/Header';
import Sidebar from '../Sidebar'; import Sidebar from '@component/Sidebar';
// Styled // Styled
import { Main, StyledAppLayout } from './styled'; import { Main, StyledAppLayout } from './styled';
@@ -0,0 +1,131 @@
import { COLOR, SIZE } from '@constant/styles';
import { ReactNode } from 'react';
import styled, { css } from 'styled-components';
interface IStyledButtonIcon {
isHaveChildren: boolean;
style?: {
fontSize?: string;
backgroundColor?: string;
color?: string;
};
iconStyle?: {
color?: string;
size?: string;
};
}
const StyledButtonIcon = styled.button<IStyledButtonIcon>`
background: none;
padding: 10px;
transition: all 0.2s;
border: none;
${(props) =>
props.style?.fontSize &&
css`
font-size: ${props.style.fontSize};
`}
${(props) =>
props.style?.fontSize &&
css`
font-size: ${props.style.fontSize};
`}
${(props) =>
props.color &&
css`
font-size: ${props.color};
`}
${(props) =>
props.isHaveChildren
? css`
display: flex;
align-items: center;
gap: 10px;
text-align: left;
width: 100%;
`
: css`
border-radius: var(--radius-md);
`}
&:hover {
background-color: var(--hover-background-color);
}
& svg {
${(props) =>
props.iconStyle?.size &&
css`
width: ${props.iconStyle.size};
height: ${props.iconStyle.size};
`}
${(props) =>
props.iconStyle?.color &&
css`
color: ${props.iconStyle.color};
`}
transition: all 0.3s;
}
`;
StyledButtonIcon.defaultProps = {
style: {
fontSize: SIZE.DEFAULT,
backgroundColor: COLOR.DEFAULT,
color: COLOR.BLACK,
},
iconStyle: {
color: COLOR.BLACK,
size: SIZE.DEFAULT,
},
};
interface IButtonIcon {
icon: ReactNode;
children?: ReactNode;
style?: {
fontSize?: string;
backgroundColor?: string;
color?: string;
};
iconStyle?: {
color?: string;
size?: string;
};
onClick?: () => void;
}
const ButtonIcon = ({
icon,
children,
style,
iconStyle,
onClick,
}: IButtonIcon) => {
const isHaveChildren = Boolean(children);
return (
<StyledButtonIcon
isHaveChildren={isHaveChildren}
style={{
fontSize: style?.fontSize,
backgroundColor: style?.backgroundColor,
color: style?.color,
}}
iconStyle={iconStyle}
onClick={onClick}
>
{icon} <span>{children}</span>
</StyledButtonIcon>
);
};
export default ButtonIcon;
@@ -1,7 +1,7 @@
// Components // Components
import { IoPersonCircleOutline } from 'react-icons/io5'; import { IoPersonCircleOutline } from 'react-icons/io5';
import { HiOutlineLogout } from 'react-icons/hi'; import { HiOutlineLogout } from 'react-icons/hi';
import ButtonIcon from '../../commons/styles/ButtonIcon'; import ButtonIcon from '@component/ButtonIcon/ButtonIcon';
// Styled // Styled
import { StyledHeaderMenu } from './styled'; import { StyledHeaderMenu } from './styled';
@@ -10,14 +10,18 @@ const HeaderMenu = () => {
return ( return (
<StyledHeaderMenu> <StyledHeaderMenu>
<li> <li>
<ButtonIcon aria-label="Profile"> <ButtonIcon
<IoPersonCircleOutline /> aria-label="Profile"
</ButtonIcon> icon={<IoPersonCircleOutline />}
iconStyle={{ size: '23px' }}
/>
</li> </li>
<li> <li>
<ButtonIcon aria-label="Logout"> <ButtonIcon
<HiOutlineLogout /> aria-label="Logout"
</ButtonIcon> icon={<HiOutlineLogout />}
iconStyle={{size: '23px' }}
/>
</li> </li>
</StyledHeaderMenu> </StyledHeaderMenu>
); );
@@ -4,16 +4,18 @@ import { MouseEvent, ReactNode, useContext, useState } from 'react';
import { HiEllipsisVertical } from 'react-icons/hi2'; import { HiEllipsisVertical } from 'react-icons/hi2';
// Hooks // Hooks
import { useOutsideClick } from '../../hooks/useOutsideClick'; import { useOutsideClick } from '@hook/useOutsideClick';
// Styled // Styled
import { StyledMenu, StyledButton, StyledList, StyledToggle } from './styled'; import { StyledMenu, StyledList, StyledToggle } from './styled';
// Contexts // Contexts
import MenusContext from '../../contexts/MenuContext'; import MenusContext from '@context/MenuContext';
// Types // Types
import { Nullable } from '../../types/common'; import { Nullable } from '@type/common';
import ButtonIcon from '@component/ButtonIcon/ButtonIcon';
import { COLOR } from '@constant/styles';
interface IButton { interface IButton {
children?: string; children?: string;
@@ -74,9 +76,14 @@ const Button = ({ children, icon, onClick }: IButton): ReactNode => {
return ( return (
<li> <li>
<StyledButton onClick={handleClick}> <ButtonIcon
{icon} <span>{children}</span> icon={icon}
</StyledButton> onClick={handleClick}
iconStyle={{ color: COLOR.PRIMARY, size: '19px' }}
style={{ fontSize: '16px' }}
>
{children}
</ButtonIcon>
</li> </li>
); );
}; };
@@ -8,10 +8,10 @@ import {
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
// Hooks // Hooks
import { useOutsideClick } from '../../hooks/useOutsideClick'; import { useOutsideClick } from '@hook/useOutsideClick';
// Contexts // Contexts
import { ModalContext } from '../../contexts/ModalContext'; import { ModalContext } from '@context/ModalContext';
// Styled // Styled
import { Overlay, StyledModal, StyledModalContent, TitleModal } from './styled'; import { Overlay, StyledModal, StyledModalContent, TitleModal } from './styled';
@@ -34,14 +34,14 @@ const Modal = ({ children }: IModal) => {
}; };
interface IOpen { interface IOpen {
children: ReactElement; renderChildren: (onCloseModal: () => void) => ReactNode;
modalName: string; modalName: string;
} }
const Open = ({ children, modalName }: IOpen) => { const Open = ({ renderChildren, modalName }: IOpen) => {
const { open } = useContext(ModalContext); const { open } = useContext(ModalContext);
return cloneElement(children, { onClick: () => open!(modalName) }); return renderChildren(() => open!(modalName));
}; };
interface IWindow { interface IWindow {
@@ -54,9 +54,6 @@ const Window = ({ children, name, title }: IWindow) => {
const { openName, close } = useContext(ModalContext); const { openName, close } = useContext(ModalContext);
const ref = useOutsideClick<HTMLDivElement>(close!); const ref = useOutsideClick<HTMLDivElement>(close!);
console.log('name: ', name);
console.log('openName: ', openName);
if (name !== openName) return null; if (name !== openName) return null;
return createPortal( return createPortal(
@@ -7,7 +7,7 @@ import { BsHouseDoorFill } from 'react-icons/bs';
import { StyledNav, StyledNavLink } from './styled'; import { StyledNav, StyledNavLink } from './styled';
// Constants // Constants
import { DASHBOARD, ROOM, USER } from '../../constants/path'; import { DASHBOARD, ROOM, USER } from '@constant/path';
const Nav = () => { const Nav = () => {
return ( return (
@@ -1,4 +1,3 @@
import { memo } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
// Styled // Styled
@@ -11,7 +10,7 @@ interface IOrderProps {
}[]; }[];
} }
const OrderBy = memo(({ options }: IOrderProps) => { const OrderBy = ({ options }: IOrderProps) => {
const field = 'orderBy'; const field = 'orderBy';
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const currentOrder = searchParams.get(field) || options[0].value; const currentOrder = searchParams.get(field) || options[0].value;
@@ -35,6 +34,6 @@ const OrderBy = memo(({ options }: IOrderProps) => {
))} ))}
</StyledOrder> </StyledOrder>
); );
}); };
export default OrderBy; export default OrderBy;
@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
import { StyledSearch } from './styled'; import { StyledSearch } from './styled';
// Hooks // Hooks
import { useDebounce } from '../../hooks/useDebounce'; import { useDebounce } from '@hook/useDebounce';
interface ISearch { interface ISearch {
setPlaceHolder: string; setPlaceHolder: string;
@@ -1,10 +1,9 @@
import { import {
FieldValues, FieldValues,
RegisterOptions, RegisterOptions,
UseFormRegister,
useFormContext, useFormContext,
} from 'react-hook-form'; } from 'react-hook-form';
import { ChangeEventHandler, ReactNode } from 'react'; import { ChangeEventHandler } from 'react';
// Styled // Styled
import { StyledSelect } from './styled'; import { StyledSelect } from './styled';
@@ -22,46 +21,6 @@ interface ISelect {
ariaLabel: string; ariaLabel: string;
} }
interface IRender extends ISelect {
register: UseFormRegister<FieldValues>;
children: ReactNode[];
}
const RenderSelect = ({
options,
id,
value,
optionsConfigForm,
onChange,
register,
children,
ariaLabel,
}: IRender) => {
if (options && !register) {
return (
<StyledSelect
aria-label={ariaLabel}
id={id}
value={value}
onChange={onChange}
>
{children}
</StyledSelect>
);
}
return (
<StyledSelect
aria-label={ariaLabel}
id={id}
value={value}
{...register(id!, optionsConfigForm)}
>
{children}
</StyledSelect>
);
};
const Select = ({ const Select = ({
options, options,
id, id,
@@ -75,21 +34,20 @@ const Select = ({
if (!options) return; if (!options) return;
return ( return (
<RenderSelect <StyledSelect
register={register} aria-label={ariaLabel}
options={options}
optionsConfigForm={optionsConfigForm}
id={id} id={id}
value={value} value={value}
onChange={onChange} {...(register
ariaLabel={ariaLabel} ? { ...register(id!, optionsConfigForm) }
: { onChange: onChange })}
> >
{options.map((option) => ( {options.map((option) => (
<option value={option.value} key={option.value}> <option value={option.value} key={option.value}>
{option.label} {option.label}
</option> </option>
))} ))}
</RenderSelect> </StyledSelect>
); );
}; };
@@ -1,5 +1,5 @@
// Components // Components
import Nav from '../Nav'; import Nav from '@component/Nav';
// Styled // Styled
import { Heading, StyledSidebar } from './styled'; import { Heading, StyledSidebar } from './styled';
@@ -1,8 +1,8 @@
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { ChangeEvent, memo } from 'react'; import { ChangeEvent } from 'react';
// Components // Components
import Select from '../Select'; import Select from '@component/Select';
interface ISortByProps { interface ISortByProps {
options: { options: {
@@ -11,7 +11,7 @@ interface ISortByProps {
}[]; }[];
} }
const SortBy = memo(({ options }: ISortByProps) => { const SortBy = ({ options }: ISortByProps) => {
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const sortBy = searchParams.get('sortBy') || ''; const sortBy = searchParams.get('sortBy') || '';
const handleChange = (event: ChangeEvent<HTMLSelectElement>) => { const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
@@ -27,6 +27,6 @@ const SortBy = memo(({ options }: ISortByProps) => {
ariaLabel="Sort" ariaLabel="Sort"
/> />
); );
}); };
export default SortBy; export default SortBy;
@@ -4,13 +4,17 @@ import { ReactNode, useContext } from 'react';
import { StyledBody, StyledHeader, StyledRow, StyledTable } from './styled'; import { StyledBody, StyledHeader, StyledRow, StyledTable } from './styled';
// Contexts // Contexts
import TableContext from '../../contexts/TableContext'; import TableContext from '@context/TableContext';
export interface ITable { export interface ITable {
columns?: string; columns?: string;
children: ReactNode; children: ReactNode;
} }
export interface IHeader {
headerColumn: string[];
}
interface ITableBody<T> { interface ITableBody<T> {
data?: T[]; data?: T[];
render?: CallbackMapFunc<T>; render?: CallbackMapFunc<T>;
@@ -26,10 +30,12 @@ const Table = ({ columns, children }: ITable) => {
); );
}; };
const Header = ({ children }: ITable) => { const Header = ({ headerColumn }: IHeader) => {
const { columns } = useContext(TableContext); const { columns } = useContext(TableContext);
return <StyledHeader columns={columns}>{children}</StyledHeader>; return <StyledHeader columns={columns}>
{headerColumn.map((item) => <div key={item}>{item}</div>)}
</StyledHeader>;
}; };
const Body = <T,>({ data, render }: ITableBody<T>) => { const Body = <T,>({ data, render }: ITableBody<T>) => {
@@ -51,9 +51,6 @@ const ROOM_PAGE = {
], ],
}; };
const VALUE = 'value';
const ERROR = 'error';
const INIT_VALUE_USER_FORM = { const INIT_VALUE_USER_FORM = {
name: '', name: '',
id: 0, id: 0,
@@ -68,11 +65,23 @@ const INIT_VALUE_ROOM_FORM = {
discount: 0, discount: 0,
}; };
const FORM = {
EDIT: 'edit',
USER: 'user-form',
ROOM: 'room-form',
};
const REGEX = {
PHONE: /(84|0[3|5|7|8|9])+([0-9]{8})\b/g,
NAME: /^[a-zA-Z]{2,}(?: [a-zA-Z]+){0,2}$/gm,
NUMBER: /^[0-9]*$/,
};
export { export {
USER_PAGE, USER_PAGE,
ROOM_PAGE, ROOM_PAGE,
VALUE, FORM,
ERROR, REGEX,
ORDERBY_OPTIONS, ORDERBY_OPTIONS,
INIT_VALUE_USER_FORM, INIT_VALUE_USER_FORM,
INIT_VALUE_ROOM_FORM, INIT_VALUE_ROOM_FORM,
+11
View File
@@ -0,0 +1,11 @@
const COLOR = {
PRIMARY: '#0010ba',
BLACK: '#000',
DEFAULT: 'transparent',
};
const SIZE = {
DEFAULT: '14px',
};
export { COLOR, SIZE };
+2 -2
View File
@@ -1,8 +1,8 @@
// Constants // Constants
import { BASE_URL } from '../constants/path'; import { BASE_URL } from '@constant/path';
// Types // Types
import { IResponse } from '../types/responses'; import { IResponse } from '@type/responses';
type TMethodRequest = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; type TMethodRequest = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
+7 -32
View File
@@ -1,20 +1,12 @@
/** /**
* Check value is valid name or not * Compare the regex with value
* @param value Values need to be checked * @param regex The regex to compare
* @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 * @param value Value need to be checked
* @returns A boolean indicating whether or not the argument has valid * @returns A boolean indicating whether of not the argument has valid
*/ */
const isValidNumber = (value: string): boolean => { const isValidRegex = (regex: RegExp, value: string): boolean => {
return /^[0-9]*$/.test(value); return regex.test(value);
}; }
/** /**
* Check value is valid discount or not * Check value is valid discount or not
@@ -25,14 +17,6 @@ const isValidDiscount = (value: number): boolean => {
return value >= 0 && value <= 100; return value >= 0 && value <= 100;
}; };
/**
* 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 * Check value is valid string or not
@@ -43,12 +27,6 @@ const isValidString = (value: string): boolean => {
return value.trim().length >= 5; return value.trim().length >= 5;
}; };
/**
* A method skip check value
* @returns Return true
*/
const skipCheck = () => true;
/** /**
* Check object is empty or not * Check object is empty or not
* @param obj Object need to be check * @param obj Object need to be check
@@ -59,11 +37,8 @@ const isEmptyObj = (obj: object) => {
}; };
export { export {
isValidName, isValidRegex,
isValidNumber,
isValidPhoneNumber,
isValidString, isValidString,
skipCheck,
isValidDiscount, isValidDiscount,
isEmptyObj, isEmptyObj,
}; };
+4 -4
View File
@@ -1,14 +1,14 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
// Constants // Constants
import { BASE_URL } from '../constants/path'; import { BASE_URL } from '@constant/path';
import { DEFAULT_ORDER_BY, DEFAULT_SORT_BY } from '../constants/config'; import { DEFAULT_ORDER_BY, DEFAULT_SORT_BY } from '@constant/config';
// Helpers // Helpers
import { searchQuery } from '../helpers/helper'; import { searchQuery } from '@helper/helper';
// Types // Types
import { Nullable } from '../types/common'; import { Nullable } from '@type/common';
/** /**
* The function use to fetch data on server API. * The function use to fetch data on server API.
+1 -1
View File
@@ -1,7 +1,7 @@
import { ForwardedRef, useEffect, useRef } from 'react'; import { ForwardedRef, useEffect, useRef } from 'react';
// Types // Types
import { Nullable } from '../types/common'; import { Nullable } from '@type/common';
const useForwardRef = <T>( const useForwardRef = <T>(
ref: ForwardedRef<T>, ref: ForwardedRef<T>,
@@ -1,7 +1,7 @@
import { MutableRefObject, useEffect, useRef } from 'react'; import { MutableRefObject, useEffect, useRef } from 'react';
// Types // Types
import { Nullable } from '../types/common'; import { Nullable } from '@type/common';
/** /**
* Custom hook to call handler when click outside element * Custom hook to call handler when click outside element
+16 -19
View File
@@ -2,25 +2,26 @@ import { useSearchParams } from 'react-router-dom';
import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { Dispatch, SetStateAction, useEffect, useState } from 'react';
// Components // Components
import { StyledOperationTable } from './styled'; import Menus from '@component/Menus';
import Menus from '../../components/Menus'; import Table from '@component/Table';
import Table from '../../components/Table'; import Message from '@component/Message';
import Direction from '../../commons/styles/Direction'; import Search from '@component/Search';
import Message from '../../components/Message'; import SortBy from '@component/SortBy';
import Search from '../../components/Search'; import OrderBy from '@component/OrderBy';
import SortBy from '../../components/SortBy'; import RoomRow from './RoomRow';
import OrderBy from '../../components/OrderBy';
// Types // Types
import { IRoom } from '../../types/rooms'; import { IRoom } from '@type/rooms';
// Constants // Constants
import { useFetch } from '../../hooks/useFetch'; import { useFetch } from '@hook/useFetch';
import { ORDERBY_OPTIONS, ROOM_PAGE } from '../../constants/variables'; import { ORDERBY_OPTIONS, ROOM_PAGE } from '@constant/commons';
// Styled // Styled
import Spinner from '../../commons/styles/Spinner'; import { StyledOperationTable } from './styled';
import RoomRow from './RoomRow'; import Direction from '@commonStyle/Direction';
import Spinner from '@commonStyle/Spinner';
interface IRoomTable { interface IRoomTable {
reload: boolean; reload: boolean;
@@ -28,6 +29,7 @@ interface IRoomTable {
} }
const RoomTable = ({ reload, setReload }: IRoomTable) => { const RoomTable = ({ reload, setReload }: IRoomTable) => {
const columnName = ['Id', 'Name', 'Price', 'Status'];
const [nameSearch, setNameSearch] = useState(''); const [nameSearch, setNameSearch] = useState('');
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const [rooms, setRooms] = useState<IRoom[]>([]); const [rooms, setRooms] = useState<IRoom[]>([]);
@@ -77,12 +79,7 @@ const RoomTable = ({ reload, setReload }: IRoomTable) => {
{rooms.length ? ( {rooms.length ? (
<Menus> <Menus>
<Table columns="10% 40% 20% 20% 5%"> <Table columns="10% 40% 20% 20% 5%">
<Table.Header> <Table.Header headerColumn={columnName}/>
<div>Id</div>
<div>Name</div>
<div>Price</div>
<div>Status</div>
</Table.Header>
<Table.Body<IRoom> <Table.Body<IRoom>
data={rooms} data={rooms}
render={(room: IRoom) => ( render={(room: IRoom) => (
+34 -36
View File
@@ -6,36 +6,35 @@ import styled from 'styled-components';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
// Styled // Styled
import Button from '../../commons/styles/Button.ts'; import Button from '@commonStyle/Button.ts';
import Input from '../../commons/styles/Input.ts'; import Input from '@commonStyle/Input.ts';
// Types // Types
import { Nullable } from '../../types/common.ts'; import { Nullable } from '@type/common.ts';
import { IRoom } from '../../types/rooms.ts'; import { IRoom } from '@type/rooms.ts';
// Constants // Constants
import { import { ADD_SUCCESS, EDIT_SUCCESS } from '@constant/messages.ts';
ADD_SUCCESS,
EDIT_SUCCESS,
} from '../../constants/messages.ts';
import { import {
INVALID_DISCOUNT, INVALID_DISCOUNT,
INVALID_FIELD, INVALID_FIELD,
REQUIRED_FIELD_ERROR, REQUIRED_FIELD_ERROR,
} from '../../constants/formValidateMessage.ts'; } from '@constant/formValidateMessage.ts';
import { INIT_VALUE_ROOM_FORM, REGEX } from '../../constants/commons.ts';
// Helpers // Helpers
import { import {
isValidDiscount, isValidDiscount,
isValidNumber, isValidRegex,
isValidString, isValidString,
} from '../../helpers/validators.ts'; } from '@helper/validators.ts';
// Components // Components
import FormRow from '../../components/LabelControl/index.tsx'; import FormRow from '@component/LabelControl/index.tsx';
import Form from '../../components/Form/index.tsx'; import Form from '@component/Form/index.tsx';
import { addRoom, updateRoom } from '../../services/roomServices.ts';
import { INIT_VALUE_ROOM_FORM } from '../../constants/variables.ts'; // Services
import { addRoom, updateRoom } from '@service/roomServices.ts';
const FormBtn = styled(Button)` const FormBtn = styled(Button)`
width: 100%; width: 100%;
@@ -82,30 +81,25 @@ const RoomForm = ({
// Calculate final price // Calculate final price
room.finalPrice = room.price - (room.price * room.discount) / 100; room.finalPrice = room.price - (room.price * room.discount) / 100;
try { if (!roomEdit) {
if (!roomEdit) { // Add request
// Add request const response = await addRoom(room);
const response = await addRoom(room);
if(response) {
toast.success(ADD_SUCCESS);
}
} else {
// Edit request
const response = await updateRoom(room);
if (response) { if (response) {
toast.success(EDIT_SUCCESS); toast.success(ADD_SUCCESS);
}
} }
// Reload table data } else {
setReload!(!reload); // Edit request
} catch (error: unknown) { const response = await updateRoom(room);
if (error instanceof Error) {
toast.error(error.message); if (response) {
toast.success(EDIT_SUCCESS);
} }
} }
// Reload table data
setReload!(!reload);
reset(); reset();
onCloseModal!(); onCloseModal!();
}; };
@@ -136,7 +130,7 @@ const RoomForm = ({
required: REQUIRED_FIELD_ERROR, required: REQUIRED_FIELD_ERROR,
validate: { validate: {
checkIdentifiedCode: (v) => checkIdentifiedCode: (v) =>
isValidNumber(v.toString()) || INVALID_FIELD, isValidRegex(REGEX.NUMBER, v.toString()) || INVALID_FIELD,
}, },
onChange: () => trigger('price'), onChange: () => trigger('price'),
})} })}
@@ -159,7 +153,11 @@ const RoomForm = ({
</FormRow> </FormRow>
<Form.Action> <Form.Action>
<FormBtn type="submit" name="submit" disabled={!isDirty || !isValid || isSubmitting}> <FormBtn
type="submit"
name="submit"
disabled={!isDirty || !isValid || isSubmitting}
>
{ {
!roomEdit !roomEdit
? 'Add' ? 'Add'
+21 -14
View File
@@ -2,22 +2,22 @@ import { Dispatch, SetStateAction } from 'react';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
// Constants // Constants
import { ROOM_PATH } from '../../constants/path'; import { ROOM_PATH } from '@constant/path';
import { STATUS_CODE } from '../../constants/responseStatus'; import { STATUS_CODE } from '@constant/responseStatus';
import { CONFIRM_DELETE, DELETE_SUCCESS } from '../../constants/messages'; import { CONFIRM_DELETE, DELETE_SUCCESS } from '@constant/messages';
// Helpers // Helpers
import { sendRequest } from '../../helpers/sendRequest'; import { sendRequest } from '@helper/sendRequest';
import { formatCurrency } from '../../helpers/helper'; import { formatCurrency } from '@helper/helper';
import Modal from '../../components/Modal';
import RoomForm from './RoomForm';
// Types // Types
import { IRoom } from '../../types/rooms'; import { IRoom } from '@type/rooms';
// Components // Components
import Table from '../../components/Table'; import RoomForm from './RoomForm';
import Menus from '../../components/Menus'; import Modal from '@component/Modal';
import Table from '@component/Table';
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';
@@ -43,7 +43,9 @@ const RoomRow = ({ room, reload, setReload }: IRoomRow) => {
const { id, name, finalPrice, status } = room; const { id, name, finalPrice, status } = room;
const statusText = status ? 'Unavailable' : 'Available'; const statusText = status
? 'Unavailable'
: 'Available';
return ( return (
<Table.Row> <Table.Row>
@@ -58,9 +60,14 @@ const RoomRow = ({ room, reload, setReload }: IRoomRow) => {
<Menus.Toggle id={id.toString()} /> <Menus.Toggle id={id.toString()} />
<Menus.List id={id.toString()}> <Menus.List id={id.toString()}>
<Modal.Open modalName="edit"> <Modal.Open
<Menus.Button icon={<RiEditBoxFill />}>Edit</Menus.Button> modalName="edit"
</Modal.Open> renderChildren={(onCloseModal) => (
<Menus.Button onClick={onCloseModal} icon={<RiEditBoxFill />}>
Edit
</Menus.Button>
)}
/>
<Menus.Button <Menus.Button
icon={<HiTrash />} icon={<HiTrash />}
+11 -11
View File
@@ -2,15 +2,15 @@ import { useState } from 'react';
// Components // Components
import RoomTable from './RomTable'; import RoomTable from './RomTable';
import RoomForm from './RoomForm';
// Styled // Styled
import { StyledRoom, Title } from './styled'; import { StyledRoom, Title } from './styled';
import Direction from '../../commons/styles/Direction'; import Direction from '@commonStyle/Direction.ts';
import Button from '../../commons/styles/Button'; import Button from '@commonStyle/Button';
// Types // Types
import Modal from '../../components/Modal'; import Modal from '@component/Modal';
import RoomForm from './RoomForm';
const Room = () => { const Room = () => {
const [reload, setReload] = useState(true); const [reload, setReload] = useState(true);
@@ -22,19 +22,19 @@ const Room = () => {
<Title>List Room</Title> <Title>List Room</Title>
<Modal> <Modal>
<Modal.Open modalName="room-form"> <Modal.Open
<Button>Add room</Button> modalName="room-form"
</Modal.Open> renderChildren={(onCloseModal) => (
<Button onClick={onCloseModal}>Add room</Button>
)}
/>
<Modal.Window name="room-form" title="Add form"> <Modal.Window name="room-form" title="Add form">
<RoomForm setReload={setReload} reload={reload} /> <RoomForm setReload={setReload} reload={reload} />
</Modal.Window> </Modal.Window>
</Modal> </Modal>
</Direction> </Direction>
<RoomTable <RoomTable reload={reload} setReload={setReload} />
reload={reload}
setReload={setReload}
/>
</StyledRoom> </StyledRoom>
</> </>
); );
+36 -45
View File
@@ -11,41 +11,38 @@ import toast from 'react-hot-toast';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
// Styled // Styled
import Input from '../../commons/styles/Input.ts'; import Input from '@commonStyle/Input.ts';
// Components // Components
import Form from '../../components/Form/index.tsx'; import Form from '@component/Form/index.tsx';
import FormRow from '../../components/LabelControl/index.tsx'; import FormRow from '@component/LabelControl/index.tsx';
import Select, { ISelectOptions } from '../../components/Select/index.tsx'; import Select, { ISelectOptions } from '@component/Select/index.tsx';
// Helpers // Helpers
import { import {
isEmptyObj, isEmptyObj, isValidRegex,
isValidName, } from '@helper/validators.ts';
isValidNumber,
isValidPhoneNumber,
} from '../../helpers/validators.ts';
// Constants // Constants
import { ADD_SUCCESS, EDIT_SUCCESS } from '../../constants/messages.ts'; 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 '../../constants/formValidateMessage.ts'; } from '@constant/formValidateMessage.ts';
import { INIT_VALUE_USER_FORM } from '../../constants/variables.ts'; import { INIT_VALUE_USER_FORM, REGEX } from '@constant/commons.ts';
// Styled // Styled
import { FormBtn } from './styled.ts'; import { FormBtn } from './styled.ts';
// Services // Services
import { getAllRoom, updateRoomStatus } from '../../services/roomServices.ts'; import { getAllRoom, updateRoomStatus } from '@service/roomServices.ts';
import { createUser, updateUser } from '@service/userServices.ts';
// Types // Types
import { Nullable } from '../../types/common.ts'; import { Nullable } from '@type/common.ts';
import { IUser } from '../../types/users.ts'; import { IUser } from '@type/users.ts';
import { IRoom } from '../../types/rooms.ts'; import { IRoom } from '@type/rooms.ts';
import { createUser, updateUser } from '../../services/userServices.ts';
interface IUserFormProp { interface IUserFormProp {
onCloseModal?: () => void; onCloseModal?: () => void;
@@ -109,35 +106,29 @@ const UserForm = ({ onCloseModal, reload, setReload, user }: IUserFormProp) => {
// Submit form // Submit form
const onSubmit = useCallback( const onSubmit = useCallback(
async (newUser: IUser) => { async (newUser: IUser) => {
try { if (!user) {
if (!user) { // Add request
// Add request const response = await createUser(newUser);
const response = await createUser(newUser);
if (response) { if (response) {
toast.success(ADD_SUCCESS); toast.success(ADD_SUCCESS);
}
// Update room status
updateRoomStatus(newUser.roomId, true);
} else {
// Edit request
const response = await updateUser(newUser);
if (response) {
toast.success(EDIT_SUCCESS);
}
// Update room status
updateRoomStatus(user!.roomId, true, newUser.roomId);
} }
// Reload table data
setReload(!reload); // Update room status
} catch (error: unknown) { updateRoomStatus(newUser.roomId, true);
if (error instanceof Error) { } else {
toast.error(error.message); // Edit request
const response = await updateUser(newUser);
if (response) {
toast.success(EDIT_SUCCESS);
} }
// Update room status
updateRoomStatus(user!.roomId, true, newUser.roomId);
} }
// Reload table data
setReload(!reload);
reset(); reset();
onCloseModal!(); onCloseModal!();
@@ -169,7 +160,7 @@ const UserForm = ({ onCloseModal, reload, setReload, user }: IUserFormProp) => {
{...register('name', { {...register('name', {
required: REQUIRED_FIELD_ERROR, required: REQUIRED_FIELD_ERROR,
validate: { validate: {
checkValidName: (value) => isValidName(value) || INVALID_FIELD, checkValidName: (value) => isValidRegex(REGEX.NAME, value) || INVALID_FIELD,
}, },
onChange: () => trigger('name'), onChange: () => trigger('name'),
})} })}
@@ -187,7 +178,7 @@ const UserForm = ({ onCloseModal, reload, setReload, user }: IUserFormProp) => {
required: REQUIRED_FIELD_ERROR, required: REQUIRED_FIELD_ERROR,
validate: { validate: {
checkIdentifiedCode: (v) => checkIdentifiedCode: (v) =>
isValidNumber(v.toString()) || INVALID_FIELD, isValidRegex(REGEX.NUMBER, v.toString()) || INVALID_FIELD,
}, },
onChange: () => trigger('identifiedCode'), onChange: () => trigger('identifiedCode'),
})} })}
@@ -201,7 +192,7 @@ const UserForm = ({ onCloseModal, reload, setReload, user }: IUserFormProp) => {
{...register('phone', { {...register('phone', {
required: REQUIRED_FIELD_ERROR, required: REQUIRED_FIELD_ERROR,
validate: { validate: {
checkPhoneNum: (v) => isValidPhoneNumber(v) || INVALID_PHONE, checkPhoneNum: (v) => isValidRegex(REGEX.PHONE, v) || INVALID_PHONE,
}, },
onChange: () => trigger('phone'), onChange: () => trigger('phone'),
})} })}
+18 -27
View File
@@ -2,31 +2,27 @@ import { useSearchParams } from 'react-router-dom';
import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { Dispatch, SetStateAction, useEffect, useState } from 'react';
// Components // Components
import Menus from '../../components/Menus'; import Menus from '@component/Menus';
import Table from '../../components/Table'; import Table from '@component/Table';
import Direction from '../../commons/styles/Direction'; import Message from '@component/Message';
import Message from '../../components/Message'; import Search from '@component/Search';
import Search from '../../components/Search'; import SortBy from '@component/SortBy';
import SortBy from '../../components/SortBy'; import OrderBy from '@component/OrderBy';
import OrderBy from '../../components/OrderBy';
// Types
import { IUser } from '../../types/users';
// Hooks
import { useFetch } from '../../hooks/useFetch';
// Constants
import { ORDERBY_OPTIONS, USER_PAGE } from '../../constants/variables';
// Styled
import { StyledOperationTable } from './styled';
import Spinner from '../../commons/styles/Spinner';
import UserRow from './UserRow'; import UserRow from './UserRow';
// Types
import { IUser } from '@type/users';
// Hooks
import { useFetch } from '@hook/useFetch';
// Constants
import { ORDERBY_OPTIONS, USER_PAGE } from '@constant/commons';
// Styled
import Direction from '@commonStyle/Direction';
import { StyledOperationTable } from './styled';
import Spinner from '@commonStyle/Spinner';
interface IUserTable { interface IUserTable {
reload: boolean; reload: boolean;
@@ -34,6 +30,7 @@ interface IUserTable {
} }
const UserTable = ({ reload, setReload }: IUserTable) => { const UserTable = ({ reload, setReload }: IUserTable) => {
const columnName = ['Id', 'Name', 'Identified Code', 'Phone', 'Room Id'];
const [users, setUsers] = useState<IUser[]>([]); const [users, setUsers] = useState<IUser[]>([]);
const [phoneSearch, setPhoneSearch] = useState(''); const [phoneSearch, setPhoneSearch] = useState('');
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
@@ -83,13 +80,7 @@ const UserTable = ({ reload, setReload }: IUserTable) => {
{users.length ? ( {users.length ? (
<Menus> <Menus>
<Table columns="10% 30% 20% 20% 10% 5%"> <Table columns="10% 30% 20% 20% 10% 5%">
<Table.Header> <Table.Header headerColumn={columnName} />
<div>Id</div>
<div>Name</div>
<div>Identified Code</div>
<div>Phone</div>
<div>Room Id</div>
</Table.Header>
<Table.Body<IUser> <Table.Body<IUser>
data={users} data={users}
render={(user: IUser) => ( render={(user: IUser) => (
+23 -13
View File
@@ -4,21 +4,21 @@ import { Dispatch, SetStateAction } from 'react';
// Components // Components
import { RiEditBoxFill } from 'react-icons/ri'; import { RiEditBoxFill } from 'react-icons/ri';
import { IoExit } from 'react-icons/io5'; import { IoExit } from 'react-icons/io5';
import Modal from '../../components/Modal'; import Modal from '@component/Modal';
import Table from '@component/Table';
import Menus from '@component/Menus';
import UserForm from './FormUser'; import UserForm from './FormUser';
import Table from '../../components/Table';
import Menus from '../../components/Menus';
// Constants // Constants
import { STATUS_CODE } from '../../constants/responseStatus'; import { STATUS_CODE } from '@constant/responseStatus';
import { CONFIRM_MESSAGE, DENIED_ACTION } from '../../constants/messages'; import { CONFIRM_MESSAGE, DENIED_ACTION } from '@constant/messages';
// Services // Services
import { updateRoomStatus } from '../../services/roomServices'; import { updateRoomStatus } from '@service/roomServices';
import { checkOutUser } from '../../services/userServices'; import { checkOutUser } from '@service/userServices';
// Types // Types
import { IUser } from '../../types/users'; import { IUser } from '@type/users';
interface IUserRow { interface IUserRow {
user: IUser; user: IUser;
@@ -56,17 +56,27 @@ const UserRow = ({ user, reload, setReload }: IUserRow) => {
<div>{name}</div> <div>{name}</div>
<div>{identifiedCode}</div> <div>{identifiedCode}</div>
<div>{phone}</div> <div>{phone}</div>
<div>{roomId ? roomId : 'None'}</div> <div>
{
roomId
? roomId
: 'None'
}
</div>
<div> <div>
<Modal> <Modal>
<Menus.Menu> <Menus.Menu>
<Menus.Toggle id={id.toString()} /> <Menus.Toggle id={id.toString()} />
<Menus.List id={id.toString()}> <Menus.List id={id.toString()}>
<Modal.Open modalName="edit"> <Modal.Open
<Menus.Button icon={<RiEditBoxFill />}>Edit</Menus.Button> modalName="edit"
</Modal.Open> renderChildren={(onCloseModal) => (
<Menus.Button onClick={onCloseModal} icon={<RiEditBoxFill />}>
Edit
</Menus.Button>
)}
/>
<Menus.Button <Menus.Button
icon={<IoExit />} icon={<IoExit />}
onClick={() => handleCheckOut(user)} onClick={() => handleCheckOut(user)}
+14 -7
View File
@@ -4,15 +4,19 @@ import { useState } from 'react';
import UserTable from './TableUser'; import UserTable from './TableUser';
// Styled // Styled
import Button from '../../commons/styles/Button'; import Button from '@commonStyle/Button';
import Direction from '../../commons/styles/Direction'; import Direction from '@commonStyle/Direction.ts';
import { StyledUser, Title } from './styled'; import { StyledUser, Title } from './styled';
// Types // Types
import Modal from '../../components/Modal'; import Modal from '@component/Modal';
import UserForm from './FormUser'; import UserForm from './FormUser';
// Constants
import { FORM } from '@constant/commons';
const User = () => { const User = () => {
const ADD_ROOM = 'Add room';
const [reload, setReload] = useState(true); const [reload, setReload] = useState(true);
return ( return (
@@ -22,10 +26,13 @@ const User = () => {
<Title>List User</Title> <Title>List User</Title>
<Modal> <Modal>
<Modal.Open modalName="user-form"> <Modal.Open
<Button>Add user</Button> modalName={FORM.USER}
</Modal.Open> renderChildren={(onCloseModal) => (
<Modal.Window name="user-form" title="Add form"> <Button onClick={onCloseModal}>Add user</Button>
)}
/>
<Modal.Window name={FORM.USER} title={ADD_ROOM}>
<UserForm setReload={setReload} reload={reload} /> <UserForm setReload={setReload} reload={reload} />
</Modal.Window> </Modal.Window>
</Modal> </Modal>
@@ -1,17 +1,17 @@
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
// Types // Types
import { Nullable } from '../types/common'; import { Nullable } from '@type/common';
import { IResponse } from '../types/responses'; import { IResponse } from '@type/responses';
import { IRoom } from '../types/rooms'; import { IRoom } from '@type/rooms';
// Helpers // Helpers
import { sendRequest } from '../helpers/sendRequest'; import { sendRequest } from '@helper/sendRequest';
import { errorMsg } from '../helpers/helper'; import { errorMsg } from '@helper/helper';
// Constants // Constants
import { STATUS_CODE, RESPONSE_MESSAGE } from '../constants/responseStatus'; import { STATUS_CODE, RESPONSE_MESSAGE } from '@constant/responseStatus';
import { ROOM_PATH } from '../constants/path'; import { ROOM_PATH } from '@constant/path';
/** /**
* Get all rooms from server * Get all rooms from server
@@ -1,17 +1,17 @@
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
// Constants // Constants
import { USER_PATH } from '../constants/path'; import { USER_PATH } from '@constant/path';
import { STATUS_CODE } from '../constants/responseStatus'; import { STATUS_CODE } from '@constant/responseStatus';
// Types // Types
import { Nullable } from '../types/common'; import { Nullable } from '@type/common';
import { IResponse } from '../types/responses'; import { IResponse } from '@type/responses';
import { IUser } from '../types/users'; import { IUser } from '@type/users';
// Helpers // Helpers
import { sendRequest } from '../helpers/sendRequest'; import { sendRequest } from '@helper/sendRequest';
import { errorMsg } from '../helpers/helper'; import { errorMsg } from '@helper/helper';
/** /**
* Create user to the server * Create user to the server
+15 -1
View File
@@ -5,6 +5,7 @@
"lib": ["ES2020", "DOM", "DOM.Iterable"], "lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
"types": ["node"],
/* Bundler mode */ /* Bundler mode */
"moduleResolution": "bundler", "moduleResolution": "bundler",
@@ -18,7 +19,20 @@
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true,
/* Alias */
"paths": {
"@service/*": ["./src/services/*"],
"@constant/*": ["./src/constants/*"],
"@hook/*": ["./src/hooks/*"],
"@helper/*": ["./src/helpers/*"],
"@context/*": ["./src/contexts/*"],
"@component/*": ["./src/components/*"],
"@commonStyle/*": ["./src/commons/styles/*"],
"@type/*": ["./src/types/*"],
"@page/*": ["./src/pages/*"]
}
}, },
"include": ["src"], "include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }] "references": [{ "path": "./tsconfig.node.json" }]
+2 -1
View File
@@ -1,7 +1,8 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' import react from '@vitejs/plugin-react'
import tsconfigPaths from 'vite-tsconfig-paths'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react(), tsconfigPaths()],
}) })