mirror of
https://github.com/Nezumi-2711/react-training.git
synced 2026-09-22 13:38:51 +00:00
Refactor code, fix UI and logic code
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
// Components
|
||||
import Header from './Header';
|
||||
import Sidebar from './Sidebar';
|
||||
|
||||
const StyledAppLayout = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: 300px 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
height: 100vh;
|
||||
`;
|
||||
|
||||
const Main = styled.main`
|
||||
border: 1px solid var(--border-color);
|
||||
|
||||
overflow: scroll;
|
||||
`;
|
||||
|
||||
const AppLayout = () => {
|
||||
return (
|
||||
<StyledAppLayout>
|
||||
<Header />
|
||||
<Sidebar />
|
||||
<Main>
|
||||
<Outlet />
|
||||
</Main>
|
||||
</StyledAppLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppLayout;
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
// Components
|
||||
import Header from '../Header';
|
||||
import Sidebar from '../Sidebar';
|
||||
|
||||
// Styled
|
||||
import { Main, StyledAppLayout } from './styled';
|
||||
|
||||
const AppLayout = () => {
|
||||
return (
|
||||
<StyledAppLayout>
|
||||
<Header />
|
||||
<Sidebar />
|
||||
<Main>
|
||||
<Outlet />
|
||||
</Main>
|
||||
</StyledAppLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppLayout;
|
||||
@@ -0,0 +1,16 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledAppLayout = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: 300px 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
height: 100vh;
|
||||
`;
|
||||
|
||||
const Main = styled.main`
|
||||
border: 1px solid var(--border-color);
|
||||
|
||||
overflow: scroll;
|
||||
`;
|
||||
|
||||
export { StyledAppLayout, Main };
|
||||
@@ -1,3 +1,4 @@
|
||||
// Styled
|
||||
import { StyledActionBtn, StyledForm } from './styled';
|
||||
|
||||
interface IFormProps {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Styled
|
||||
import { Error, Label, StyledFormRow } from './styled';
|
||||
|
||||
interface IFormRow {
|
||||
|
||||
@@ -16,6 +16,8 @@ const Error = styled.p`
|
||||
color: var(--error-text);
|
||||
font-size: var(--fs-sm-2x);
|
||||
margin-top: 5px;
|
||||
padding-inline: 5px;
|
||||
width: 220px;
|
||||
`;
|
||||
|
||||
export { StyledFormRow, Label, Error };
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Components
|
||||
import HeaderMenu from '../HeaderMenu';
|
||||
|
||||
// Styled
|
||||
import { StyledHeader } from './styled';
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<StyledHeader>
|
||||
<p>Hi, Admin!</p>
|
||||
<HeaderMenu />
|
||||
</StyledHeader>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
+1
-13
@@ -1,8 +1,5 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
// Components
|
||||
import HeaderMenu from './HeaderMenu';
|
||||
|
||||
const StyledHeader = styled.header`
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 20px 40px;
|
||||
@@ -13,13 +10,4 @@ const StyledHeader = styled.header`
|
||||
gap: 30px;
|
||||
`;
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<StyledHeader>
|
||||
<p>Hi, Admin!</p>
|
||||
<HeaderMenu />
|
||||
</StyledHeader>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
export { StyledHeader };
|
||||
+3
-7
@@ -1,14 +1,10 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
// Components
|
||||
import { IoPersonCircleOutline } from 'react-icons/io5';
|
||||
import { HiOutlineLogout } from 'react-icons/hi';
|
||||
import ButtonIcon from '../commons/styles/ButtonIcon';
|
||||
import ButtonIcon from '../../commons/styles/ButtonIcon';
|
||||
|
||||
const StyledHeaderMenu = styled.ul`
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
`;
|
||||
// Styled
|
||||
import { StyledHeaderMenu } from './styled';
|
||||
|
||||
const HeaderMenu = () => {
|
||||
return (
|
||||
@@ -0,0 +1,8 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledHeaderMenu = styled.ul`
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
`;
|
||||
|
||||
export { StyledHeaderMenu };
|
||||
@@ -0,0 +1,8 @@
|
||||
// Styled
|
||||
import { StyledMessage } from './styled';
|
||||
|
||||
const Message = ({ children }: { children: string }) => {
|
||||
return <StyledMessage>{children}</StyledMessage>;
|
||||
};
|
||||
|
||||
export default Message;
|
||||
+1
-5
@@ -6,8 +6,4 @@ const StyledMessage = styled.p`
|
||||
padding: 20px;
|
||||
`;
|
||||
|
||||
const Message = ({ children }: { children: string }) => {
|
||||
return <StyledMessage>{children}</StyledMessage>;
|
||||
};
|
||||
|
||||
export default Message;
|
||||
export { StyledMessage };
|
||||
@@ -0,0 +1,28 @@
|
||||
// Components
|
||||
import { MdSpaceDashboard } from 'react-icons/md';
|
||||
import { HiUsers } from 'react-icons/hi2';
|
||||
import { BsHouseDoorFill } from 'react-icons/bs';
|
||||
|
||||
// Styled
|
||||
import { StyledNav, StyledNavLink } from './styled';
|
||||
|
||||
const Nav = () => {
|
||||
return (
|
||||
<StyledNav>
|
||||
<StyledNavLink to={'/dashboard'}>
|
||||
<MdSpaceDashboard />
|
||||
<span>Dashboard</span>
|
||||
</StyledNavLink>
|
||||
<StyledNavLink to={'/user'}>
|
||||
<HiUsers />
|
||||
<span>User</span>
|
||||
</StyledNavLink>
|
||||
<StyledNavLink to={'/room'}>
|
||||
<BsHouseDoorFill />
|
||||
<span>Room</span>
|
||||
</StyledNavLink>
|
||||
</StyledNav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nav;
|
||||
+1
-25
@@ -1,11 +1,6 @@
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
|
||||
// Components
|
||||
import { MdSpaceDashboard } from 'react-icons/md';
|
||||
import { HiUsers } from 'react-icons/hi2';
|
||||
import { BsHouseDoorFill } from 'react-icons/bs';
|
||||
|
||||
const StyledNav = styled.nav`
|
||||
margin-top: 70px;
|
||||
display: flex;
|
||||
@@ -38,23 +33,4 @@ const StyledNavLink = styled(NavLink)`
|
||||
}
|
||||
`;
|
||||
|
||||
const Nav = () => {
|
||||
return (
|
||||
<StyledNav>
|
||||
<StyledNavLink to={'/dashboard'}>
|
||||
<MdSpaceDashboard />
|
||||
<span>Dashboard</span>
|
||||
</StyledNavLink>
|
||||
<StyledNavLink to={'/user'}>
|
||||
<HiUsers />
|
||||
<span>User</span>
|
||||
</StyledNavLink>
|
||||
<StyledNavLink to={'/room'}>
|
||||
<BsHouseDoorFill />
|
||||
<span>Room</span>
|
||||
</StyledNavLink>
|
||||
</StyledNav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Nav;
|
||||
export { StyledNav, StyledNavLink };
|
||||
+2
-33
@@ -1,39 +1,8 @@
|
||||
import { memo } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
const StyledOrder = styled.div`
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
`;
|
||||
|
||||
interface IOrderBtn {
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
const OrderButton = styled.button<IOrderBtn>`
|
||||
border: none;
|
||||
|
||||
${(props) =>
|
||||
props.active &&
|
||||
css`
|
||||
background-color: var(--primary-color);
|
||||
color: var(--light-text);
|
||||
`}
|
||||
|
||||
border-radius: var(--radius-sm);
|
||||
font-weight: 500;
|
||||
font-size: var(--fs-sm-x);
|
||||
padding: 5px 10px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--hover-dark-background-color);
|
||||
}
|
||||
`;
|
||||
// Styled
|
||||
import { OrderButton, StyledOrder } from './styled';
|
||||
|
||||
interface IOrderProps {
|
||||
options: {
|
||||
@@ -0,0 +1,38 @@
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
const StyledOrder = styled.div`
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
`;
|
||||
|
||||
interface IOrderBtn {
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
const OrderButton = styled.button<IOrderBtn>`
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
${(props) =>
|
||||
props.active &&
|
||||
css`
|
||||
background-color: var(--primary-color);
|
||||
color: var(--light-text);
|
||||
cursor: no-drop;
|
||||
`}
|
||||
|
||||
border-radius: var(--radius-sm);
|
||||
font-weight: 500;
|
||||
font-size: var(--fs-sm-x);
|
||||
padding: 5px 10px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--hover-dark-background-color);
|
||||
}
|
||||
`;
|
||||
|
||||
export { StyledOrder, OrderButton };
|
||||
+1
-8
@@ -1,12 +1,5 @@
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledSearch = styled.input`
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border-color);
|
||||
font-size: var(--fs-sm-x);
|
||||
padding: 5px 10px;
|
||||
`;
|
||||
import { StyledSearch } from './styled';
|
||||
|
||||
interface ISearch {
|
||||
setPlaceHolder: string;
|
||||
@@ -0,0 +1,10 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledSearch = styled.input`
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border-color);
|
||||
font-size: var(--fs-sm-x);
|
||||
padding: 5px 10px;
|
||||
`;
|
||||
|
||||
export { StyledSearch };
|
||||
@@ -1,32 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
interface ISelect {
|
||||
options: {
|
||||
value: string;
|
||||
label: string;
|
||||
}[];
|
||||
value: string;
|
||||
onChange: React.ChangeEventHandler<HTMLSelectElement>;
|
||||
}
|
||||
|
||||
const StyledSelect = styled.select`
|
||||
font-size: var(--fs-sm-x);
|
||||
padding: 5px 10px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
font-weight: 500;
|
||||
`;
|
||||
|
||||
const Select = ({ options, value, onChange }: ISelect) => {
|
||||
return (
|
||||
<StyledSelect value={value} onChange={onChange} aria-label="Sort">
|
||||
{options.map((option) => (
|
||||
<option value={option.value} key={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</StyledSelect>
|
||||
);
|
||||
};
|
||||
|
||||
export default Select;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ISelectOptions } from '../../globals/interfaces';
|
||||
import { StyledSelect } from './styled';
|
||||
|
||||
interface ISelect {
|
||||
options: ISelectOptions[];
|
||||
value: string;
|
||||
onChange: React.ChangeEventHandler<HTMLSelectElement>;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
const Select = ({ options, value, onChange, name }: ISelect) => {
|
||||
if (!options) return;
|
||||
|
||||
return (
|
||||
<StyledSelect
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
aria-label="Sort"
|
||||
name={name}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<option value={option.value} key={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</StyledSelect>
|
||||
);
|
||||
};
|
||||
|
||||
export default Select;
|
||||
@@ -0,0 +1,11 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledSelect = styled.select`
|
||||
font-size: var(--fs-sm-x);
|
||||
padding: 5px 10px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius-sm);
|
||||
font-weight: 500;
|
||||
`;
|
||||
|
||||
export { StyledSelect };
|
||||
@@ -0,0 +1,16 @@
|
||||
// Components
|
||||
import Nav from '../Nav';
|
||||
|
||||
// Styled
|
||||
import { Heading, StyledSidebar } from './styled';
|
||||
|
||||
const Sidebar = () => {
|
||||
return (
|
||||
<StyledSidebar>
|
||||
<Heading>Hotel Management</Heading>
|
||||
<Nav />
|
||||
</StyledSidebar>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
+1
-13
@@ -1,8 +1,5 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
// Components
|
||||
import Nav from './Nav';
|
||||
|
||||
const StyledSidebar = styled.aside`
|
||||
padding: 50px 20px;
|
||||
|
||||
@@ -17,13 +14,4 @@ const Heading = styled.h1`
|
||||
color: var(--primary-color);
|
||||
`;
|
||||
|
||||
const Sidebar = () => {
|
||||
return (
|
||||
<StyledSidebar>
|
||||
<Heading>Hotel Management</Heading>
|
||||
<Nav />
|
||||
</StyledSidebar>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
export { StyledSidebar, Heading };
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
// Components
|
||||
import Select from './Select';
|
||||
import Select from '../Select';
|
||||
import { memo } from 'react';
|
||||
|
||||
interface ISortByProps {
|
||||
@@ -11,6 +11,8 @@ const EDIT_SUCCESS = 'Edit success';
|
||||
const CONFIRM_DELETE = 'Are you sure to delete it?';
|
||||
const DELETE_SUCCESS = 'Delete success';
|
||||
const REQUIRED_FIELD_ERROR = 'This is required field';
|
||||
const DISCOUNT_FIELD_ERROR =
|
||||
'Discount should be greater than 0 and less than 100';
|
||||
|
||||
export {
|
||||
invalidFormatMsg,
|
||||
@@ -20,4 +22,5 @@ export {
|
||||
CONFIRM_DELETE,
|
||||
DELETE_SUCCESS,
|
||||
REQUIRED_FIELD_ERROR,
|
||||
DISCOUNT_FIELD_ERROR,
|
||||
};
|
||||
|
||||
@@ -2,6 +2,6 @@ export const DASHBOARD: string = '/dashboard';
|
||||
export const USER: string = '/user';
|
||||
export const ROOM: string = '/room';
|
||||
export const OTHER_PATH: string = '*';
|
||||
export const BASE_URL: string = 'https://hotel-management-api.loiphan.com/';
|
||||
export const BASE_URL: string = 'http://localhost:3000/';
|
||||
export const USER_PATH = 'users';
|
||||
export const ROOM_PATH = 'rooms';
|
||||
|
||||
@@ -20,6 +20,11 @@ interface ITableBody<T> {
|
||||
render?: (value: T) => JSX.Element;
|
||||
}
|
||||
|
||||
interface ISelectOptions {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface IDialogProps<T> {
|
||||
title?: string;
|
||||
children?: JSX.Element[] | JSX.Element;
|
||||
@@ -31,4 +36,11 @@ interface IDialogProps<T> {
|
||||
isAdd?: boolean;
|
||||
}
|
||||
|
||||
export type { IMenusContext, IButton, ITable, ITableBody, IDialogProps };
|
||||
export type {
|
||||
IMenusContext,
|
||||
IButton,
|
||||
ITable,
|
||||
ITableBody,
|
||||
IDialogProps,
|
||||
ISelectOptions,
|
||||
};
|
||||
|
||||
@@ -56,7 +56,8 @@ const getPropValues = (stateSchema: TStateSchema, prop?: TPropValues) => {
|
||||
|
||||
type TValidator = {
|
||||
validatorFunc: (value: string) => boolean;
|
||||
prop: string;
|
||||
prop?: string;
|
||||
customErrorMsg?: string;
|
||||
required?: boolean;
|
||||
};
|
||||
|
||||
@@ -65,12 +66,20 @@ type TValidator = {
|
||||
* @param param0 Pass TValidator object
|
||||
* @returns An object contains condition validator
|
||||
*/
|
||||
const addValidator = ({ validatorFunc, prop, required = true }: TValidator) => {
|
||||
const addValidator = ({
|
||||
validatorFunc,
|
||||
prop = '',
|
||||
customErrorMsg = '',
|
||||
required = true,
|
||||
}: TValidator) => {
|
||||
return {
|
||||
required,
|
||||
validator: {
|
||||
func: validatorFunc,
|
||||
error: invalidFormatMsg(prop),
|
||||
// prettier-ignore
|
||||
error: customErrorMsg
|
||||
? customErrorMsg
|
||||
: invalidFormatMsg(prop),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -7,6 +7,16 @@ const isValidNumber = (value: string): boolean => {
|
||||
return /^[0-9]*$/.test(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check value is valid discount or not
|
||||
* @param value Value need to be checked
|
||||
* @returns A boolean indicating whether or not the argument has valid
|
||||
*/
|
||||
const isValidDiscount = (value: string): boolean => {
|
||||
console.log(Boolean(+value >= 0 && +value <= 100));
|
||||
return +value >= 0 && +value <= 100;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check value is valid phone number or not
|
||||
* @param value Value need to be checked
|
||||
@@ -31,4 +41,10 @@ const isValidString = (value: string): boolean => {
|
||||
*/
|
||||
const skipCheck = () => true;
|
||||
|
||||
export { isValidNumber, isValidPhoneNumber, isValidString, skipCheck };
|
||||
export {
|
||||
isValidNumber,
|
||||
isValidPhoneNumber,
|
||||
isValidString,
|
||||
skipCheck,
|
||||
isValidDiscount,
|
||||
};
|
||||
|
||||
@@ -19,15 +19,15 @@ import { searchQuery } from '../helpers/utils';
|
||||
*/
|
||||
const useFetch = (
|
||||
path: string,
|
||||
columnSearch: string,
|
||||
keyWord: string,
|
||||
tempSortBy: string,
|
||||
tempOrderBy: string,
|
||||
columnSearch: string = '',
|
||||
keyWord: string = '',
|
||||
tempSortBy?: string,
|
||||
tempOrderBy?: string,
|
||||
reload?: boolean,
|
||||
) => {
|
||||
const [data, setData] = useState(null);
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
const [errorMsg, setErrorMsg] = useState<string | null>(null);
|
||||
const [errorFetchMsg, setErrorFetchMsg] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Clear data
|
||||
@@ -61,16 +61,16 @@ const useFetch = (
|
||||
|
||||
setIsPending(false);
|
||||
setData(json);
|
||||
setErrorMsg(null);
|
||||
setErrorFetchMsg(null);
|
||||
} catch (error) {
|
||||
setErrorMsg(`Could not fetch data.\n ${error}`);
|
||||
setErrorFetchMsg(`Could not fetch data.\n ${error}`);
|
||||
setIsPending(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [path, reload, columnSearch, keyWord, tempOrderBy, tempSortBy]);
|
||||
return { data, isPending, errorMsg };
|
||||
return { data, isPending, errorFetchMsg };
|
||||
};
|
||||
|
||||
export { useFetch };
|
||||
|
||||
@@ -110,7 +110,11 @@ const useForm = (
|
||||
|
||||
// Event handler for handling changes in input.
|
||||
const handleOnChange = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
(
|
||||
event: ChangeEvent<
|
||||
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||
>,
|
||||
) => {
|
||||
setIsDirty(true);
|
||||
|
||||
let error = '';
|
||||
|
||||
@@ -24,6 +24,7 @@ import useForm from '../../hooks/useForm.ts';
|
||||
|
||||
// Utils
|
||||
import {
|
||||
isValidDiscount,
|
||||
isValidNumber,
|
||||
isValidString,
|
||||
skipCheck,
|
||||
@@ -35,6 +36,7 @@ import { sendRequest } from '../../helpers/sendRequest.ts';
|
||||
import { STATUS_CODE } from '../../constants/statusCode.ts';
|
||||
import {
|
||||
ADD_SUCCESS,
|
||||
DISCOUNT_FIELD_ERROR,
|
||||
EDIT_SUCCESS,
|
||||
errorMsg,
|
||||
} from '../../constants/messages.ts';
|
||||
@@ -46,6 +48,7 @@ const FormBtn = styled(Button)`
|
||||
&:disabled,
|
||||
&[disabled] {
|
||||
background-color: var(--disabled-btn-color);
|
||||
cursor: no-drop;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -134,8 +137,8 @@ const RoomForm = ({
|
||||
prop: 'price',
|
||||
}),
|
||||
discount: addValidator({
|
||||
validatorFunc: isValidNumber,
|
||||
prop: 'discount'
|
||||
validatorFunc: isValidDiscount,
|
||||
customErrorMsg: DISCOUNT_FIELD_ERROR
|
||||
}),
|
||||
status: addValidator({
|
||||
validatorFunc: skipCheck,
|
||||
@@ -305,8 +308,8 @@ const RoomForm = ({
|
||||
label="Discount"
|
||||
error={
|
||||
// prettier-ignore
|
||||
errors.roomId && dirty.roomId
|
||||
? (errors.roomId as string)
|
||||
errors.discount && dirty.discount
|
||||
? (errors.discount as string)
|
||||
: ''
|
||||
}
|
||||
>
|
||||
@@ -350,7 +353,7 @@ const RoomForm = ({
|
||||
// prettier-ignore
|
||||
isAdd
|
||||
? 'Add'
|
||||
: 'Edit'
|
||||
: 'Save'
|
||||
}
|
||||
</FormBtn>
|
||||
<FormBtn type="button" styled="secondary" onClick={closeAndReset}>
|
||||
|
||||
@@ -125,7 +125,7 @@ const RoomTable = ({
|
||||
? searchParams.get('orderBy')!
|
||||
: '';
|
||||
|
||||
const { data, isPending, errorMsg } = useFetch(
|
||||
const { data, isPending, errorFetchMsg } = useFetch(
|
||||
'rooms',
|
||||
'name',
|
||||
nameSearch,
|
||||
@@ -141,10 +141,10 @@ const RoomTable = ({
|
||||
setRooms([]);
|
||||
}
|
||||
|
||||
if (errorMsg) {
|
||||
console.error(errorMsg);
|
||||
if (errorFetchMsg) {
|
||||
console.error(errorFetchMsg);
|
||||
}
|
||||
}, [data, errorMsg]);
|
||||
}, [data, errorFetchMsg]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -39,6 +39,11 @@ import {
|
||||
errorMsg,
|
||||
} from '../../constants/messages.ts';
|
||||
import { USER_PATH } from '../../constants/path.ts';
|
||||
import Select from '../../components/Select/index.tsx';
|
||||
import { useFetch } from '../../hooks/useFetch.ts';
|
||||
|
||||
// Interfaces
|
||||
import { ISelectOptions } from '../../globals/interfaces.ts';
|
||||
|
||||
const FormBtn = styled(Button)`
|
||||
width: 100%;
|
||||
@@ -46,6 +51,7 @@ const FormBtn = styled(Button)`
|
||||
&:disabled,
|
||||
&[disabled] {
|
||||
background-color: var(--disabled-btn-color);
|
||||
cursor: no-drop;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -65,6 +71,27 @@ const UserForm = ({
|
||||
isAdd,
|
||||
}: IUserFormProp) => {
|
||||
const [reset, setReset] = useState(true);
|
||||
const [options, setOptions] = useState<ISelectOptions[]>();
|
||||
const { data, errorFetchMsg } = useFetch('rooms');
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
const tempData = data as TKeyValue[];
|
||||
const tempOptions: ISelectOptions[] = [];
|
||||
tempData.forEach((item) => {
|
||||
tempOptions.push({
|
||||
label: item.name! as string,
|
||||
value: item.id! as string,
|
||||
});
|
||||
});
|
||||
|
||||
setOptions(tempOptions);
|
||||
}
|
||||
|
||||
if (errorFetchMsg) {
|
||||
toast.error(errorFetchMsg);
|
||||
}
|
||||
}, [data, errorFetchMsg]);
|
||||
|
||||
if (isAdd) {
|
||||
// If this is add form, reset value
|
||||
@@ -294,11 +321,17 @@ const UserForm = ({
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<Input
|
||||
{/* <Input
|
||||
type="text"
|
||||
name="roomId"
|
||||
value={roomId as string}
|
||||
onChange={handleOnChange}
|
||||
/> */}
|
||||
<Select
|
||||
name="roomId"
|
||||
value={roomId as string}
|
||||
onChange={handleOnChange}
|
||||
options={options!}
|
||||
/>
|
||||
</FormRow>
|
||||
|
||||
@@ -325,7 +358,7 @@ const UserForm = ({
|
||||
// prettier-ignore
|
||||
isAdd
|
||||
? 'Add'
|
||||
: 'Edit'
|
||||
: 'Save'
|
||||
}
|
||||
</FormBtn>
|
||||
<FormBtn type="button" styled="secondary" onClick={closeAndReset}>
|
||||
|
||||
@@ -22,13 +22,13 @@ import { useFetch } from '../../hooks/useFetch';
|
||||
import { USER_PATH } from '../../constants/path';
|
||||
import { STATUS_CODE } from '../../constants/statusCode';
|
||||
import { CONFIRM_DELETE, DELETE_SUCCESS } from '../../constants/messages';
|
||||
import { USER_PAGE } from '../../constants/variables';
|
||||
|
||||
// Styled
|
||||
import Spinner from '../../commons/styles/Spinner';
|
||||
|
||||
// Utils
|
||||
import { sendRequest } from '../../helpers/sendRequest';
|
||||
import { USER_PAGE } from '../../constants/variables';
|
||||
|
||||
interface IUserRow {
|
||||
user: TUser;
|
||||
@@ -119,7 +119,7 @@ const UserTable = ({
|
||||
? searchParams.get('orderBy')!
|
||||
: '';
|
||||
|
||||
const { data, isPending, errorMsg } = useFetch(
|
||||
const { data, isPending, errorFetchMsg } = useFetch(
|
||||
'users',
|
||||
'phone',
|
||||
phoneSearch,
|
||||
@@ -135,10 +135,10 @@ const UserTable = ({
|
||||
setUsers([]);
|
||||
}
|
||||
|
||||
if (errorMsg) {
|
||||
console.error(errorMsg);
|
||||
if (errorFetchMsg) {
|
||||
console.error(errorFetchMsg);
|
||||
}
|
||||
}, [data, errorMsg]);
|
||||
}, [data, errorFetchMsg]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -163,7 +163,7 @@ const UserTable = ({
|
||||
<div>Name</div>
|
||||
<div>Identified Code</div>
|
||||
<div>Phone</div>
|
||||
<div>Room</div>
|
||||
<div>Room Id</div>
|
||||
</Table.Header>
|
||||
<Table.Body<TUser>
|
||||
data={users}
|
||||
|
||||
Reference in New Issue
Block a user