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';
// Styled
import { ColumnProps } from '@src/types/common';
import {
StyledBody,
StyledFooter,
StyledHeader,
StyledRow,
StyledTable,
StyledFooter,
} from './styled';
import Pagination from '../Pagination';
// Contexts
import TableContext from '@src/contexts/TableContext';
const parseWidthString = (columnsWidth: number[]): string => {
let result: string = '';
export interface ITable {
columns?: string;
children: ReactNode;
}
const totalWidth = columnsWidth.reduce((partialSum, a) => partialSum + a, 0);
columnsWidth.forEach((width) => {
result += Math.round((width / totalWidth) * 100).toString() + '% ';
});
export interface IHeader {
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>
);
return result;
};
const Header = ({ headerColumn }: IHeader) => {
const { columns } = useContext(TableContext);
type Props<T> = {
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 (
<StyledHeader columns={columns}>
{headerColumn.map((item) => (
<div key={item}>{item}</div>
))}
</StyledHeader>
<StyledTable>
<StyledHeader width={width}>{headers}</StyledHeader>
<StyledBody>{renderRows}</StyledBody>
{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;
@@ -1,7 +1,8 @@
import styled from 'styled-components';
// Interfaces
import { ITable } from '.';
interface ICommonRow {
width: string;
}
const StyledTable = styled.div`
border: 1px solid var(--border-color);
@@ -10,9 +11,9 @@ const StyledTable = styled.div`
font-size: 20px;
`;
const CommonRow = styled.div<ITable>`
const CommonRow = styled.div<ICommonRow>`
display: grid;
grid-template-columns: ${(props) => props.columns};
grid-template-columns: ${(props) => props.width};
column-gap: 10px;
align-items: center;
justify-items: center;
@@ -1,12 +1,7 @@
import { useCallback } from 'react';
// Components
import Menus from '@src/components/Menus';
import Table from '@src/components/Table';
import Message from '@src/components/Message';
import Search from '@src/components/Search';
import Pagination from '@src/components/Pagination';
import BookingRow from './BookingRow';
// Styled
import { StyledOperationTable } from './styled';
@@ -17,28 +12,57 @@ import Spinner from '@src/commons/styles/Spinner';
import { useBookings } from '@src/hooks/bookings/useBookings';
// 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 columnName = [
'User',
'Date',
'Room',
'Amount',
'Status'
const columns: ColumnProps[] = [
{
key: 'user',
title: 'User',
width: 15,
},
{
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(
(booking: TBookingResponse) => (
<BookingRow booking={booking} key={booking.id} />
),
[]
);
const { isLoading, bookings, count } = useBookings();
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 (
<>
@@ -49,19 +73,12 @@ const BookingTable = () => {
{isLoading && <Spinner />}
{bookings && bookings.length ? (
<Menus>
<Table columns="15% 25% 20% 15% 10% 10% 5%">
<Table.Header headerColumn={columnName} />
<Table.Body<TBookingResponse>
data={bookings}
render={renderBookingRow}
{tempBookings && tempBookings.length ? (
<Table<IBookingTable>
columns={columns}
rows={tempBookings}
count={count}
/>
<Table.Footer>
<Pagination count={count!} />
</Table.Footer>
</Table>
</Menus>
) : (
!isLoading && <Message>No data to show here!</Message>
)}
+38 -25
View File
@@ -1,16 +1,13 @@
import { useCallback } from 'react';
// Components
import Menus from '@src/components/Menus';
import Table from '@src/components/Table';
import Message from '@src/components/Message';
import Search from '@src/components/Search';
import SortBy from '@src/components/SortBy';
import OrderBy from '@src/components/OrderBy';
import RoomRow from './RoomRow';
// Types
import { IRoom } from '@src/types/room';
import { ColumnProps } from '@src/types/common';
// Constants
import { ORDERBY_OPTIONS, ROOM_PAGE } from '@src/constants/commons';
@@ -22,20 +19,44 @@ import Spinner from '@src/commons/styles/Spinner';
// Hooks
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 columnName = ['Id', 'Name', 'Price', 'Status'];
const {
isLoading,
rooms,
count
} = useRooms();
const columns: ColumnProps[] = [
{
key: 'id',
title: 'Id',
width: 10,
},
{
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(
(room: IRoom) => <RoomRow room={room} key={room.id} />,
[]
);
const tempRooms = rooms?.map((room) => ({
...room,
status: room.status
? 'Unavailable'
: 'Available',
onClick: () => console.log(room),
}));
return (
<>
@@ -49,16 +70,8 @@ const RoomTable = () => {
{isLoading && <Spinner />}
{rooms && rooms.length ? (
<Menus>
<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>
{tempRooms && tempRooms.length ? (
<Table<IRoomTable> columns={columns} rows={tempRooms} count={count}/>
) : (
!isLoading && <Message>No data to show here!</Message>
)}
+38 -37
View File
@@ -1,17 +1,9 @@
import { useCallback } from 'react';
// Components
import Menus from '@src/components/Menus';
import Table from '@src/components/Table';
import Message from '@src/components/Message';
import Search from '@src/components/Search';
import SortBy from '@src/components/SortBy';
import OrderBy from '@src/components/OrderBy';
import UserRow from './UserRow';
import Pagination from '@src/components/Pagination';
// Types
import { IUser } from '@src/types/user';
// Constants
import { ORDERBY_OPTIONS, USER_PAGE } from '@src/constants/commons';
@@ -23,29 +15,46 @@ import Spinner from '@src/commons/styles/Spinner';
// Hooks
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 columnName = [
'Id',
'Name',
'Phone',
'Is Booked'
const columns: ColumnProps[] = [
{
key: 'id',
title: 'Id',
width: 10,
},
{
key: 'name',
title: 'Name',
width: 35,
},
{
key: 'phone',
title: 'Phone',
width: 30,
},
{
key: 'isBooked',
title: 'Is Booked',
width: 20,
},
];
const {
isLoading,
users,
count
} = useUsers();
const { isLoading, users, count } = useUsers();
const renderUserRow = useCallback(
(user: IUser) => (
<UserRow
user={user}
key={user.id}
/>
),
[]
);
const tempUsers = users?.map((user) => ({
...user,
isBooked: user.isBooked
? 'Yes'
: 'No',
onClick: () => console.log(user),
}));
return (
<>
@@ -59,16 +68,8 @@ const UserTable = () => {
{isLoading && <Spinner />}
{users && users.length ? (
<Menus>
<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>
{tempUsers && tempUsers.length ? (
<Table<IUserTable> columns={columns} rows={tempUsers} count={count}/>
) : (
!isLoading && <Message>No data to show here!</Message>
)}
+13 -1
View File
@@ -12,6 +12,18 @@ interface ILogin {
password: string;
}
interface ColumnProps {
key: string;
title: string;
width: number;
}
type TDirection = 'vertical' | 'horizontal';
export type { Nullable, IDataState, TDirection, ILogin };
export type {
Nullable,
IDataState,
TDirection,
ILogin,
ColumnProps
};