Update table component

This commit is contained in:
2023-12-07 17:43:56 +07:00
parent 7c7e6cd1b1
commit d878d3b0f6
6 changed files with 201 additions and 157 deletions
+53 -53
View File
@@ -1,70 +1,70 @@
import { ReactNode, useContext } from 'react'; import { ColumnProps } from '@src/types/common';
// Styled
import { import {
StyledBody, StyledBody,
StyledFooter,
StyledHeader, StyledHeader,
StyledRow, StyledRow,
StyledTable, StyledTable,
StyledFooter,
} from './styled'; } from './styled';
import Pagination from '../Pagination';
// Contexts const parseWidthString = (columnsWidth: number[]): string => {
import TableContext from '@src/contexts/TableContext'; let result: string = '';
export interface ITable { const totalWidth = columnsWidth.reduce((partialSum, a) => partialSum + a, 0);
columns?: string; columnsWidth.forEach((width) => {
children: ReactNode; result += Math.round((width / totalWidth) * 100).toString() + '% ';
} });
export interface IHeader { return result;
headerColumn: string[];
}
interface ITableBody<T> {
data?: T[];
render?: CallbackMapFunc<T>;
}
type CallbackMapFunc<T> = (value: T, index: number, array: T[]) => ReactNode;
const Table = ({ columns, children }: ITable) => {
return (
<TableContext.Provider value={{ columns }}>
<StyledTable>{children}</StyledTable>
</TableContext.Provider>
);
}; };
const Header = ({ headerColumn }: IHeader) => { type Props<T> = {
const { columns } = useContext(TableContext); columns: ColumnProps[];
rows: T[];
count?: number | null;
};
const Table = <T,>({ rows, columns, count }: Props<T>) => {
const width = parseWidthString(columns.map((item) => item.width));
const headers = columns.map((column, index) => {
return <div key={`headCell-${index}`}>{column.title}</div>;
});
const renderRows = rows.map((row, rowIndex) => {
return (
<StyledRow
key={`row-${rowIndex}`}
width={width}
onClick={
row[
'onClick' as keyof typeof row
] as React.MouseEventHandler<HTMLDivElement>
}
>
{columns.map((column, columnIndex) => {
const value = row[column.key as keyof typeof row] as string;
return <div key={`cell-${columnIndex}`}>{value}</div>;
})}
</StyledRow>
);
});
return ( return (
<StyledHeader columns={columns}> <StyledTable>
{headerColumn.map((item) => ( <StyledHeader width={width}>{headers}</StyledHeader>
<div key={item}>{item}</div>
))} <StyledBody>{renderRows}</StyledBody>
</StyledHeader>
{Boolean(count) && (
<StyledFooter>
<Pagination count={count!} />
</StyledFooter>
)}
</StyledTable>
); );
}; };
const Body = <T,>({ data, render }: ITableBody<T>) => {
return (
data!.length && (
<StyledBody>{data?.map(render as CallbackMapFunc<T>)}</StyledBody>
)
);
};
const Row = ({ children }: ITable) => {
const { columns } = useContext(TableContext);
return <StyledRow columns={columns}>{children}</StyledRow>;
};
Table.Header = Header;
Table.Body = Body;
Table.Row = Row;
Table.Footer = StyledFooter;
export default Table; export default Table;
@@ -1,7 +1,8 @@
import styled from 'styled-components'; import styled from 'styled-components';
// Interfaces interface ICommonRow {
import { ITable } from '.'; width: string;
}
const StyledTable = styled.div` const StyledTable = styled.div`
border: 1px solid var(--border-color); border: 1px solid var(--border-color);
@@ -10,9 +11,9 @@ const StyledTable = styled.div`
font-size: 20px; font-size: 20px;
`; `;
const CommonRow = styled.div<ITable>` const CommonRow = styled.div<ICommonRow>`
display: grid; display: grid;
grid-template-columns: ${(props) => props.columns}; grid-template-columns: ${(props) => props.width};
column-gap: 10px; column-gap: 10px;
align-items: center; align-items: center;
justify-items: center; justify-items: center;
@@ -1,12 +1,7 @@
import { useCallback } from 'react';
// Components // Components
import Menus from '@src/components/Menus';
import Table from '@src/components/Table'; import Table from '@src/components/Table';
import Message from '@src/components/Message'; import Message from '@src/components/Message';
import Search from '@src/components/Search'; import Search from '@src/components/Search';
import Pagination from '@src/components/Pagination';
import BookingRow from './BookingRow';
// Styled // Styled
import { StyledOperationTable } from './styled'; import { StyledOperationTable } from './styled';
@@ -17,28 +12,57 @@ import Spinner from '@src/commons/styles/Spinner';
import { useBookings } from '@src/hooks/bookings/useBookings'; import { useBookings } from '@src/hooks/bookings/useBookings';
// Types // Types
import { TBookingResponse } from '@src/types/booking'; import { ColumnProps } from '@src/types/common';
import { formatCurrency } from '@src/helpers/helper';
interface IBookingTable {
user: string;
date: string;
room: string;
amount: string;
status: string;
onClick: () => void;
}
const BookingTable = () => { const BookingTable = () => {
const columnName = [ const columns: ColumnProps[] = [
'User', {
'Date', key: 'user',
'Room', title: 'User',
'Amount', width: 15,
'Status' },
{
key: 'date',
title: 'Date',
width: 25,
},
{
key: 'room',
title: 'Room',
width: 20,
},
{
key: 'amount',
title: 'Amount',
width: 15,
},
{
key: 'status',
title: 'Status',
width: 15,
},
]; ];
const {
isLoading,
bookings,
count
} = useBookings();
const renderBookingRow = useCallback( const { isLoading, bookings, count } = useBookings();
(booking: TBookingResponse) => (
<BookingRow booking={booking} key={booking.id} /> const tempBookings = bookings?.map((booking) => ({
), date: 'Error',
[] amount: formatCurrency(booking.amount),
); room: booking.rooms!.name,
user: booking.users!.name,
status: booking.status ? 'Check in' : 'Check out',
onClick: () => console.log(booking),
}));
return ( return (
<> <>
@@ -49,19 +73,12 @@ const BookingTable = () => {
{isLoading && <Spinner />} {isLoading && <Spinner />}
{bookings && bookings.length ? ( {tempBookings && tempBookings.length ? (
<Menus> <Table<IBookingTable>
<Table columns="15% 25% 20% 15% 10% 10% 5%"> columns={columns}
<Table.Header headerColumn={columnName} /> rows={tempBookings}
<Table.Body<TBookingResponse> count={count}
data={bookings}
render={renderBookingRow}
/> />
<Table.Footer>
<Pagination count={count!} />
</Table.Footer>
</Table>
</Menus>
) : ( ) : (
!isLoading && <Message>No data to show here!</Message> !isLoading && <Message>No data to show here!</Message>
)} )}
+38 -25
View File
@@ -1,16 +1,13 @@
import { useCallback } from 'react';
// Components // Components
import Menus from '@src/components/Menus';
import Table from '@src/components/Table'; import Table from '@src/components/Table';
import Message from '@src/components/Message'; import Message from '@src/components/Message';
import Search from '@src/components/Search'; import Search from '@src/components/Search';
import SortBy from '@src/components/SortBy'; import SortBy from '@src/components/SortBy';
import OrderBy from '@src/components/OrderBy'; import OrderBy from '@src/components/OrderBy';
import RoomRow from './RoomRow';
// Types // Types
import { IRoom } from '@src/types/room'; import { IRoom } from '@src/types/room';
import { ColumnProps } from '@src/types/common';
// Constants // Constants
import { ORDERBY_OPTIONS, ROOM_PAGE } from '@src/constants/commons'; import { ORDERBY_OPTIONS, ROOM_PAGE } from '@src/constants/commons';
@@ -22,20 +19,44 @@ import Spinner from '@src/commons/styles/Spinner';
// Hooks // Hooks
import { useRooms } from '@src/hooks/rooms/useRooms'; import { useRooms } from '@src/hooks/rooms/useRooms';
import Pagination from '@src/components/Pagination';
interface IRoomTable extends Omit<IRoom, 'status'>{
status: string;
onClick: () => void;
}
const RoomTable = () => { const RoomTable = () => {
const columnName = ['Id', 'Name', 'Price', 'Status']; const columns: ColumnProps[] = [
const { {
isLoading, key: 'id',
rooms, title: 'Id',
count width: 10,
} = useRooms(); },
{
key: 'name',
title: 'Name',
width: 40,
},
{
key: 'price',
title: 'Price',
width: 20,
},
{
key: 'status',
title: 'Status',
width: 20,
},
];
const { isLoading, rooms, count } = useRooms();
const renderRoomRow = useCallback( const tempRooms = rooms?.map((room) => ({
(room: IRoom) => <RoomRow room={room} key={room.id} />, ...room,
[] status: room.status
); ? 'Unavailable'
: 'Available',
onClick: () => console.log(room),
}));
return ( return (
<> <>
@@ -49,16 +70,8 @@ const RoomTable = () => {
{isLoading && <Spinner />} {isLoading && <Spinner />}
{rooms && rooms.length ? ( {tempRooms && tempRooms.length ? (
<Menus> <Table<IRoomTable> columns={columns} rows={tempRooms} count={count}/>
<Table columns="10% 40% 20% 20% 5%">
<Table.Header headerColumn={columnName} />
<Table.Body<IRoom> data={rooms} render={renderRoomRow} />
<Table.Footer>
<Pagination count={count!}/>
</Table.Footer>
</Table>
</Menus>
) : ( ) : (
!isLoading && <Message>No data to show here!</Message> !isLoading && <Message>No data to show here!</Message>
)} )}
+38 -37
View File
@@ -1,17 +1,9 @@
import { useCallback } from 'react';
// Components // Components
import Menus from '@src/components/Menus';
import Table from '@src/components/Table'; import Table from '@src/components/Table';
import Message from '@src/components/Message'; import Message from '@src/components/Message';
import Search from '@src/components/Search'; import Search from '@src/components/Search';
import SortBy from '@src/components/SortBy'; import SortBy from '@src/components/SortBy';
import OrderBy from '@src/components/OrderBy'; import OrderBy from '@src/components/OrderBy';
import UserRow from './UserRow';
import Pagination from '@src/components/Pagination';
// Types
import { IUser } from '@src/types/user';
// Constants // Constants
import { ORDERBY_OPTIONS, USER_PAGE } from '@src/constants/commons'; import { ORDERBY_OPTIONS, USER_PAGE } from '@src/constants/commons';
@@ -23,29 +15,46 @@ import Spinner from '@src/commons/styles/Spinner';
// Hooks // Hooks
import { useUsers } from '@src/hooks/users/useUsers'; import { useUsers } from '@src/hooks/users/useUsers';
import { ColumnProps } from '@src/types/common';
import { IUser } from '@src/types/user';
interface IUserTable extends Omit<IUser, 'isBooked'>{
isBooked: string;
onClick: () => void;
}
const UserTable = () => { const UserTable = () => {
const columnName = [ const columns: ColumnProps[] = [
'Id', {
'Name', key: 'id',
'Phone', title: 'Id',
'Is Booked' width: 10,
},
{
key: 'name',
title: 'Name',
width: 35,
},
{
key: 'phone',
title: 'Phone',
width: 30,
},
{
key: 'isBooked',
title: 'Is Booked',
width: 20,
},
]; ];
const { const { isLoading, users, count } = useUsers();
isLoading,
users,
count
} = useUsers();
const renderUserRow = useCallback( const tempUsers = users?.map((user) => ({
(user: IUser) => ( ...user,
<UserRow isBooked: user.isBooked
user={user} ? 'Yes'
key={user.id} : 'No',
/> onClick: () => console.log(user),
), }));
[]
);
return ( return (
<> <>
@@ -59,16 +68,8 @@ const UserTable = () => {
{isLoading && <Spinner />} {isLoading && <Spinner />}
{users && users.length ? ( {tempUsers && tempUsers.length ? (
<Menus> <Table<IUserTable> columns={columns} rows={tempUsers} count={count}/>
<Table columns="10% 35% 30% 15% 5%">
<Table.Header headerColumn={columnName} />
<Table.Body<IUser> data={users} render={renderUserRow} />
<Table.Footer>
<Pagination count={count!}/>
</Table.Footer>
</Table>
</Menus>
) : ( ) : (
!isLoading && <Message>No data to show here!</Message> !isLoading && <Message>No data to show here!</Message>
)} )}
+13 -1
View File
@@ -12,6 +12,18 @@ interface ILogin {
password: string; password: string;
} }
interface ColumnProps {
key: string;
title: string;
width: number;
}
type TDirection = 'vertical' | 'horizontal'; type TDirection = 'vertical' | 'horizontal';
export type { Nullable, IDataState, TDirection, ILogin }; export type {
Nullable,
IDataState,
TDirection,
ILogin,
ColumnProps
};