Merge pull request #2124 from automatisch/AUT-1232

fix: use correct default values when editing a role
This commit is contained in:
Ali BARIN
2024-11-08 12:24:31 +01:00
committed by GitHub
6 changed files with 148 additions and 33 deletions

View File

@@ -0,0 +1,51 @@
import React from 'react';
import { useFormContext } from 'react-hook-form';
import PropTypes from 'prop-types';
import ControlledCheckbox from 'components/ControlledCheckbox';
const ActionField = ({ action, subject, disabled, name, syncIsCreator }) => {
const { formState, resetField } = useFormContext();
const actionDefaultValue =
formState.defaultValues?.[name]?.[subject.key]?.[action.key].value;
const conditionFieldName = `${name}.${subject.key}.${action.key}.conditions.isCreator`;
const conditionFieldTouched =
formState.touchedFields?.[name]?.[subject.key]?.[action.key]?.conditions
?.isCreator === true;
const handleSyncIsCreator = (newValue) => {
if (
syncIsCreator &&
actionDefaultValue === false &&
!conditionFieldTouched
) {
resetField(conditionFieldName, { defaultValue: newValue });
}
};
return (
<ControlledCheckbox
disabled={disabled}
name={`${name}.${subject.key}.${action.key}.value`}
dataTest={`${action.key.toLowerCase()}-checkbox`}
onChange={(e, value) => {
handleSyncIsCreator(value);
}}
/>
);
};
ActionField.propTypes = {
action: PropTypes.shape({
key: PropTypes.string.isRequired,
subjects: PropTypes.arrayOf(PropTypes.string).isRequired,
}),
subject: PropTypes.shape({
key: PropTypes.string.isRequired,
}).isRequired,
disabled: PropTypes.bool,
name: PropTypes.string.isRequired,
syncIsCreator: PropTypes.bool,
};
export default ActionField;

View File

