Merge pull request #2124 from automatisch/AUT-1232
fix: use correct default values when editing a role
This commit is contained in:
@@ -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;
|
@@ -25,7 +25,6 @@ function PermissionSettings(props) {
|
|||||||
subject,
|
subject,
|
||||||
actions,
|
actions,
|
||||||
conditions,
|
conditions,
|
||||||
defaultChecked,
|
|
||||||
} = props;
|
} = props;
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const { getValues, resetField } = useFormContext();
|
const { getValues, resetField } = useFormContext();
|
||||||
@@ -34,7 +33,7 @@ function PermissionSettings(props) {
|
|||||||
for (const action of actions) {
|
for (const action of actions) {
|
||||||
for (const condition of conditions) {
|
for (const condition of conditions) {
|
||||||
const fieldName = `${fieldPrefix}.${action.key}.conditions.${condition.key}`;
|
const fieldName = `${fieldPrefix}.${action.key}.conditions.${condition.key}`;
|
||||||
resetField(fieldName);
|
resetField(fieldName, { keepTouched: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClose();
|
onClose();
|
||||||
@@ -45,7 +44,7 @@ function PermissionSettings(props) {
|
|||||||
for (const condition of conditions) {
|
for (const condition of conditions) {
|
||||||
const fieldName = `${fieldPrefix}.${action.key}.conditions.${condition.key}`;
|
const fieldName = `${fieldPrefix}.${action.key}.conditions.${condition.key}`;
|
||||||
const value = getValues(fieldName);
|
const value = getValues(fieldName);
|
||||||
resetField(fieldName, { defaultValue: value });
|
resetField(fieldName, { defaultValue: value, keepTouched: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClose();
|
onClose();
|
||||||
@@ -56,6 +55,7 @@ function PermissionSettings(props) {
|
|||||||
open={open}
|
open={open}
|
||||||
onClose={cancel}
|
onClose={cancel}
|
||||||
data-test={`${subject}-role-conditions-modal`}
|
data-test={`${subject}-role-conditions-modal`}
|
||||||
|
keepMounted
|
||||||
>
|
>
|
||||||
<DialogTitle>{formatMessage('permissionSettings.title')}</DialogTitle>
|
<DialogTitle>{formatMessage('permissionSettings.title')}</DialogTitle>
|
||||||
|
|
||||||
@@ -65,10 +65,10 @@ function PermissionSettings(props) {
|
|||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell component="th" />
|
<TableCell component="th" />
|
||||||
|
|
||||||
{actions.map((action) => (
|
{actions.map((action) => (
|
||||||
<TableCell component="th" key={action.key}>
|
<TableCell component="th" key={action.key}>
|
||||||
<Typography
|
<Typography
|
||||||
|
component="div"
|
||||||
variant="subtitle1"
|
variant="subtitle1"
|
||||||
align="center"
|
align="center"
|
||||||
sx={{
|
sx={{
|
||||||
@@ -89,7 +89,7 @@ function PermissionSettings(props) {
|
|||||||
sx={{ '&:last-child td': { border: 0 } }}
|
sx={{ '&:last-child td': { border: 0 } }}
|
||||||
>
|
>
|
||||||
<TableCell scope="row">
|
<TableCell scope="row">
|
||||||
<Typography variant="subtitle2">
|
<Typography variant="subtitle2" component="div">
|
||||||
{condition.label}
|
{condition.label}
|
||||||
</Typography>
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -99,14 +99,13 @@ function PermissionSettings(props) {
|
|||||||
key={`${action.key}.${condition.key}`}
|
key={`${action.key}.${condition.key}`}
|
||||||
align="center"
|
align="center"
|
||||||
>
|
>
|
||||||
<Typography variant="subtitle2">
|
<Typography variant="subtitle2" component="div">
|
||||||
{action.subjects.includes(subject) && (
|
{action.subjects.includes(subject) && (
|
||||||
<ControlledCheckbox
|
<ControlledCheckbox
|
||||||
name={`${fieldPrefix}.${action.key}.conditions.${condition.key}`}
|
name={`${fieldPrefix}.${action.key}.conditions.${condition.key}`}
|
||||||
dataTest={`${
|
dataTest={`${
|
||||||
condition.key
|
condition.key
|
||||||
}-${action.key.toLowerCase()}-checkbox`}
|
}-${action.key.toLowerCase()}-checkbox`}
|
||||||
defaultValue={defaultChecked}
|
|
||||||
disabled={
|
disabled={
|
||||||
getValues(
|
getValues(
|
||||||
`${fieldPrefix}.${action.key}.value`,
|
`${fieldPrefix}.${action.key}.value`,
|
||||||
@@ -144,7 +143,6 @@ PermissionSettings.propTypes = {
|
|||||||
fieldPrefix: PropTypes.string.isRequired,
|
fieldPrefix: PropTypes.string.isRequired,
|
||||||
subject: PropTypes.string.isRequired,
|
subject: PropTypes.string.isRequired,
|
||||||
open: PropTypes.bool,
|
open: PropTypes.bool,
|
||||||
defaultChecked: PropTypes.bool,
|
|
||||||
actions: PropTypes.arrayOf(
|
actions: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
|
@@ -12,15 +12,15 @@ import TableRow from '@mui/material/TableRow';
|
|||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import ControlledCheckbox from 'components/ControlledCheckbox';
|
|
||||||
import usePermissionCatalog from 'hooks/usePermissionCatalog.ee';
|
import usePermissionCatalog from 'hooks/usePermissionCatalog.ee';
|
||||||
import PermissionSettings from './PermissionSettings.ee';
|
import PermissionSettings from './PermissionSettings.ee';
|
||||||
import PermissionCatalogFieldLoader from './PermissionCatalogFieldLoader';
|
import PermissionCatalogFieldLoader from './PermissionCatalogFieldLoader';
|
||||||
|
import ActionField from './ActionField';
|
||||||
|
|
||||||
const PermissionCatalogField = ({
|
const PermissionCatalogField = ({
|
||||||
name = 'permissions',
|
name = 'permissions',
|
||||||
disabled = false,
|
disabled = false,
|
||||||
defaultChecked = false,
|
syncIsCreator = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { data, isLoading: isPermissionCatalogLoading } =
|
const { data, isLoading: isPermissionCatalogLoading } =
|
||||||
usePermissionCatalog();
|
usePermissionCatalog();
|
||||||
@@ -39,6 +39,7 @@ const PermissionCatalogField = ({
|
|||||||
{permissionCatalog?.actions.map((action) => (
|
{permissionCatalog?.actions.map((action) => (
|
||||||
<TableCell component="th" key={action.key}>
|
<TableCell component="th" key={action.key}>
|
||||||
<Typography
|
<Typography
|
||||||
|
component="div"
|
||||||
variant="subtitle1"
|
variant="subtitle1"
|
||||||
align="center"
|
align="center"
|
||||||
sx={{
|
sx={{
|
||||||
@@ -62,20 +63,23 @@ const PermissionCatalogField = ({
|
|||||||
data-test={`${subject.key}-permission-row`}
|
data-test={`${subject.key}-permission-row`}
|
||||||
>
|
>
|
||||||
<TableCell scope="row">
|
<TableCell scope="row">
|
||||||
<Typography variant="subtitle2">{subject.label}</Typography>
|
<Typography variant="subtitle2" component="div">
|
||||||
|
{subject.label}
|
||||||
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
{permissionCatalog?.actions.map((action) => (
|
{permissionCatalog?.actions.map((action) => (
|
||||||
<TableCell key={`${subject.key}.${action.key}`} align="center">
|
<TableCell key={`${subject.key}.${action.key}`} align="center">
|
||||||
<Typography variant="subtitle2">
|
<Typography variant="subtitle2" component="div">
|
||||||
{action.subjects.includes(subject.key) && (
|
{action.subjects.includes(subject.key) && (
|
||||||
<ControlledCheckbox
|
<ActionField
|
||||||
|
action={action}
|
||||||
|
subject={subject}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
name={`${name}.${subject.key}.${action.key}.value`}
|
name={name}
|
||||||
dataTest={`${action.key.toLowerCase()}-checkbox`}
|
syncIsCreator={syncIsCreator}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!action.subjects.includes(subject.key) && '-'}
|
{!action.subjects.includes(subject.key) && '-'}
|
||||||
</Typography>
|
</Typography>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -100,7 +104,6 @@ const PermissionCatalogField = ({
|
|||||||
subject={subject.key}
|
subject={subject.key}
|
||||||
actions={permissionCatalog?.actions}
|
actions={permissionCatalog?.actions}
|
||||||
conditions={permissionCatalog?.conditions}
|
conditions={permissionCatalog?.conditions}
|
||||||
defaultChecked={defaultChecked}
|
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
@@ -114,7 +117,7 @@ const PermissionCatalogField = ({
|
|||||||
PermissionCatalogField.propTypes = {
|
PermissionCatalogField.propTypes = {
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
defaultChecked: PropTypes.bool,
|
syncIsCreator: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PermissionCatalogField;
|
export default PermissionCatalogField;
|
||||||
|
@@ -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;
|
||||||
|
};
|
||||||
|
@@ -11,9 +11,13 @@ import Form from 'components/Form';
|
|||||||
import PageTitle from 'components/PageTitle';
|
import PageTitle from 'components/PageTitle';
|
||||||
import TextField from 'components/TextField';
|
import TextField from 'components/TextField';
|
||||||
import * as URLS from 'config/urls';
|
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 useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useAdminCreateRole from 'hooks/useAdminCreateRole';
|
import useAdminCreateRole from 'hooks/useAdminCreateRole';
|
||||||
|
import usePermissionCatalog from 'hooks/usePermissionCatalog.ee';
|
||||||
|
|
||||||
export default function CreateRole() {
|
export default function CreateRole() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -21,6 +25,21 @@ export default function CreateRole() {
|
|||||||
const enqueueSnackbar = useEnqueueSnackbar();
|
const enqueueSnackbar = useEnqueueSnackbar();
|
||||||
const { mutateAsync: createRole, isPending: isCreateRolePending } =
|
const { mutateAsync: createRole, isPending: isCreateRolePending } =
|
||||||
useAdminCreateRole();
|
useAdminCreateRole();
|
||||||
|
const { data: permissionCatalogData } = usePermissionCatalog();
|
||||||
|
|
||||||
|
const defaultValues = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
computedPermissions: getComputedPermissionsDefaultValues(
|
||||||
|
permissionCatalogData?.data,
|
||||||
|
{
|
||||||
|
isCreator: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
[permissionCatalogData],
|
||||||
|
);
|
||||||
|
|
||||||
const handleRoleCreation = async (roleData) => {
|
const handleRoleCreation = async (roleData) => {
|
||||||
try {
|
try {
|
||||||
@@ -64,7 +83,7 @@ export default function CreateRole() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 5 }}>
|
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 5 }}>
|
||||||
<Form onSubmit={handleRoleCreation}>
|
<Form onSubmit={handleRoleCreation} defaultValues={defaultValues}>
|
||||||
<Stack direction="column" gap={2}>
|
<Stack direction="column" gap={2}>
|
||||||
<TextField
|
<TextField
|
||||||
required={true}
|
required={true}
|
||||||
@@ -81,10 +100,7 @@ export default function CreateRole() {
|
|||||||
data-test="description-input"
|
data-test="description-input"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PermissionCatalogField
|
<PermissionCatalogField name="computedPermissions" />
|
||||||
name="computedPermissions"
|
|
||||||
defaultChecked={true}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@@ -5,6 +5,7 @@ import Stack from '@mui/material/Stack';
|
|||||||
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
import { merge } from 'lodash';
|
||||||
|
|
||||||
import Container from 'components/Container';
|
import Container from 'components/Container';
|
||||||
import Form from 'components/Form';
|
import Form from 'components/Form';
|
||||||
@@ -13,21 +14,25 @@ import PermissionCatalogField from 'components/PermissionCatalogField/index.ee';
|
|||||||
import TextField from 'components/TextField';
|
import TextField from 'components/TextField';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
import {
|
import {
|
||||||
|
getComputedPermissionsDefaultValues,
|
||||||
getPermissions,
|
getPermissions,
|
||||||
getRoleWithComputedPermissions,
|
getRoleWithComputedPermissions,
|
||||||
} from 'helpers/computePermissions.ee';
|
} from 'helpers/computePermissions.ee';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import useAdminUpdateRole from 'hooks/useAdminUpdateRole';
|
import useAdminUpdateRole from 'hooks/useAdminUpdateRole';
|
||||||
import useRole from 'hooks/useRole.ee';
|
import useRole from 'hooks/useRole.ee';
|
||||||
|
import usePermissionCatalog from 'hooks/usePermissionCatalog.ee';
|
||||||
|
|
||||||
export default function EditRole() {
|
export default function EditRole() {
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { roleId } = useParams();
|
const { roleId } = useParams();
|
||||||
const { data, loading: isRoleLoading } = useRole({ roleId });
|
const { data: roleData, isLoading: isRoleLoading } = useRole({ roleId });
|
||||||
const { mutateAsync: updateRole, isPending: isUpdateRolePending } =
|
const { mutateAsync: updateRole, isPending: isUpdateRolePending } =
|
||||||
useAdminUpdateRole(roleId);
|
useAdminUpdateRole(roleId);
|
||||||
const role = data?.data;
|
const { data: permissionCatalogData } = usePermissionCatalog();
|
||||||
|
const role = roleData?.data;
|
||||||
|
const permissionCatalog = permissionCatalogData?.data;
|
||||||
const enqueueSnackbar = useEnqueueSnackbar();
|
const enqueueSnackbar = useEnqueueSnackbar();
|
||||||
|
|
||||||
const handleRoleUpdate = async (roleData) => {
|
const handleRoleUpdate = async (roleData) => {
|
||||||
@@ -52,7 +57,20 @@ export default function EditRole() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultValues = React.useMemo(() => {
|
||||||
const roleWithComputedPermissions = getRoleWithComputedPermissions(role);
|
const roleWithComputedPermissions = getRoleWithComputedPermissions(role);
|
||||||
|
const computedPermissionsDefaultValues =
|
||||||
|
getComputedPermissionsDefaultValues(permissionCatalog);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...roleWithComputedPermissions,
|
||||||
|
computedPermissions: merge(
|
||||||
|
{},
|
||||||
|
computedPermissionsDefaultValues,
|
||||||
|
roleWithComputedPermissions.computedPermissions,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}, [role, permissionCatalog]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
<Container sx={{ py: 3, display: 'flex', justifyContent: 'center' }}>
|
||||||
@@ -64,10 +82,7 @@ export default function EditRole() {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 5 }}>
|
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 5 }}>
|
||||||
<Form
|
<Form defaultValues={defaultValues} onSubmit={handleRoleUpdate}>
|
||||||
defaultValues={roleWithComputedPermissions}
|
|
||||||
onSubmit={handleRoleUpdate}
|
|
||||||
>
|
|
||||||
<Stack direction="column" gap={2}>
|
<Stack direction="column" gap={2}>
|
||||||
{isRoleLoading && (
|
{isRoleLoading && (
|
||||||
<>
|
<>
|
||||||
@@ -95,12 +110,11 @@ export default function EditRole() {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<PermissionCatalogField
|
<PermissionCatalogField
|
||||||
name="computedPermissions"
|
name="computedPermissions"
|
||||||
disabled={role?.isAdmin}
|
disabled={role?.isAdmin}
|
||||||
|
syncIsCreator
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
|
Reference in New Issue
Block a user