Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OH2-399 | Add a 'select all' option for columns and rows in the group permissions edit page #684

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
<th>
<Tooltip title={t("permission.toggle-check-all")}>
<Checkbox
checked={allRowsChecked()}
onChange={toggleCheckAllRows}
name={"permission.all"}
/>
</Tooltip>
</th>
<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 @@ -576,8 +576,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