@@ -25,7 +25,6 @@ function PermissionSettings(props) {
subject,
actions,
conditions,
defaultChecked,
} = props;
const formatMessage = useFormatMessage();
const { getValues, resetField } = useFormContext();
@@ -34,7 +33,7 @@ function PermissionSettings(props) {
for (const action of actions) {
for (const condition of conditions) {
const fieldName = `${fieldPrefix}.${action.key}.conditions.${condition.key}`;
resetField(fieldName);
resetField(fieldName, { keepTouched: true });
}
}
onClose();
@@ -45,7 +44,7 @@ function PermissionSettings(props) {
for (const condition of conditions) {
const fieldName = `${fieldPrefix}.${action.key}.conditions.${condition.key}`;
const value = getValues(fieldName);
resetField(fieldName, { defaultValue: value });
resetField(fieldName, { defaultValue: value, keepTouched: true });
}
}
onClose();
@@ -56,6 +55,7 @@ function PermissionSettings(props) {
open={open}
onClose={cancel}
data-test={`${subject}-role-conditions-modal`}
keepMounted
>
<DialogTitle>{formatMessage('permissionSettings.title')}</DialogTitle>
@@ -65,10 +65,10 @@ function PermissionSettings(props) {
<TableHead>
<TableRow>
<TableCell component="th" />
{actions.map((action) => (
<TableCell component="th" key={action.key}>
<Typography
component="div"
variant="subtitle1"
align="center"
sx={{
@@ -89,7 +89,7 @@ function PermissionSettings(props) {
sx={{ '&:last-child td': { border: 0 } }}
>
<TableCell scope="row">
<Typography variant="subtitle2">
<Typography variant="subtitle2" component="div">
{condition.label}
</Typography>
</TableCell>
@@ -99,14 +99,13 @@ function PermissionSettings(props) {
key={`${action.key}.${condition.key}`}
align="center"
>
<Typography variant="subtitle2">
<Typography variant="subtitle2" component="div">
{action.subjects.includes(subject) && (
<ControlledCheckbox
name={`${fieldPrefix}.${action.key}.conditions.${condition.key}`}
dataTest={`${
condition.key
}-${action.key.toLowerCase()}-checkbox`}
defaultValue={defaultChecked}
disabled={
getValues(
`${fieldPrefix}.${action.key}.value`,
@@ -144,7 +143,6 @@ PermissionSettings.propTypes = {
fieldPrefix: PropTypes.string.isRequired,
subject: PropTypes.string.isRequired,
open: PropTypes.bool,
defaultChecked: PropTypes.bool,
actions: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string,

View File

@@ -12,15 +12,15 @@ import TableRow from '@mui/material/TableRow';
import Typography from '@mui/material/Typography';
import * as React from 'react';
import ControlledCheckbox from 'components/ControlledCheckbox';
import usePermissionCatalog from 'hooks/usePermissionCatalog.ee';
import PermissionSettings from './PermissionSettings.ee';
import PermissionCatalogFieldLoader from './PermissionCatalogFieldLoader';
import ActionField from './ActionField';
const PermissionCatalogField = ({
name = 'permissions',
disabled = false,
defaultChecked = false,
syncIsCreator = false,
}) => {
const { data, isLoading: isPermissionCatalogLoading } =
usePermissionCatalog();
@@ -39,6 +39,7 @@ const PermissionCatalogField = ({
{permissionCatalog?.actions.map((action) => (
<TableCell component="th" key={action.key}>
<Typography
component="div"
variant="subtitle1"
align="center"
sx={{
@@ -62,20 +63,23 @@ const PermissionCatalogField = ({
data-test={`${subject.key}-permission-row`}
>
<TableCell scope="row">
<Typography variant="subtitle2">{subject.label}</Typography>
<Typography variant="subtitle2" component="div">
{subject.label}
</Typography>
</TableCell>
{permissionCatalog?.actions.map((action) => (
<TableCell key={`${subject.key}.${action.key}`} align="center">
<Typography variant="subtitle2">
<Typography variant="subtitle2" component="div">
{action.subjects.includes(subject.key) && (
<ControlledCheckbox
<ActionField
action={action}
subject={subject}
disabled={disabled}
name={`${name}.${subject.key}.${action.key}.value`}
dataTest={`${action.key.toLowerCase()}-checkbox`}
name={name}
syncIsCreator={syncIsCreator}
/>
)}
{!action.subjects.includes(subject.key) && '-'}
</Typography>
</TableCell>
@@ -100,7 +104,6 @@ const PermissionCatalogField = ({
subject={subject.key}
actions={permissionCatalog?.actions}
conditions={permissionCatalog?.conditions}
defaultChecked={defaultChecked}
/>
</Stack>
</TableCell>
@@ -114,7 +117,7 @@ const PermissionCatalogField = ({
PermissionCatalogField.propTypes = {
name: PropTypes.string,
disabled: PropTypes.bool,
defaultChecked: PropTypes.bool,
syncIsCreator: PropTypes.bool,
};
export default PermissionCatalogField;

View File

@@ -45,3 +45,36 @@ export function getPermissions(computedPermissions) {
[],
);
}
export const getComputedPermissionsDefaultValues = (
data,
conditionsInitialValues,
) => {
if (!data) return {};
const conditions = {};
data.conditions.forEach((condition) => {
conditions[condition.key] =
conditionsInitialValues?.[condition.key] || false;
});
const result = {};
data.subjects.forEach((subject) => {
const subjectKey = subject.key;
result[subjectKey] = {};
data.actions.forEach((action) => {
const actionKey = action.key;
if (action.subjects.includes(subjectKey)) {
result[subjectKey][actionKey] = {
value: false,
conditions: { ...conditions },
};
}
});
});
return result;
};

View File

@@ -11,9 +11,13 @@ import Form from 'components/Form';
import PageTitle from 'components/PageTitle';
import TextField from 'components/TextField';
import * as URLS from 'config/urls';
import { getPermissions } from 'helpers/computePermissions.ee';
import {
getComputedPermissionsDefaultValues,
getPermissions,
} from 'helpers/computePermissions.ee';
import useFormatMessage from 'hooks/useFormatMessage';
import useAdminCreateRole from 'hooks/useAdminCreateRole';
import usePermissionCatalog from 'hooks/usePermissionCatalog.ee';
export default function CreateRole() {
const navigate = useNavigate();
@@ -21,6 +25,21 @@ export default function CreateRole() {
const enqueueSnackbar = useEnqueueSnackbar();
const { mutateAsync: createRole, isPending: isCreateRolePending } =
useAdminCreateRole();
const { data: permissionCatalogData } = usePermissionCatalog();
const defaultValues = React.useMemo(
() => ({
name: '',
description: '',
computedPermissions: getComputedPermissionsDefaultValues(
permissionCatalogData?.data,
{
isCreator: true,
},
),
}),
[permissionCatalogData],
);
const handleRoleCreation = async (roleData) => {
try {
@@ -64,7 +83,7 @@ export default function CreateRole() {
</Grid>
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 5 }}>
<Form onSubmit={handleRoleCreation}>
<Form onSubmit={handleRoleCreation} defaultValues={defaultValues}>
<Stack direction="column" gap={2}>
<TextField
required={true}
@@ -81,10 +100,7 @@ export default function CreateRole() {
data-test="description-input"
/>
<PermissionCatalogField
name="computedPermissions"
defaultChecked={true}
/>
<PermissionCatalogField name="computedPermissions" />
<LoadingButton
type="submit"

View File

@@ -5,6 +5,7 @@ import Stack from '@mui/material/Stack';
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
import * as React from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { merge } from 'lodash';
import Container from 'components/Container';
import Form from 'components/Form';
@@ -13,21 +14,25 @@ import PermissionCatalogField from 'components/PermissionCatalogField/index.ee';
import TextField from 'components/TextField';
import * as URLS from 'config/urls';
import {
getComputedPermissionsDefaultValues,
getPermissions,
getRoleWithComputedPermissions,
} from 'helpers/computePermissions.ee';
import useFormatMessage from 'hooks/useFormatMessage';
import useAdminUpdateRole from 'hooks/useAdminUpdateRole';
import useRole from 'hooks/useRole.ee';
import usePermissionCatalog from 'hooks/usePermissionCatalog.ee';
export default function EditRole() {
const formatMessage = useFormatMessage();
const navigate = useNavigate();
const { roleId } = useParams();
const { data, loading: isRoleLoading } = useRole({ roleId });
const { data: roleData, isLoading: isRoleLoading } = useRole({ roleId });
const { mutateAsync: updateRole, isPending: isUpdateRolePending } =
useAdminUpdateRole(roleId);
const role = data?.data;
const { data: permissionCatalogData } = usePermissionCatalog();
const role = roleData?.data;
const permissionCatalog = permissionCatalogData?.data;
const enqueueSnackbar = useEnqueueSnackbar();
const handleRoleUpdate = async (roleData) => {
@@ -52,7 +57,20 @@ export default function EditRole() {
}
};
const roleWithComputedPermissions = getRoleWithComputedPermissions(role);
const defaultValues = React.useMemo(() => {
const roleWithComputedPermissions = getRoleWithComputedPermissions(role);
const computedPermissionsDefaultValues =
getComputedPermissionsDefaultValues(permissionCatalog);
return {
...roleWithComputedPermissions,
computedPermissions: merge(
{},
computedPermissionsDefaultValues,
roleWithComputedPermissions.computedPermissions,
),
};
}, [role, permissionCatalog]);
return (
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
@@ -64,10 +82,7 @@ export default function EditRole() {
</Grid>
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 5 }}>
<Form
defaultValues={roleWithComputedPermissions}
onSubmit={handleRoleUpdate}
>
<Form defaultValues={defaultValues} onSubmit={handleRoleUpdate}>
<Stack direction="column" gap={2}>
{isRoleLoading && (
<>
@@ -95,12 +110,11 @@ export default function EditRole() {
/>
</>
)}
<PermissionCatalogField
name="computedPermissions"
disabled={role?.isAdmin}
syncIsCreator
/>
<LoadingButton
type="submit"
variant="contained"