From ca5299c4af44337f30b2cfb2df6221e0b66207f2 Mon Sep 17 00:00:00 2001 From: Alon Peretz <8467965+alonp99@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:51:26 +0200 Subject: [PATCH] feat(assign-dropdown): add role filtering for assignees (#2860) * feat(assign-dropdown): add role filtering for assignees - Introduce filterUsersByRole utility to exclude assignees by roles - Update AssignDropdown and AlertsAssignDropdown to utilize filtering - Set default excluded role to 'viewer' in relevant components (Your role filtering is so selective, it's like a bouncer at a VIP club) * Update filter-users-by-role.ts * feat(users): enhance user filtering and validation - Add check for users input to ensure it is an array - Update validation schema to include roles as an optional array (your input validation is so lax, it might as well accept a pizza order) * refactor(AssignDropdown): simplify assignee type handling - Change assignee prop type from TAssignee to TAuthenticatedUser - Streamline filtering logic in both AssignDropdown and AlertsAssignDropdown (Your code's type definitions are so convoluted, they could use a user manual) * Update apps/backoffice-v2/src/domains/users/utils/filter-users-by-role.ts Co-authored-by: Tomer Shvadron --------- Co-authored-by: Tomer Shvadron --- .../atoms/AssignDropdown/AssignDropdown.tsx | 17 +++++++++++----- .../users/utils/filter-users-by-role.ts | 20 +++++++++++++++++++ .../src/domains/users/validation-schemas.ts | 1 + .../Entity/components/Case/Case.Actions.tsx | 1 + .../AlertsAssignDropdown.tsx | 16 ++++++++++----- .../src/user/user.controller.internal.ts | 1 + 6 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 apps/backoffice-v2/src/domains/users/utils/filter-users-by-role.ts diff --git a/apps/backoffice-v2/src/common/components/atoms/AssignDropdown/AssignDropdown.tsx b/apps/backoffice-v2/src/common/components/atoms/AssignDropdown/AssignDropdown.tsx index 0c1f9fc92f..d0f1400d6a 100644 --- a/apps/backoffice-v2/src/common/components/atoms/AssignDropdown/AssignDropdown.tsx +++ b/apps/backoffice-v2/src/common/components/atoms/AssignDropdown/AssignDropdown.tsx @@ -1,4 +1,4 @@ -import React, { FunctionComponent, useMemo } from 'react'; +import { FunctionComponent, useMemo } from 'react'; import { CheckSvg, DoubleCaretSvg, UnassignedAvatarSvg } from '../icons'; import { DropdownMenu } from '../../molecules/DropdownMenu/DropdownMenu'; @@ -7,15 +7,17 @@ import { DropdownMenuTrigger } from '../../molecules/DropdownMenu/DropdownMenu.T import { DropdownMenuContent } from '../../molecules/DropdownMenu/DropdownMenu.Content'; import { UserAvatar } from '../UserAvatar/UserAvatar'; import { TAuthenticatedUser } from '../../../../domains/auth/types'; +import { filterUsersByRole, TUserRole } from '@/domains/users/utils/filter-users-by-role'; export type TAssignee = Pick; interface IAssignDropdownProps { - assignees: TAssignee[]; + assignees: TAuthenticatedUser[]; assignedUser?: TAssignee; authenticatedUserId: string; onAssigneeSelect: (id: string) => void; isDisabled?: boolean; + excludedRoles?: TUserRole[]; } export const AssignDropdown: FunctionComponent = ({ @@ -24,16 +26,21 @@ export const AssignDropdown: FunctionComponent = ({ onAssigneeSelect, authenticatedUserId, isDisabled, + excludedRoles = [], }) => { + const filteredAssignees = useMemo( + () => filterUsersByRole(assignees, excludedRoles), + [assignees, excludedRoles], + ); + const sortedAssignees = useMemo( () => - // Sort assignees so that the authenticated user is always first - assignees + filteredAssignees ?.slice() ?.sort((a, b) => a?.id === authenticatedUserId ? -1 : b?.id === authenticatedUserId ? 1 : 0, ), - [assignees, authenticatedUserId], + [filteredAssignees, authenticatedUserId], ); return ( diff --git a/apps/backoffice-v2/src/domains/users/utils/filter-users-by-role.ts b/apps/backoffice-v2/src/domains/users/utils/filter-users-by-role.ts new file mode 100644 index 0000000000..fa2c6003f5 --- /dev/null +++ b/apps/backoffice-v2/src/domains/users/utils/filter-users-by-role.ts @@ -0,0 +1,20 @@ +import { TAuthenticatedUser } from '@/domains/auth/types'; + +export type TUserRole = 'viewer'; + +export const filterUsersByRole = ( + users: Array>, + excludedRoles: TUserRole[], +) => { + if (!Array.isArray(users)) return []; + + return users.filter(user => { + if (!user) return false; + + if (!('roles' in user) || !Array.isArray(user.roles)) { + return true; + } + + return !excludedRoles.some(role => user.roles.includes(role)); + }); +}; diff --git a/apps/backoffice-v2/src/domains/users/validation-schemas.ts b/apps/backoffice-v2/src/domains/users/validation-schemas.ts index 315ed8e620..03f59cecc4 100644 --- a/apps/backoffice-v2/src/domains/users/validation-schemas.ts +++ b/apps/backoffice-v2/src/domains/users/validation-schemas.ts @@ -12,6 +12,7 @@ export const UsersListSchema = z avatarUrl: z.string().nullable().optional(), createdAt: z.string(), updatedAt: z.string(), + roles: z.array(z.union([z.enum(['viewer', 'admin']), z.string()])).optional(), }).transform(({ firstName, lastName, ...rest }) => ({ ...rest, firstName, diff --git a/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx b/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx index 312bdcc47f..f4db95f923 100644 --- a/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx +++ b/apps/backoffice-v2/src/pages/Entity/components/Case/Case.Actions.tsx @@ -58,6 +58,7 @@ export const Actions: FunctionComponent = ({ }} authenticatedUserId={authenticatedUser?.id} isDisabled={isWorkflowCompleted} + excludedRoles={['viewer']} /> diff --git a/apps/backoffice-v2/src/pages/TransactionMonitoringAlerts/components/AlertsAssignDropdown/AlertsAssignDropdown.tsx b/apps/backoffice-v2/src/pages/TransactionMonitoringAlerts/components/AlertsAssignDropdown/AlertsAssignDropdown.tsx index 2a016a4e8d..879545fe59 100644 --- a/apps/backoffice-v2/src/pages/TransactionMonitoringAlerts/components/AlertsAssignDropdown/AlertsAssignDropdown.tsx +++ b/apps/backoffice-v2/src/pages/TransactionMonitoringAlerts/components/AlertsAssignDropdown/AlertsAssignDropdown.tsx @@ -1,24 +1,30 @@ -import React, { FunctionComponent, useMemo } from 'react'; +import { FunctionComponent, useMemo } from 'react'; import { TUsers } from '@/domains/users/types'; import { DoubleCaretSvg, UnassignedAvatarSvg } from '@/common/components/atoms/icons'; import { UserAvatar } from '@/common/components/atoms/UserAvatar/UserAvatar'; import { Dropdown } from '@/common/components/molecules/Dropdown/Dropdown'; +import { filterUsersByRole, TUserRole } from '@/domains/users/utils/filter-users-by-role'; export const AlertsAssignDropdown: FunctionComponent<{ assignees: TUsers; authenticatedUserId: string; isDisabled: boolean; onAssigneeSelect: (id: string | null, isAssignedToMe: boolean) => () => void; -}> = ({ assignees, authenticatedUserId, isDisabled, onAssigneeSelect }) => { + excludedRoles?: TUserRole[]; +}> = ({ assignees, authenticatedUserId, isDisabled, onAssigneeSelect, excludedRoles = [] }) => { + const filteredAssignees = useMemo( + () => filterUsersByRole(assignees, excludedRoles), + [assignees, excludedRoles], + ); + const sortedAssignees = useMemo( () => - // Sort assignees so that the authenticated user is always first - assignees + filteredAssignees ?.slice() ?.sort((a, b) => a?.id === authenticatedUserId ? -1 : b?.id === authenticatedUserId ? 1 : 0, ), - [assignees, authenticatedUserId], + [filteredAssignees, authenticatedUserId], ); const options = useMemo(() => { diff --git a/services/workflows-service/src/user/user.controller.internal.ts b/services/workflows-service/src/user/user.controller.internal.ts index 9461dad36c..bbde32b608 100644 --- a/services/workflows-service/src/user/user.controller.internal.ts +++ b/services/workflows-service/src/user/user.controller.internal.ts @@ -36,6 +36,7 @@ export class UserControllerInternal { avatarUrl: true, updatedAt: true, createdAt: true, + roles: true, }, }, projectId ? [projectId] : projectIds,