From dfb0fa6a8f9fe603531ebd4345361360ce2e29e8 Mon Sep 17 00:00:00 2001 From: Jake Rosenberg Date: Fri, 15 Nov 2024 15:59:48 -0600 Subject: [PATCH] Bug/WC-122: Prevent a user from being added in multiple roles on a project. (#1452) * Prevent a user from being added in multiple roles on a project * formatting --- .../src/projects/forms/BaseProjectForm.tsx | 10 ++++++--- .../src/projects/forms/CreateProjectForm.tsx | 22 ++++++++++++++++--- .../projects/forms/_fields/AuthorSelect.tsx | 9 +++++--- .../src/projects/forms/_fields/UserSelect.tsx | 19 +++++++++++----- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/client/modules/datafiles/src/projects/forms/BaseProjectForm.tsx b/client/modules/datafiles/src/projects/forms/BaseProjectForm.tsx index b7ead8c7b2..1cf7517328 100644 --- a/client/modules/datafiles/src/projects/forms/BaseProjectForm.tsx +++ b/client/modules/datafiles/src/projects/forms/BaseProjectForm.tsx @@ -281,7 +281,11 @@ export const BaseProjectForm: React.FC<{ ]} className="inner-form-item" > - +
- +
@@ -305,7 +309,7 @@ export const BaseProjectForm: React.FC<{ initialValue={[]} className="inner-form-item" > - + diff --git a/client/modules/datafiles/src/projects/forms/CreateProjectForm.tsx b/client/modules/datafiles/src/projects/forms/CreateProjectForm.tsx index 4a7ac685df..18acee6e6c 100644 --- a/client/modules/datafiles/src/projects/forms/CreateProjectForm.tsx +++ b/client/modules/datafiles/src/projects/forms/CreateProjectForm.tsx @@ -1,5 +1,5 @@ import { Button, Form, Input } from 'antd'; -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { UserSelect, GuestMembersInput } from './_fields'; import { TProjectUser } from './_fields/UserSelect'; @@ -34,6 +34,22 @@ export const BaseProjectCreateForm: React.FC<{ ]); }, [form, user]); + const watchedPi = Form.useWatch(['pi'], form); + const watchedCoPis = Form.useWatch(['coPis'], form); + const watchedMembers = Form.useWatch(['teamMembers'], form); + const watchedGuestMembers = Form.useWatch(['guestMembers'], form); + const watchedUsers = useMemo( + () => [ + ...(watchedPi ?? []), + ...(watchedCoPis ?? []), + ...(watchedMembers ?? []), + ...(watchedGuestMembers?.filter( + (f: TProjectUser) => !!f && f.fname && f.lname && f.email && f.inst + ) ?? []), + ], + [watchedPi, watchedCoPis, watchedMembers, watchedGuestMembers] + ); + if (!user) return null; return (

- + @@ -106,7 +122,7 @@ export const BaseProjectCreateForm: React.FC<{ initialValue={[]} className="inner-form-item" > - + diff --git a/client/modules/datafiles/src/projects/forms/_fields/AuthorSelect.tsx b/client/modules/datafiles/src/projects/forms/_fields/AuthorSelect.tsx index d6dee50c30..541e16784e 100644 --- a/client/modules/datafiles/src/projects/forms/_fields/AuthorSelect.tsx +++ b/client/modules/datafiles/src/projects/forms/_fields/AuthorSelect.tsx @@ -16,7 +16,8 @@ export const AuthorSelect: React.FC<{ (u) => (u.email || '') === (a.email || '') && u.fname === a.fname && - u.lname === a.lname + u.lname === a.lname && + (u.role || '') === (a.role || '') ) ) .filter((u) => !!u); @@ -28,7 +29,8 @@ export const AuthorSelect: React.FC<{ (a) => (u.email || '') === (a.email || '') && u.fname === a.fname && - u.lname === a.lname + u.lname === a.lname && + (u.role || '') === (a.role || '') ) ) .filter((u) => !!u); @@ -60,7 +62,8 @@ export const AuthorSelect: React.FC<{ (v) => (user?.email || '') === (v.email || '') && user?.fname === v.fname && - user?.lname === v.lname + user?.lname === v.lname && + (user?.role || '') === (v?.role || '') ) ) .map((v) => JSON.stringify(v) ?? [])} diff --git a/client/modules/datafiles/src/projects/forms/_fields/UserSelect.tsx b/client/modules/datafiles/src/projects/forms/_fields/UserSelect.tsx index b1958ba8d8..445c685111 100644 --- a/client/modules/datafiles/src/projects/forms/_fields/UserSelect.tsx +++ b/client/modules/datafiles/src/projects/forms/_fields/UserSelect.tsx @@ -18,7 +18,12 @@ export const UserSelect: React.FC<{ userRole?: string; maxCount?: number; disabled?: boolean; -}> = ({ value, onChange, id, userRole, maxCount, disabled }) => { + existingUsers?: TProjectUser[]; +}> = ({ value, onChange, id, userRole, maxCount, disabled, existingUsers }) => { + const existingUsernames = useMemo( + () => (existingUsers ?? []).map((u) => u.username), + [existingUsers] + ); const initialOptions: SelectProps['options'] = useMemo( () => value?.map((u) => ({ @@ -46,15 +51,17 @@ export const UserSelect: React.FC<{ signal: controller.signal, }) .then((resp) => - resp.data.result.map((u) => ({ - label: `${u.fname} ${u.lname} (${u.email})`, - value: JSON.stringify({ ...u, role: userRole }), - })) + resp.data.result + .filter((u) => !existingUsernames.includes(u.username)) + .map((u) => ({ + label: `${u.fname} ${u.lname} (${u.email})`, + value: JSON.stringify({ ...u, role: userRole }), + })) ) .then((opts) => setData(opts)) .catch((_) => setData([])); return () => controller.abort(); - }, [debouncedSearchTerm, setData, userRole]); + }, [debouncedSearchTerm, setData, userRole, existingUsernames]); const changeCallback = (newValue: string[]) => { onChange && onChange(newValue.map((v) => JSON.parse(v)));