1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-10-13 11:17:26 +02:00
unleash.unleash/frontend/src/component/admin/roles/RoleModal/RoleModal.tsx
Nuno Góis f348acb3b9
feat: improve role form validation (#5548)
https://linear.app/unleash/issue/2-1717/improve-the-ux-when-all-the-required-fields-are-not-filled-in

Improves role form validation behavior.
We may want to look into a form validation library, like
[react-hook-form](https://react-hook-form.com/), for future
implementations.
2023-12-05 12:39:30 +00:00

170 lines
5.6 KiB
TypeScript

import { Button, styled } from '@mui/material';
import { SidebarModal } from 'component/common/SidebarModal/SidebarModal';
import { useRoleForm } from '../RoleForm/useRoleForm';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import FormTemplate from 'component/common/FormTemplate/FormTemplate';
import { RoleForm } from '../RoleForm/RoleForm';
import { useRoles } from 'hooks/api/getters/useRoles/useRoles';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
import { FormEvent, useEffect } from 'react';
import { useRolesApi } from 'hooks/api/actions/useRolesApi/useRolesApi';
import { useRole } from 'hooks/api/getters/useRole/useRole';
import { PredefinedRoleType } from 'interfaces/role';
import { ROOT_ROLE_TYPE } from '@server/util/constants';
const StyledForm = styled('form')(() => ({
display: 'flex',
flexDirection: 'column',
height: '100%',
}));
const StyledButtonContainer = styled('div')(({ theme }) => ({
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
paddingTop: theme.spacing(4),
}));
const StyledCancelButton = styled(Button)(({ theme }) => ({
marginLeft: theme.spacing(3),
}));
interface IRoleModalProps {
type?: PredefinedRoleType;
roleId?: number;
open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
}
export const RoleModal = ({
type = ROOT_ROLE_TYPE,
roleId,
open,
setOpen,
}: IRoleModalProps) => {
const { role, refetch: refetchRole } = useRole(roleId?.toString());
const {
name,
setName,
validateName,
description,
setDescription,
validateDescription,
checkedPermissions,
setCheckedPermissions,
getRolePayload,
errors,
showErrors,
validate,
reload: reloadForm,
} = useRoleForm(role?.name, role?.description, role?.permissions);
const { refetch: refetchRoles } = useRoles();
const { addRole, updateRole, loading } = useRolesApi();
const { setToastData, setToastApiError } = useToast();
const { uiConfig } = useUiConfig();
const editing = role !== undefined;
const payload = getRolePayload(type);
const formatApiCode = () => {
return `curl --location --request ${editing ? 'PUT' : 'POST'} '${
uiConfig.unleashUrl
}/api/admin/roles${editing ? `/${role.id}` : ''}' \\
--header 'Authorization: INSERT_API_KEY' \\
--header 'Content-Type: application/json' \\
--data-raw '${JSON.stringify(payload, undefined, 2)}'`;
};
const refetch = () => {
refetchRoles();
refetchRole();
};
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!validate()) return;
try {
if (editing) {
await updateRole(role.id, payload);
} else {
await addRole(payload);
}
setToastData({
title: `Role ${editing ? 'updated' : 'added'} successfully`,
type: 'success',
});
refetch();
setOpen(false);
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};
useEffect(() => {
reloadForm();
}, [open]);
const titleCasedType = type[0].toUpperCase() + type.slice(1);
return (
<SidebarModal
open={open}
onClose={() => {
setOpen(false);
}}
label={editing ? `Edit ${type} role` : `New ${type} role`}
>
<FormTemplate
loading={loading}
modal
title={editing ? `Edit ${type} role` : `New ${type} role`}
description={`${titleCasedType} roles allow you to control access to ${type} resources. Besides the built-in ${type} roles, you can create and manage custom ${type} roles to fit your needs.`}
documentationLink={`https://docs.getunleash.io/reference/rbac${
type === ROOT_ROLE_TYPE
? '#custom-root-roles'
: '#custom-project-roles'
}`}
documentationLinkLabel='Roles documentation'
formatApiCode={formatApiCode}
>
<StyledForm onSubmit={onSubmit}>
<RoleForm
type={type}
name={name}
setName={setName}
validateName={validateName}
description={description}
setDescription={setDescription}
validateDescription={validateDescription}
checkedPermissions={checkedPermissions}
setCheckedPermissions={setCheckedPermissions}
errors={errors}
showErrors={showErrors}
/>
<StyledButtonContainer>
<Button
type='submit'
variant='contained'
color='primary'
>
{editing ? 'Save' : 'Add'} role
</Button>
<StyledCancelButton
onClick={() => {
setOpen(false);
}}
>
Cancel
</StyledCancelButton>
</StyledButtonContainer>
</StyledForm>
</FormTemplate>
</SidebarModal>
);
};