|
|
@@ -17,6 +17,7 @@ import { useTranslation } from "react-i18next"; |
|
|
|
import { convertDateArrayToString, moneyFormatter } from "@/app/utils/formatUtil"; |
|
|
|
import DoneIcon from '@mui/icons-material/Done'; |
|
|
|
import CloseIcon from '@mui/icons-material/Close'; |
|
|
|
import { Button, Link, LinkOwnProps } from "@mui/material"; |
|
|
|
|
|
|
|
export interface ResultWithId { |
|
|
|
id: string | number; |
|
|
@@ -25,7 +26,6 @@ export interface ResultWithId { |
|
|
|
interface BaseColumn<T extends ResultWithId> { |
|
|
|
name: keyof T; |
|
|
|
label: string; |
|
|
|
color?: IconButtonOwnProps["color"]; |
|
|
|
needTranslation?: boolean; |
|
|
|
type?: string; |
|
|
|
isHidden?: boolean; |
|
|
@@ -34,13 +34,25 @@ interface BaseColumn<T extends ResultWithId> { |
|
|
|
interface ColumnWithAction<T extends ResultWithId> extends BaseColumn<T> { |
|
|
|
onClick: (item: T) => void; |
|
|
|
buttonIcon: React.ReactNode; |
|
|
|
color?: IconButtonOwnProps["color"]; |
|
|
|
disabled?: boolean; |
|
|
|
disabledRows?: { [columnName in keyof T]: string[] }; // Filter the row which is going to be disabled |
|
|
|
disabledRows?: { [columnValue in keyof T]: string[] }; // Filter the row which is going to be disabled |
|
|
|
} |
|
|
|
|
|
|
|
interface LinkColumn<T extends ResultWithId> extends BaseColumn<T> { |
|
|
|
// href: string; |
|
|
|
onClick: (item: T) => void; |
|
|
|
underline: LinkOwnProps["underline"]; |
|
|
|
underlines: { [columnValue in keyof T]: LinkOwnProps["underline"] }; |
|
|
|
color: LinkOwnProps["color"]; |
|
|
|
colors: { [columnValue in keyof T]: LinkOwnProps["color"] }; |
|
|
|
} |
|
|
|
|
|
|
|
export type Column<T extends ResultWithId> = |
|
|
|
| BaseColumn<T> |
|
|
|
| ColumnWithAction<T>; |
|
|
|
| ColumnWithAction<T> |
|
|
|
| LinkColumn<T> |
|
|
|
; |
|
|
|
|
|
|
|
interface Props<T extends ResultWithId> { |
|
|
|
items: T[]; |
|
|
@@ -55,6 +67,12 @@ function isActionColumn<T extends ResultWithId>( |
|
|
|
return Boolean((column as ColumnWithAction<T>).onClick); |
|
|
|
} |
|
|
|
|
|
|
|
function isLinkColumn<T extends ResultWithId>( |
|
|
|
column: Column<T>, |
|
|
|
): column is LinkColumn<T> { |
|
|
|
return column.type === "link"; |
|
|
|
} |
|
|
|
|
|
|
|
function SearchResults<T extends ResultWithId>({ |
|
|
|
items, |
|
|
|
columns, |
|
|
@@ -101,6 +119,43 @@ function SearchResults<T extends ResultWithId>({ |
|
|
|
return false; |
|
|
|
}; |
|
|
|
|
|
|
|
function convertObjectKeysToLowercase<T extends object>(obj: T): object | undefined { |
|
|
|
return obj ? Object.fromEntries( |
|
|
|
Object.entries(obj).map(([key, value]) => [key.toLowerCase(), value]) |
|
|
|
) : undefined; |
|
|
|
} |
|
|
|
|
|
|
|
// Link Component Functions |
|
|
|
function handleLinkColors<T extends ResultWithId>( |
|
|
|
column: LinkColumn<T>, |
|
|
|
value: T[keyof T], |
|
|
|
): LinkOwnProps["color"] { |
|
|
|
const colors = convertObjectKeysToLowercase(column.colors); |
|
|
|
console.log(colors) |
|
|
|
const valueKey = String(value).toLowerCase() as keyof typeof colors; |
|
|
|
|
|
|
|
if (colors && valueKey in colors) { |
|
|
|
return colors[valueKey]; |
|
|
|
} |
|
|
|
|
|
|
|
return column.color ?? "primary"; |
|
|
|
}; |
|
|
|
|
|
|
|
function handleLinkUnderlines<T extends ResultWithId>( |
|
|
|
column: LinkColumn<T>, |
|
|
|
value: T[keyof T], |
|
|
|
): LinkOwnProps["underline"] { |
|
|
|
const underlines = convertObjectKeysToLowercase(column.underlines); |
|
|
|
console.log(underlines) |
|
|
|
const valueKey = String(value).toLowerCase() as keyof typeof underlines; |
|
|
|
|
|
|
|
if (underlines && valueKey in underlines) { |
|
|
|
return underlines[valueKey]; |
|
|
|
} |
|
|
|
|
|
|
|
return column.underline ?? "always"; |
|
|
|
}; |
|
|
|
|
|
|
|
const table = ( |
|
|
|
<> |
|
|
|
<TableContainer sx={{ maxHeight: 440 }}> |
|
|
@@ -125,15 +180,15 @@ function SearchResults<T extends ResultWithId>({ |
|
|
|
|
|
|
|
return ( |
|
|
|
<TableCell key={`${columnName.toString()}-${idx}`}> |
|
|
|
{isActionColumn(column) ? ( |
|
|
|
<IconButton |
|
|
|
color={column.color ?? "primary"} |
|
|
|
onClick={() => column.onClick(item)} |
|
|
|
disabled={Boolean(column.disabled) || Boolean(disabledRows(column, item))} |
|
|
|
> |
|
|
|
{column.buttonIcon} |
|
|
|
</IconButton> |
|
|
|
) : |
|
|
|
{isActionColumn(column) && !column?.type ? ( |
|
|
|
<IconButton |
|
|
|
color={column.color ?? "primary"} |
|
|
|
onClick={() => column.onClick(item)} |
|
|
|
disabled={Boolean(column.disabled) || Boolean(disabledRows(column, item))} |
|
|
|
> |
|
|
|
{column.buttonIcon} |
|
|
|
</IconButton> |
|
|
|
) : |
|
|
|
column?.type === "date" ? ( |
|
|
|
<>{convertDateArrayToString(item[columnName] as number[])}</> |
|
|
|
) : |
|
|
@@ -143,9 +198,18 @@ function SearchResults<T extends ResultWithId>({ |
|
|
|
column?.type === "checkbox" ? ( |
|
|
|
Boolean(item[columnName]) ? |
|
|
|
<DoneIcon color="primary" /> : <CloseIcon color="error"/> |
|
|
|
) : |
|
|
|
isLinkColumn(column) ? ( |
|
|
|
<Link |
|
|
|
onClick={() => column.onClick(item)} |
|
|
|
underline={handleLinkUnderlines(column, item[columnName])} |
|
|
|
color={handleLinkColors(column, item[columnName])} |
|
|
|
> |
|
|
|
{item[columnName] as string} |
|
|
|
</Link> |
|
|
|
) : |
|
|
|
( |
|
|
|
<>{column?.needTranslation ? t(item[columnName] as string) : item[columnName]}</> |
|
|
|
<>{column?.needTranslation ? t(item[columnName] as string) : item[columnName] as string}</> |
|
|
|
)} |
|
|
|
</TableCell> |
|
|
|
); |
|
|
|