|
- "use client";
-
- import React from "react";
- import Paper from "@mui/material/Paper";
- import Table from "@mui/material/Table";
- import TableBody from "@mui/material/TableBody";
- import TableCell, { TableCellProps } from "@mui/material/TableCell";
- import TableContainer from "@mui/material/TableContainer";
- import TableHead from "@mui/material/TableHead";
- import TablePagination, {
- TablePaginationProps,
- } from "@mui/material/TablePagination";
- import TableRow from "@mui/material/TableRow";
- import IconButton, { IconButtonOwnProps } from "@mui/material/IconButton";
- import { ButtonOwnProps, Icon, IconOwnProps, SxProps, Theme } from "@mui/material";
- import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
- import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil";
-
- export interface ResultWithId {
- id: string | number;
- }
-
- type ColumnType = "icon" | "decimal" | "integer";
-
- interface BaseColumn<T extends ResultWithId> {
- name: keyof T;
- label: string;
- align?: TableCellProps["align"];
- headerAlign?: TableCellProps["align"];
- sx?: SxProps<Theme> | undefined;
- style?: Partial<HTMLElement["style"]> & { [propName: string]: string };
- type?: ColumnType;
- renderCell?: (params: T) => React.ReactNode;
- }
-
- interface IconColumn<T extends ResultWithId> extends BaseColumn<T> {
- name: keyof T;
- type: "icon";
- icon?: React.ReactNode;
- icons?: { [columnValue in keyof T]: React.ReactNode };
- color?: IconOwnProps["color"];
- colors?: { [columnValue in keyof T]: IconOwnProps["color"] };
- }
-
- interface DecimalColumn<T extends ResultWithId> extends BaseColumn<T> {
- type: "decimal";
- }
-
- interface IntegerColumn<T extends ResultWithId> extends BaseColumn<T> {
- type: "integer";
- }
-
- interface ColumnWithAction<T extends ResultWithId> extends BaseColumn<T> {
- onClick: (item: T) => void;
- buttonIcon: React.ReactNode;
- buttonIcons: { [columnValue in keyof T]: React.ReactNode };
- buttonColor?: IconButtonOwnProps["color"];
- }
-
- export type Column<T extends ResultWithId> =
- | BaseColumn<T>
- | IconColumn<T>
- | DecimalColumn<T>
- | ColumnWithAction<T>;
-
- interface Props<T extends ResultWithId> {
- items: T[],
- columns: Column<T>[],
- noWrapper?: boolean,
- setPagingController?: (value: (((prevState: { pageNum: number; pageSize: number; totalCount: number }) => {
- pageNum: number;
- pageSize: number;
- totalCount: number
- }) | { pageNum: number; pageSize: number; totalCount: number })) => void,
- pagingController: { pageNum: number; pageSize: number; totalCount: number },
- isAutoPaging?: boolean
- }
-
- function isActionColumn<T extends ResultWithId>(
- column: Column<T>,
- ): column is ColumnWithAction<T> {
- return Boolean((column as ColumnWithAction<T>).onClick);
- }
-
- function isIconColumn<T extends ResultWithId>(
- column: Column<T>,
- ): column is IconColumn<T> {
- return column.type === "icon";
- }
-
- function isDecimalColumn<T extends ResultWithId>(
- column: Column<T>,
- ): column is DecimalColumn<T> {
- return column.type === "decimal";
- }
-
- function isIntegerColumn<T extends ResultWithId>(
- column: Column<T>,
- ): column is IntegerColumn<T> {
- return column.type === "integer";
- }
-
- // Icon Component Functions
- function convertObjectKeysToLowercase<T extends object>(obj: T): object | undefined {
- return obj ? Object.fromEntries(
- Object.entries(obj).map(([key, value]) => [key.toLowerCase(), value])
- ) : undefined;
- }
-
- function handleIconColors<T extends ResultWithId>(
- column: IconColumn<T>,
- value: T[keyof T],
- ): IconOwnProps["color"] {
- const colors = convertObjectKeysToLowercase(column.colors ?? {});
- const valueKey = String(value).toLowerCase() as keyof typeof colors;
-
- if (colors && valueKey in colors) {
- return colors[valueKey];
- }
-
- return column.color ?? "primary";
- };
-
- function handleIconIcons<T extends ResultWithId>(
- column: IconColumn<T>,
- value: T[keyof T],
- ): React.ReactNode {
- const icons = convertObjectKeysToLowercase(column.icons ?? {});
- const valueKey = String(value).toLowerCase() as keyof typeof icons;
-
- if (icons && valueKey in icons) {
- return icons[valueKey];
- }
-
- return column.icon ?? <CheckCircleOutlineIcon fontSize="small" />;
- };
-
- function SearchResults<T extends ResultWithId>({
- items,
- columns,
- noWrapper,
- pagingController,
- setPagingController,
- isAutoPaging = true,
- }: Props<T>) {
- const [page, setPage] = React.useState(0);
- const [rowsPerPage, setRowsPerPage] = React.useState(10);
-
- /// this
- const handleChangePage: TablePaginationProps["onPageChange"] = (
- _event,
- newPage,
- ) => {
- console.log(_event)
- setPage(newPage);
- if (setPagingController) {
- setPagingController({
- ...pagingController,
- pageNum: newPage + 1,
- })
- }
- }
-
- const handleChangeRowsPerPage: TablePaginationProps["onRowsPerPageChange"] = (
- event,
- ) => {
- console.log(event)
- setRowsPerPage(+event.target.value);
- setPage(0);
- if (setPagingController) {
- setPagingController({
- ...pagingController,
- pageNum: +event.target.value,
- })
- }
- };
-
- const table = (
- <>
- <TableContainer sx={{ maxHeight: 440 }}>
- <Table stickyHeader>
- <TableHead>
- <TableRow>
- {columns.map((column, idx) => (
- <TableCell align={column.headerAlign} sx={column.sx} key={`${column.name.toString()}${idx}`}>
- {column.label}
- </TableCell>
- ))}
- </TableRow>
- </TableHead>
- <TableBody>
- {
- isAutoPaging ?
- items
- .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
- .map((item) => {
- return (
- <TableRow hover tabIndex={-1} key={item.id}>
- {columns.map((column, idx) => {
- const columnName = column.name;
-
- return (
- <TabelCells key={`${columnName.toString()}-${idx}`} column={column} columnName={columnName} idx={idx} item={item}/>
- );
- })}
- </TableRow>
- );
- })
- :
- items
- .map((item) => {
- return (
- <TableRow hover tabIndex={-1} key={item.id}>
- {columns.map((column, idx) => {
- const columnName = column.name;
-
- return (
- <TabelCells key={`${columnName.toString()}-${idx}`} column={column} columnName={columnName} idx={idx} item={item}/>
- );
- })}
- </TableRow>
- );
- })
- }
- </TableBody>
- </Table>
- </TableContainer>
- <TablePagination
- rowsPerPageOptions={[10, 25, 100]}
- component="div"
- count={!pagingController || pagingController.totalCount == 0 ? items.length : pagingController.totalCount}
- rowsPerPage={rowsPerPage}
- page={page}
- onPageChange={handleChangePage}
- onRowsPerPageChange={handleChangeRowsPerPage}
- />
- </>
- );
-
- return noWrapper ? table : <Paper sx={{ overflow: "hidden" }}>{table}</Paper>;
- }
-
- // Table cells
- interface TableCellsProps<T extends ResultWithId> {
- column: Column<T>,
- columnName: keyof T,
- idx: number,
- item: T,
- }
-
- function TabelCells<T extends ResultWithId>({
- column,
- columnName,
- idx,
- item
- }: TableCellsProps<T>) {
- return (
- <TableCell align={column.align} sx={column.sx} key={`${columnName.toString()}-${idx}`}>
- {isActionColumn(column) ? (
- <IconButton
- color={column.buttonColor ?? "primary"}
- onClick={() => column.onClick(item)}
- >
- {column.buttonIcon}
- </IconButton>
- ) :
- isIconColumn(column) ? (
- <Icon
- color={handleIconColors(column, item[columnName])}
- >
- {handleIconIcons(column, item[columnName])}
- </Icon>
- ) :
- isDecimalColumn(column) ? (
- <>{decimalFormatter.format(Number(item[columnName]))}</>
- ) :
- isIntegerColumn(column) ? (
- <>{integerFormatter.format(Number(item[columnName]))}</>
- ) :
- (
- column.renderCell ? column.renderCell(item) : <>{item[columnName] as string}</>
- )}
- </TableCell>)
- }
-
- export default SearchResults;
|