"use client"; import React, { ChangeEvent, Dispatch, MouseEvent, SetStateAction, useCallback, useMemo, useState, } 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, Checkbox, Icon, IconOwnProps, SxProps, Theme, } from "@mui/material"; import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline"; import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; import { filter, remove, uniq } from "lodash"; export interface ResultWithId { id: string | number; } type ColumnType = "icon" | "decimal" | "integer" | "checkbox"; interface BaseColumn { name: keyof T; label: string; align?: TableCellProps["align"]; headerAlign?: TableCellProps["align"]; sx?: SxProps | undefined; style?: Partial & { [propName: string]: string }; type?: ColumnType; renderCell?: (params: T) => React.ReactNode; } interface IconColumn extends BaseColumn { 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 extends BaseColumn { type: "decimal"; } interface IntegerColumn extends BaseColumn { type: "integer"; } interface CheckboxColumn extends BaseColumn { type: "checkbox"; disabled?: (params: T) => boolean; // checkboxIds: readonly (string | number)[], // setCheckboxIds: (ids: readonly (string | number)[]) => void } interface ColumnWithAction extends BaseColumn { onClick: (item: T) => void; buttonIcon: React.ReactNode; buttonIcons: { [columnValue in keyof T]: React.ReactNode }; buttonColor?: IconButtonOwnProps["color"]; } export type Column = | BaseColumn | IconColumn | DecimalColumn | CheckboxColumn | ColumnWithAction; interface Props { totalCount?: number; items: T[]; columns: Column[]; noWrapper?: boolean; setPagingController?: Dispatch< SetStateAction<{ pageNum: number; pageSize: number; }> >; pagingController?: { pageNum: number; pageSize: number }; isAutoPaging?: boolean; checkboxIds?: (string | number)[]; setCheckboxIds?: Dispatch>; onRowClick?: (item: T) => void; } function isActionColumn( column: Column, ): column is ColumnWithAction { return Boolean((column as ColumnWithAction).onClick); } function isIconColumn( column: Column, ): column is IconColumn { return column.type === "icon"; } function isDecimalColumn( column: Column, ): column is DecimalColumn { return column.type === "decimal"; } function isIntegerColumn( column: Column, ): column is IntegerColumn { return column.type === "integer"; } function isCheckboxColumn( column: Column, ): column is CheckboxColumn { return column.type === "checkbox"; } // Icon Component Functions function convertObjectKeysToLowercase( obj: T, ): object | undefined { return obj ? Object.fromEntries( Object.entries(obj).map(([key, value]) => [key.toLowerCase(), value]), ) : undefined; } function handleIconColors( column: IconColumn, 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( column: IconColumn, 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 ?? ; } export const defaultPagingController: { pageNum: number; pageSize: number } = { pageNum: 1, pageSize: 10, }; export type defaultSetPagingController = Dispatch< SetStateAction<{ pageNum: number; pageSize: number; }> > function SearchResults({ items, columns, noWrapper, pagingController, setPagingController, isAutoPaging = true, totalCount, checkboxIds = [], setCheckboxIds = undefined, onRowClick = undefined, }: Props) { 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 ?? defaultPagingController), pageNum: newPage + 1, }); } }; const handleChangeRowsPerPage: TablePaginationProps["onRowsPerPageChange"] = ( event, ) => { console.log(event); setRowsPerPage(+event.target.value); setPage(0); if (setPagingController) { setPagingController({ ...(pagingController ?? defaultPagingController), pageNum: 1, }); } }; // checkbox const currItems = useMemo(() => { return items.length > 10 ? items .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) .map((i) => i.id) : items.map((i) => i.id) }, [items, page, rowsPerPage]) const currItemsWithChecked = useMemo(() => { return filter(checkboxIds, function (c) { return currItems.includes(c); }) }, [checkboxIds, items, page, rowsPerPage]) const handleRowClick = useCallback( (event: MouseEvent, item: T, columns: Column[]) => { // check is disabled or not let disabled = false; columns.forEach((col) => { if (isCheckboxColumn(col) && col.disabled) { disabled = col.disabled(item); if (disabled) { return; } } }); if (disabled) { return; } // set id const id = item.id; if (setCheckboxIds) { const selectedIndex = checkboxIds.indexOf(id); let newSelected: (string | number)[] = []; if (selectedIndex === -1) { newSelected = newSelected.concat(checkboxIds, id); } else if (selectedIndex === 0) { newSelected = newSelected.concat(checkboxIds.slice(1)); } else if (selectedIndex === checkboxIds.length - 1) { newSelected = newSelected.concat(checkboxIds.slice(0, -1)); } else if (selectedIndex > 0) { newSelected = newSelected.concat( checkboxIds.slice(0, selectedIndex), checkboxIds.slice(selectedIndex + 1), ); } setCheckboxIds(newSelected); } }, [checkboxIds, setCheckboxIds], ); const handleSelectAllClick = (event: React.ChangeEvent) => { if (setCheckboxIds) { const pageItemId = currItems if (event.target.checked) { setCheckboxIds((prev) => uniq([...prev, ...pageItemId])) } else { setCheckboxIds((prev) => filter(prev, function (p) { return !pageItemId.includes(p); })) } } } const table = ( <> {columns.map((column, idx) => ( isCheckboxColumn(column) ? 0 && currItemsWithChecked.length < currItems.length} checked={currItems.length > 0 && currItemsWithChecked.length >= currItems.length} onChange={handleSelectAllClick} /> : {column.label.split('\n').map((line, index) => (
{line}
// Render each line in a div ))}
))}
{isAutoPaging ? items .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) .map((item) => { return ( { console.log("first") setCheckboxIds ? handleRowClick(event, item, columns) : undefined if (onRowClick) { onRowClick(item) } } } role={setCheckboxIds ? "checkbox" : undefined} > {columns.map((column, idx) => { const columnName = column.name; return ( ); })} ); }) : items.map((item) => { return ( { setCheckboxIds ? handleRowClick(event, item, columns) : undefined if (onRowClick) { onRowClick(item) } } } role={setCheckboxIds ? "checkbox" : undefined} > {columns.map((column, idx) => { const columnName = column.name; return ( ); })} ); })}
); return noWrapper ? table : {table}; } // Table cells interface TableCellsProps { column: Column; columnName: keyof T; idx: number; item: T; checkboxIds: (string | number)[]; } function TabelCells({ column, columnName, idx, item, checkboxIds = [], }: TableCellsProps) { const isItemSelected = checkboxIds.includes(item.id); return ( {isActionColumn(column) ? ( column.onClick(item)} > {column.buttonIcon} ) : isIconColumn(column) ? ( {handleIconIcons(column, item[columnName])} ) : isDecimalColumn(column) ? ( <>{decimalFormatter.format(Number(item[columnName]))} ) : isIntegerColumn(column) ? ( <>{integerFormatter.format(Number(item[columnName]))} ) : isCheckboxColumn(column) ? ( ) : column.renderCell ? ( column.renderCell(item) ) : ( <>{item[columnName] as string} )} ); } export default SearchResults;