From 73b1ea2649727dfc1a02aae960640d7272830d80 Mon Sep 17 00:00:00 2001 From: Ben Lerner Date: Fri, 24 Sep 2021 16:54:48 -0400 Subject: [PATCH] initial attempts at a more visual select-menu to choose users to impersonate. will likely be useful as a standalone component, and used in other parts of the UI --- app/javascript/components/common/helpers.ts | 6 +-- .../components/workflows/home/index.tsx | 52 ++++++++++++------- .../workflows/professor/courses/show.tsx | 13 ++--- 3 files changed, 40 insertions(+), 31 deletions(-) diff --git a/app/javascript/components/common/helpers.ts b/app/javascript/components/common/helpers.ts index fd876de86..396166028 100644 --- a/app/javascript/components/common/helpers.ts +++ b/app/javascript/components/common/helpers.ts @@ -55,12 +55,12 @@ export function pluralize(number: number, singular: string, plural: string): str return `${number} ${plural}`; } -export type SelectOption = { +export type SelectOption = { label: string; value: T; -}; +} & Extra; -export type SelectOptions = SelectOption[]; +export type SelectOptions = SelectOption[]; export type MutationReturn = [ MutateWithVariables, MutationState diff --git a/app/javascript/components/workflows/home/index.tsx b/app/javascript/components/workflows/home/index.tsx index 66c53c0bd..d75d17b64 100644 --- a/app/javascript/components/workflows/home/index.tsx +++ b/app/javascript/components/workflows/home/index.tsx @@ -8,7 +8,7 @@ import { useMutation, } from 'relay-hooks'; import { RenderError } from '@hourglass/common/boundary'; -import Select, { GroupedOptionsType } from 'react-select'; +import Select, { GroupedOptionsType, OptionProps, components } from 'react-select'; import { Button, Container, @@ -278,17 +278,43 @@ const ShowProfRegs: React.FC<{ ); }; -export type ImpersonateVal = SelectOption +export type ImpersonateVal = SelectOption + +const ImpersonateUserOption: React.FC> = (props) => { + const { + data, + } = props; + const value = data as ImpersonateVal; + return ( +
+ {( + // eslint-disable-next-line react/jsx-props-no-spreading + + + + {value.imageUrl ? ( + + ) : ( + + )} + + + {value.label} + + + + )} +
+ ); +}; export const ImpersonateUser: React.FC<{ userOptions: ImpersonateVal[] | GroupedOptionsType; - userIdToImageMap: { [key: string]: string; }; courseId?: ImpersonateUserInput['courseId']; }> = (props) => { const { userOptions, courseId, - userIdToImageMap, } = props; const { alert } = useContext(AlertContext); const [impersonate, { loading }] = useMutation( @@ -322,8 +348,9 @@ export const ImpersonateUser: React.FC<{ placeholder="Select a user to impersonate..." isDisabled={loading} options={userOptions} + components={{ Option: ImpersonateUserOption }} formatOptionLabel={(option) => { - const userHasImage = option.value in userIdToImageMap; + const userHasImage = !!option.imageUrl; return ( {userHasImage ? ( - {option.label} + {option.label} ) : ( No image for that user. )} @@ -343,11 +370,6 @@ export const ImpersonateUser: React.FC<{ )} > - {userHasImage ? ( - {option.label} - ) : ( - - )} {option.label} @@ -403,20 +425,14 @@ const Admin: React.FC = () => { if (!res.data) { return

Loading...

; } - const userIdToImageMap = {}; - res.data.users.forEach((user) => { - if (user.imageUrl) { - userIdToImageMap[user.id] = user.imageUrl; - } - }); const userOptions: ImpersonateVal[] = res.data.users.map((user) => ({ label: `${user.displayName} (${user.username})`, value: user.id, + imageUrl: user.imageUrl, })); return ( ); }; diff --git a/app/javascript/components/workflows/professor/courses/show.tsx b/app/javascript/components/workflows/professor/courses/show.tsx index 8f54104f3..f32c05bc1 100644 --- a/app/javascript/components/workflows/professor/courses/show.tsx +++ b/app/javascript/components/workflows/professor/courses/show.tsx @@ -86,21 +86,13 @@ const ShowCourse: React.FC = () => { if (!res.data) { return

Loading...

; } - const userIdToImageMap = {}; - const allUsers = res.data.course.students - .concat(res.data.course.staff) - .concat(res.data.course.professors); - allUsers.forEach((user) => { - if (user.imageUrl) { - userIdToImageMap[user.id] = user.imageUrl; - } - }); const userOptions: GroupedOptionsType = [ { label: 'Students', options: res.data.course.students.map((user) => ({ label: `${user.displayName} (${user.username})`, value: user.id, + imageUrl: user.imageUrl, })), }, { @@ -108,6 +100,7 @@ const ShowCourse: React.FC = () => { options: res.data.course.staff.map((user) => ({ label: `${user.displayName} (${user.username})`, value: user.id, + imageUrl: user.imageUrl, })), }, { @@ -115,6 +108,7 @@ const ShowCourse: React.FC = () => { options: res.data.course.professors.map((user) => ({ label: `${user.displayName} (${user.username})`, value: user.id, + imageUrl: user.imageUrl, })), }, ]; @@ -145,7 +139,6 @@ const ShowCourse: React.FC = () => {