diff --git a/packages/manager/.changeset/pr-11834-upcoming-features-1741795011556.md b/packages/manager/.changeset/pr-11834-upcoming-features-1741795011556.md new file mode 100644 index 00000000000..992ca971826 --- /dev/null +++ b/packages/manager/.changeset/pr-11834-upcoming-features-1741795011556.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Add functionality to support the 'Assign New Roles' drawer for a single user in IAM ([#11834](https://github.com/linode/manager/pull/11834)) diff --git a/packages/manager/src/features/IAM/Shared/utilities.ts b/packages/manager/src/features/IAM/Shared/utilities.ts index 62ad79a97b4..171e8eaeca0 100644 --- a/packages/manager/src/features/IAM/Shared/utilities.ts +++ b/packages/manager/src/features/IAM/Shared/utilities.ts @@ -429,3 +429,9 @@ export const updateUserRoles = ({ } ); }; + +export interface AssignNewRoleFormValues { + roles: { + role: RolesType | null; + }[]; +} diff --git a/packages/manager/src/features/IAM/Users/UserRoles/AssignNewRoleDrawer.tsx b/packages/manager/src/features/IAM/Users/UserRoles/AssignNewRoleDrawer.tsx index 75d8db02052..3bc9e68864e 100644 --- a/packages/manager/src/features/IAM/Users/UserRoles/AssignNewRoleDrawer.tsx +++ b/packages/manager/src/features/IAM/Users/UserRoles/AssignNewRoleDrawer.tsx @@ -1,14 +1,18 @@ -import { Autocomplete, Drawer, Typography } from '@linode/ui'; +import { ActionsPanel, Drawer, Typography } from '@linode/ui'; +import { useTheme } from '@mui/material'; import React from 'react'; +import { FormProvider, useFieldArray, useForm } from 'react-hook-form'; import { Link } from 'src/components/Link'; +import { LinkButton } from 'src/components/LinkButton'; import { NotFound } from 'src/components/NotFound'; +import { StyledLinkButtonBox } from 'src/components/SelectFirewallPanel/SelectFirewallPanel'; +import { AssignSingleRole } from 'src/features/IAM/Users/UserRoles/AssignSingleRole'; import { useAccountPermissions } from 'src/queries/iam/iam'; -import { AssignedPermissionsPanel } from '../../Shared/AssignedPermissionsPanel/AssignedPermissionsPanel'; -import { getAllRoles, getRoleByName } from '../../Shared/utilities'; +import { getAllRoles } from '../../Shared/utilities'; -import type { RolesType } from '../../Shared/utilities'; +import type { AssignNewRoleFormValues } from '../../Shared/utilities'; interface Props { onClose: () => void; @@ -16,31 +20,44 @@ interface Props { } export const AssignNewRoleDrawer = ({ onClose, open }: Props) => { - const [ - selectedOptions, - setSelectedOptions, - ] = React.useState(null); + const theme = useTheme(); - const { - data: accountPermissions, - isLoading: accountPermissionsLoading, - } = useAccountPermissions(); + const { data: accountPermissions } = useAccountPermissions(); + + const form = useForm({ + defaultValues: { + roles: [{ role: null }], + }, + }); + + const { control, handleSubmit, reset, watch } = form; + const { append, fields, remove } = useFieldArray({ + control, + name: 'roles', + }); + + // to watch changes to this value since we're conditionally rendering "Add another role" + const roles = watch('roles'); const allRoles = React.useMemo(() => { if (!accountPermissions) { return []; } - return getAllRoles(accountPermissions); }, [accountPermissions]); - // Get the selected role based on the `selectedOptions` - const selectedRole = React.useMemo(() => { - if (!selectedOptions || !accountPermissions) { - return null; - } - return getRoleByName(accountPermissions, selectedOptions.value); - }, [selectedOptions, accountPermissions]); + const onSubmit = handleSubmit(async (values: AssignNewRoleFormValues) => { + // TODO - make this really do something apart from console logging - UIE-8590 + + // const selectedRoles = values.roles.map((r) => r.role).filter(Boolean); + handleClose(); + }); + + const handleClose = () => { + reset(); + + onClose(); + }; // TODO - add a link 'Learn more" - UIE-8534 return ( @@ -50,31 +67,49 @@ export const AssignNewRoleDrawer = ({ onClose, open }: Props) => { open={open} title="Assign New Roles" > - - Select a role you want to assign to a user. Some roles require selecting - resources they should apply to. Configure the first role and continue - adding roles or save the assignment. - Learn more about roles and permissions. - - - ( -
  • - {option.label} -
  • - )} - label="Assign New Roles" - loading={accountPermissionsLoading} - onChange={(_, value) => setSelectedOptions(value)} - options={allRoles} - placeholder="Select a Role" - textFieldProps={{ hideLabel: true, noMarginTop: true }} - value={selectedOptions} - /> - - {selectedRole && ( - - )} + {' '} + +
    + + Select a role you want to assign to a user. Some roles require + selecting resources they should apply to. Configure the first role + and continue adding roles or save the assignment. + Learn more about roles and permissions. + + + {!!accountPermissions && + fields.map((field, index) => ( + remove(index)} + options={allRoles} + permissions={accountPermissions} + /> + ))} + + {/* If all roles are filled, allow them to add another */} + {roles.length > 0 && roles.every((field) => field.role) && ( + + append({ role: null })}> + Add another role + + + )} + + +
    ); }; diff --git a/packages/manager/src/features/IAM/Users/UserRoles/AssignSingleRole.tsx b/packages/manager/src/features/IAM/Users/UserRoles/AssignSingleRole.tsx new file mode 100644 index 00000000000..2b82c0e3882 --- /dev/null +++ b/packages/manager/src/features/IAM/Users/UserRoles/AssignSingleRole.tsx @@ -0,0 +1,83 @@ +import { Autocomplete, Button } from '@linode/ui'; +import Close from '@mui/icons-material/Close'; +import { Divider, useTheme } from '@mui/material'; +import Box from '@mui/material/Box'; +import React from 'react'; +import { Controller, useFormContext } from 'react-hook-form'; + +import { AssignedPermissionsPanel } from 'src/features/IAM/Shared/AssignedPermissionsPanel/AssignedPermissionsPanel'; +import { getRoleByName } from 'src/features/IAM/Shared/utilities'; + +import type { IamAccountPermissions } from '@linode/api-v4'; +import type { + AssignNewRoleFormValues, + RolesType, +} from 'src/features/IAM/Shared/utilities'; + +interface Props { + index: number; + onRemove: (idx: number) => void; + options: RolesType[]; + permissions: IamAccountPermissions; +} + +export const AssignSingleRole = ({ + index, + onRemove, + options, + permissions, +}: Props) => { + const theme = useTheme(); + + const { control } = useFormContext(); + + return ( + + + {index !== 0 && ( + + )} + + ( + <> + { + onChange(newValue); + }} + label="Assign New Roles" + options={options} + placeholder="Select a Role" + textFieldProps={{ hideLabel: true }} + value={value || null} + /> + {value && ( + + )} + + )} + control={control} + name={`roles.${index}.role`} + /> + + + + + + ); +};