improvement: use filterable multi-select for add users to project role select

This commit is contained in:
Scott Wilson
2024-11-28 12:58:03 -08:00
parent 5495ffd78e
commit 9c03144f19
@@ -2,24 +2,11 @@ import { useMemo } from "react";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import Link from "next/link"; import Link from "next/link";
import { faCheckCircle, faChevronDown } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { twMerge } from "tailwind-merge";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { import { Button, FilterableSelect, FormControl, Modal, ModalContent } from "@app/components/v2";
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
FilterableSelect,
FormControl,
Modal,
ModalContent
} from "@app/components/v2";
import { useOrganization, useWorkspace } from "@app/context"; import { useOrganization, useWorkspace } from "@app/context";
import { import {
useAddUsersToOrg, useAddUsersToOrg,
@@ -33,7 +20,7 @@ import { UsePopUpState } from "@app/hooks/usePopUp";
const addMemberFormSchema = z.object({ const addMemberFormSchema = z.object({
orgMemberships: z.array(z.object({ label: z.string().trim(), value: z.string().trim() })).min(1), orgMemberships: z.array(z.object({ label: z.string().trim(), value: z.string().trim() })).min(1),
projectRoleSlugs: z.array(z.string().trim().min(1)).min(1) projectRoleSlugs: z.array(z.object({ slug: z.string().trim(), name: z.string().trim() })).min(1)
}); });
type TAddMemberForm = z.infer<typeof addMemberFormSchema>; type TAddMemberForm = z.infer<typeof addMemberFormSchema>;
@@ -64,7 +51,7 @@ export const AddMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
formState: { isSubmitting, errors } formState: { isSubmitting, errors }
} = useForm<TAddMemberForm>({ } = useForm<TAddMemberForm>({
resolver: zodResolver(addMemberFormSchema), resolver: zodResolver(addMemberFormSchema),
defaultValues: { orgMemberships: [], projectRoleSlugs: [ProjectMembershipRole.Member] } defaultValues: { orgMemberships: [], projectRoleSlugs: [] }
}); });
const { mutateAsync: addMembersToProject } = useAddUsersToOrg(); const { mutateAsync: addMembersToProject } = useAddUsersToOrg();
@@ -94,7 +81,7 @@ export const AddMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
{ {
slug: currentWorkspace.slug, slug: currentWorkspace.slug,
id: currentWorkspace.id, id: currentWorkspace.id,
projectRoleSlug: projectRoleSlugs projectRoleSlug: projectRoleSlugs.map((role) => role.slug)
} }
] ]
}); });
@@ -172,78 +159,23 @@ export const AddMemberModal = ({ popUp, handlePopUpToggle }: Props) => {
<Controller <Controller
control={control} control={control}
name="projectRoleSlugs" name="projectRoleSlugs"
render={({ field }) => ( render={({ field: { onChange, value }, fieldState: { error } }) => (
<FormControl <FormControl
className="w-full" className="w-full"
label="Select roles" label="Select roles"
tooltipText="Select the roles that you wish to assign to the users" tooltipText="Select the roles that you wish to assign to the users"
errorText={error?.message}
isError={Boolean(error)}
> >
<DropdownMenu> <FilterableSelect
<DropdownMenuTrigger asChild> options={roles}
{roles && roles.length > 0 ? ( placeholder="Select roles..."
<div className="inline-flex w-full cursor-pointer items-center justify-between rounded-md border border-mineshaft-600 bg-mineshaft-900 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none data-[placeholder]:text-mineshaft-200"> value={value}
{/* eslint-disable-next-line no-nested-ternary */} onChange={onChange}
{selectedRoleSlugs.length === 1 isMulti
? roles.find((role) => role.slug === selectedRoleSlugs[0])?.name getOptionValue={(option) => option.slug}
: selectedRoleSlugs.length === 0 getOptionLabel={(option) => option.name}
? "Select at least one role"
: `${selectedRoleSlugs.length} roles selected`}
<FontAwesomeIcon
icon={faChevronDown}
className={twMerge("ml-2 text-xs")}
/> />
</div>
) : (
<div className="inline-flex w-full cursor-default items-center justify-between rounded-md border border-mineshaft-600 bg-mineshaft-900 px-3 py-2 font-inter text-sm font-normal text-bunker-200 outline-none data-[placeholder]:text-mineshaft-200">
No roles found
</div>
)}
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className="thin-scrollbar z-[100] max-h-80"
>
{roles && roles.length > 0 ? (
roles.map((role) => {
const isSelected = selectedRoleSlugs.includes(role.slug);
return (
<DropdownMenuItem
onSelect={(event) => roles.length > 1 && event.preventDefault()}
onClick={() => {
if (selectedRoleSlugs.includes(String(role.slug))) {
field.onChange(
selectedRoleSlugs.filter(
(roleSlug: string) => roleSlug !== String(role.slug)
)
);
} else {
field.onChange([...selectedRoleSlugs, role.slug]);
}
}}
key={`role-slug-${role.slug}`}
icon={
isSelected ? (
<FontAwesomeIcon
icon={faCheckCircle}
className="pr-0.5 text-primary"
/>
) : (
<div className="pl-[1.01rem]" />
)
}
iconPos="left"
className="w-[28.4rem] text-sm"
>
{role.name}
</DropdownMenuItem>
);
})
) : (
<div />
)}
</DropdownMenuContent>
</DropdownMenu>
</FormControl> </FormControl>
)} )}
/> />