Skip to content

Commit

Permalink
Add a 'select all' option for columns and rows in the group permissio…
Browse files Browse the repository at this point in the history
…ns edit page
  • Loading branch information
SteveGT96 committed Oct 29, 2024
1 parent f3bda7a commit f4ae4ab
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 53 deletions.
24 changes: 13 additions & 11 deletions src/components/accessories/admin/users/editGroup/EditGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ import {
updateUserGroup,
updateUserGroupReset,
} from "../../../../../state/usergroups";
import { TabOptions } from "../Users";
import { GroupPermissionsEditor } from "../editPermissions/GroupPermissionsEditor";
import {
PermissionActionEnum,
PermissionActionType,
comparePermissions,
} from "../editPermissions/permission.utils";
import { TabOptions } from "../Users";
import "./styles.scss";
import { userGroupSchema } from "./validation";

Expand Down Expand Up @@ -58,18 +58,18 @@ export const EditGroup = () => {
>([]);

const handleUpdatePermissions = ({
permission,
permissions: perms,
action,
}: PermissionActionType) => {
const otherPermissions = groupPermissions.filter(
(p) => p.id !== permission.id
(p) => !perms.some((item) => item.id === p.id)
);

if (action === PermissionActionEnum.REVOKE) {
setGroupPermissions(otherPermissions);
}
if (action === PermissionActionEnum.ASSIGN) {
setGroupPermissions([...otherPermissions, permission]);
setGroupPermissions([...otherPermissions, ...perms]);
}
};

Expand Down Expand Up @@ -197,13 +197,15 @@ export const EditGroup = () => {
<code>
Editing permissions:{" "}
{updatedPermissionsStack
.map(
(p) =>
`${p.permission.name}: ${
p.action === PermissionActionEnum.ASSIGN
? "assign"
: "revoked"
}`
.flatMap((p) =>
p.permissions.map(
(item) =>
`${item.name}: ${
p.action === PermissionActionEnum.ASSIGN
? "assign"
: "revoked"
}`
)
)
.join(",")}
</code>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,59 +1,39 @@
import { Checkbox, Popper } from "@mui/material";
import { Checkbox, Tooltip } from "@mui/material";
import React from "react";
import { useTranslation } from "react-i18next";
import { PermissionDTO } from "../../../../../generated";
import { PermissionActionEnum } from "./permission.utils";

interface IProps {
permission: PermissionDTO;
groupPermissions: Array<PermissionDTO>;
onChange: (permission: PermissionDTO, action: PermissionActionEnum) => void;
onChange: (
permissions: [PermissionDTO],
action: PermissionActionEnum
) => void;
}

export const AclPermissionCheckbox = ({
permission,
groupPermissions,
onChange,
}: IProps) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const open = Boolean(anchorEl);
const id = open ? "simple-popper" : undefined;
const { t } = useTranslation();
const checked =
groupPermissions?.some((p) => p.id === permission.id) || false;
return (
<>
<Tooltip title={t(`permission.${permission.name!.split(".")[1]}`)}>
<Checkbox
aria-describedby={id}
aria-describedby={permission.name}
checked={checked}
onChange={(_ev, val) =>
onChange(
permission,
[permission],
checked ? PermissionActionEnum.REVOKE : PermissionActionEnum.ASSIGN
)
}
name={permission.id.toString()}
onMouseEnter={(event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
}}
onMouseLeave={() => setAnchorEl(null)}
/>
<Popper
id={id}
open={open}
placement="right"
disablePortal
anchorEl={anchorEl}
style={{ zIndex: 1 }}
>
<span
style={{
backgroundColor: "#fc1812",
color: "#fff",
padding: "5px",
}}
>
{permission.name || "unknown"}
</span>
</Popper>
</>
</Tooltip>
);
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useMemo } from "react";
import React, { ChangeEvent, useCallback, useMemo } from "react";

import { Checkbox, Tooltip } from "@mui/material";
import { useTranslation } from "react-i18next";
import { PermissionDTO } from "../../../../../generated";
import { AclPermissionCheckbox } from "./AclPermissionCheckbox";
Expand All @@ -13,7 +14,10 @@ import {
interface IProps {
permissions: PermissionDTO[];
groupPermissions: PermissionDTO[];
onChange: (permission: PermissionDTO, action: PermissionActionEnum) => void;
onChange: (
permissions: PermissionDTO[],
action: PermissionActionEnum
) => void;
}

export const AclTable = ({
Expand All @@ -29,22 +33,111 @@ export const AclTable = ({

const crudKeys = Array.from(crudPermissions.keys());

const allColumnsChecked = useCallback(
(crudKey: string) => {
return (
[Crud.CREATE, Crud.READ, Crud.UPDATE, Crud.DELETE].filter((item) =>
groupPermissions.some((p) => p.name === `${crudKey}.${item}`)
).length ===
[Crud.CREATE, Crud.READ, Crud.UPDATE, Crud.DELETE].filter((item) =>
permissions.some((p) => p.name === `${crudKey}.${item}`)
).length
);
},
[groupPermissions, permissions]
);

const allRowsChecked = useCallback(() => {
return (
crudKeys.filter((crudKey) => allColumnsChecked(crudKey)).length ===
crudKeys.length
);
}, [groupPermissions, permissions, allColumnsChecked]);

const toggleCheckAllColumns = useCallback(
(crudKey: string) => (event: ChangeEvent<HTMLInputElement>) => {
const crudPermNames = [
Crud.CREATE,
Crud.READ,
Crud.UPDATE,
Crud.DELETE,
].map((item) => `${crudKey}.${item}`);
const crudPerms = permissions.filter((p) =>
crudPermNames.includes(p.name!)
);
if (event.target.checked) {
onChange(
crudPerms.filter(
(item) => !groupPermissions.some((p) => p.id === item.id)
),
PermissionActionEnum.ASSIGN
);
} else {
onChange(crudPerms, PermissionActionEnum.REVOKE);
}
},
[groupPermissions, permissions, onChange]
);

const toggleCheckAllRows = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
const crudPermNames = [
Crud.CREATE,
Crud.READ,
Crud.UPDATE,
Crud.DELETE,
].flatMap((item) => crudKeys.map((crudKey) => `${crudKey}.${item}`));
const crudPerms = permissions.filter((p) =>
crudPermNames.includes(p.name!)
);
if (event.target.checked) {
onChange(
crudPerms.filter(
(item) => !groupPermissions.some((p) => p.id === item.id)
),
PermissionActionEnum.ASSIGN
);
} else {
onChange(crudPerms, PermissionActionEnum.REVOKE);
}
},
[groupPermissions, permissions]
);

return (
<div className={classes.container}>
<table className={classes.acl}>
<thead>
<tr>
<td>
<Tooltip title={t("permission.toggle-check-all")}>
<Checkbox
checked={allRowsChecked()}
onChange={toggleCheckAllRows}
name={"permission.all"}
/>
</Tooltip>
</td>
<th>{t("permission.name")}</th>
<th>{t("permission.create")}</th>
<th>{t("permission.read")}</th>
<th>{t("permission.update")}</th>
<th>{t("permission.deleted")}</th>
<th>{t("permission.delete")}</th>
</tr>
</thead>
<tbody>
{Array.from(crudPermissions.values()).map((crudPermission, index) => {
return (
<tr key={index}>
<td>
<Tooltip title={t("permission.toggle-check-all")}>
<Checkbox
checked={allColumnsChecked(crudKeys[index])}
onChange={toggleCheckAllColumns(crudKeys[index])}
name={"permission.all"}
/>
</Tooltip>
</td>
<td>{crudKeys[index]}</td>
{[Crud.CREATE, Crud.READ, Crud.UPDATE, Crud.DELETE].map(
(access: Crud, index: number) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { PermissionActionEnum } from "./permission.utils";
interface IProps {
permissions: PermissionDTO[];
groupPermissions: PermissionDTO[];
onChange: (permission: PermissionDTO, action: PermissionActionEnum) => void;
onChange: (
permissions: PermissionDTO[],
action: PermissionActionEnum
) => void;
}
export const AreaAccess = ({
permissions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ export const GroupPermissionsEditor = ({
update,
}: IProps) => {
const handleChange = (
newPermission: PermissionDTO,
newPermissions: PermissionDTO[],
action: PermissionActionEnum
) => {
setDirty(true);
update({ permission: newPermission, action });
update({ permissions: newPermissions, action });
};

const { t } = useTranslation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import { PermissionActionEnum } from "./permission.utils";
interface IProps {
permission: PermissionDTO;
groupPermissions: Array<PermissionDTO>;
onChange: (permission: PermissionDTO, action: PermissionActionEnum) => void;
onChange: (
permissions: PermissionDTO[],
action: PermissionActionEnum
) => void;
}

export const PermissionCheckbox = ({
Expand All @@ -23,7 +26,7 @@ export const PermissionCheckbox = ({
checked={checked}
onChange={(_ev, val) =>
onChange(
permission,
[permission],
checked
? PermissionActionEnum.REVOKE
: PermissionActionEnum.ASSIGN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export enum PermissionActionEnum {

export type PermissionActionType = {
action: PermissionActionEnum;
permission: PermissionDTO;
permissions: PermissionDTO[];
};

export enum Crud {
Expand Down Expand Up @@ -79,7 +79,7 @@ export const comparePermissions = (
action: stackedPermission
? PermissionActionEnum.ASSIGN
: PermissionActionEnum.REVOKE,
permission,
permissions: [permission],
},
];
}
Expand Down
5 changes: 4 additions & 1 deletion src/resources/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -574,8 +574,11 @@
"read": "Read",
"update": "Update",
"deleted": "Deleted",
"delete": "Delete",
"accessarea": "Access area",
"accesscontrollist": "Access control list"
"accesscontrollist": "Access control list",
"toggle-check-all": "Toggle check all",
"all": "All"
},
"therapy": {
"newtherapy": "New Therapy",
Expand Down

0 comments on commit f4ae4ab

Please sign in to comment.