hexabot/frontend/src/app-components/tables/columns/getColumns.tsx
2024-10-01 14:23:02 +01:00

199 lines
6.1 KiB
TypeScript

/*
* Copyright © 2024 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { CheckCircle } from "@mui/icons-material";
import AdminPanelSettingsIcon from "@mui/icons-material/AdminPanelSettingsOutlined";
import DeleteIcon from "@mui/icons-material/DeleteOutlined";
import EditIcon from "@mui/icons-material/EditOutlined";
import ListAltOutlinedIcon from "@mui/icons-material/ListAltOutlined";
import LocalOfferIcon from "@mui/icons-material/LocalOffer";
import ManageAccountsIcon from "@mui/icons-material/ManageAccounts";
import TocOutlinedIcon from "@mui/icons-material/TocOutlined";
import ViewListIcon from "@mui/icons-material/ViewListOutlined";
import { Stack, Tooltip } from "@mui/material";
import {
GridActionsCellItem,
GridColDef,
GridRenderCellParams,
GridTreeNodeWithRender,
GridValidRowModel,
} from "@mui/x-data-grid";
import { useHasPermission } from "@/hooks/useHasPermission";
import { useTranslate } from "@/hooks/useTranslate";
import { TTranslationKeys } from "@/i18n/i18n.types";
import { theme } from "@/layout/themes/theme";
import { EntityType } from "@/services/types";
import { PermissionAction } from "@/types/permission.types";
export enum ActionColumnLabel {
Edit = "Edit",
Delete = "Delete",
Values = "Values",
Manage_Roles = "Manage_Roles",
Permissions = "Permissions",
Content = "Content",
Fields = "Fields",
Manage_Labels = "Manage_Labels",
Toggle = "Toggle",
}
const ACTION_COLUMN_LABEL_MAP: Record<ActionColumnLabel, TTranslationKeys> = {
[ActionColumnLabel.Edit]: "button.edit",
[ActionColumnLabel.Delete]: "button.delete",
[ActionColumnLabel.Values]: "button.values",
[ActionColumnLabel.Manage_Roles]: "button.manage_roles",
[ActionColumnLabel.Permissions]: "button.permissions",
[ActionColumnLabel.Content]: "button.content",
[ActionColumnLabel.Fields]: "button.fields",
[ActionColumnLabel.Manage_Labels]: "title.manage_labels",
[ActionColumnLabel.Toggle]: "button.toggle",
} as const;
export interface ActionColumn<T extends GridValidRowModel> {
label: ActionColumnLabel;
action?: (row: T) => void;
requires?: PermissionAction[];
getState?: (row: T) => boolean;
helperText?: string;
isDisabled?: (row: T) => boolean;
}
const BUTTON_WIDTH = 60;
export const getActionsWidth = (itemsNumber: number) =>
itemsNumber === 1 ? BUTTON_WIDTH + 60 : BUTTON_WIDTH * itemsNumber;
function getIcon(label: ActionColumnLabel) {
switch (label) {
case ActionColumnLabel.Edit:
return <EditIcon />;
case ActionColumnLabel.Delete:
return <DeleteIcon />;
case ActionColumnLabel.Values:
return <ViewListIcon />;
case ActionColumnLabel.Manage_Roles:
return <ManageAccountsIcon />;
case ActionColumnLabel.Permissions:
return <AdminPanelSettingsIcon />;
case ActionColumnLabel.Content:
return <ListAltOutlinedIcon />;
case ActionColumnLabel.Fields:
return <TocOutlinedIcon />;
case ActionColumnLabel.Manage_Labels:
return <LocalOfferIcon />;
case ActionColumnLabel.Toggle:
return <CheckCircle />;
default:
return <></>;
}
}
function getColor(label: ActionColumnLabel) {
switch (label) {
case ActionColumnLabel.Edit:
return theme.palette.grey[900];
case ActionColumnLabel.Delete:
return theme.palette.error.main;
default:
return theme.palette.primary.main;
}
}
function StackComponent<T extends GridValidRowModel>({
actions,
params,
}: {
actions: ActionColumn<T>[];
params: GridRenderCellParams<T, any, any, GridTreeNodeWithRender>;
}) {
const { t } = useTranslate();
return (
<Stack height="100%" alignItems="center" direction="row" spacing={0.5}>
{actions.map(
({
label,
action,
requires = [],
getState,
helperText,
isDisabled,
}) => (
<GridActionsCellItem
key={label}
className="actionButton"
icon={
<Tooltip
title={helperText || String(t(ACTION_COLUMN_LABEL_MAP[label]))}
>
{getIcon(label)}
</Tooltip>
}
label={helperText || t(ACTION_COLUMN_LABEL_MAP[label])}
showInMenu={false}
sx={{
color:
label === ActionColumnLabel.Toggle &&
getState &&
getState(params.row)
? getColor(label)
: theme.palette.grey[600],
"&:hover": {
color: getColor(label),
},
}}
disabled={
(isDisabled && isDisabled(params.row)) ||
(params.row.builtin &&
(requires.includes(PermissionAction.UPDATE) ||
requires.includes(PermissionAction.DELETE)))
}
onClick={() => {
action && action(params.row);
}}
/>
),
)}
</Stack>
);
}
export const getActionsColumn = <T extends GridValidRowModel>(
actions: ActionColumn<T>[],
headerName: string,
): GridColDef<T> => {
return {
maxWidth: getActionsWidth(actions.length),
field: "actions",
headerName,
sortable: false,
minWidth: getActionsWidth(actions.length),
align: "center",
renderCell: (params) => (
<StackComponent actions={actions} params={params} />
),
};
};
export const useActionColumns = <T extends GridValidRowModel>(
type: EntityType,
actions: ActionColumn<T>[],
headerName: string,
) => {
const hasPermission = useHasPermission();
return getActionsColumn(
actions.filter(
({ requires }) =>
!requires || requires.every((action) => hasPermission(type, action)),
),
headerName,
);
};