diff --git a/frontend/src/components/v2/FilterableSelect/FilterableSelect.tsx b/frontend/src/components/v2/FilterableSelect/FilterableSelect.tsx index 7bf546b13..8f51c2867 100644 --- a/frontend/src/components/v2/FilterableSelect/FilterableSelect.tsx +++ b/frontend/src/components/v2/FilterableSelect/FilterableSelect.tsx @@ -1,81 +1,118 @@ import Select, { Props } from "react-select"; import { twMerge } from "tailwind-merge"; -import { ClearIndicator, DropdownIndicator, MultiValueRemove, Option } from "../Select/components"; +import { + ClearIndicator, + DropdownIndicator, + Group, + MultiValueRemove, + Option +} from "../Select/components"; export const FilterableSelect = ({ isMulti, closeMenuOnSelect, tabSelectsValue = false, + groupBy = null, + getGroupHeaderLabel = null, + options = [], ...props -}: Props) => ( - ({ + ...base, + "input:focus": { + boxShadow: "none" + } + }), + multiValueLabel: (base) => ({ + ...base, + whiteSpace: "normal", + overflow: "visible" + }), + control: (base) => ({ + ...base, + transition: "none" + }) + }} + tabSelectsValue={tabSelectsValue} + components={{ + DropdownIndicator, + ClearIndicator, + MultiValueRemove, + Option, + Group, + ...props.components + }} + classNames={{ + container: ({ isDisabled }) => + twMerge("w-full font-inter text-sm", isDisabled && "!pointer-events-auto opacity-50"), + control: ({ isFocused, isDisabled }) => + twMerge( + isFocused ? "border-primary-400/50" : "border-mineshaft-600", + `w-full rounded-md border bg-mineshaft-900 p-0.5 font-inter text-mineshaft-200 ${ + isDisabled ? "!cursor-not-allowed" : "hover:cursor-pointer hover:border-gray-400" + } ` + ), + placeholder: () => + `${isMulti ? "py-[0.22rem]" : "leading-7"} text-mineshaft-400 text-sm pl-1`, + input: () => "pl-1", + valueContainer: () => + `px-1 max-h-[8.2rem] ${ + isMulti ? "!overflow-y-auto thin-scrollbar py-1" : "py-[0.1rem]" + } gap-1`, + singleValue: () => "leading-7 ml-1", + multiValue: () => "bg-mineshaft-600 text-sm rounded items-center py-0.5 px-2 gap-1.5", + multiValueLabel: () => "leading-6 text-sm", + multiValueRemove: () => "hover:text-red text-bunker-400", + indicatorsContainer: () => "p-1 gap-1", + clearIndicator: () => "p-1 hover:text-red text-bunker-400", + indicatorSeparator: () => "bg-bunker-400", + dropdownIndicator: () => "text-bunker-200 p-1", + menuList: () => "flex flex-col gap-1", + menu: () => + "my-2 p-2 border text-sm text-mineshaft-200 thin-scrollbar bg-mineshaft-900 border-mineshaft-600 rounded-md", + groupHeading: () => "ml-3 mt-2 mb-1 text-mineshaft-400 text-sm", + option: ({ isFocused, isSelected }) => + twMerge( + isFocused && "bg-mineshaft-700 active:bg-mineshaft-600", + isSelected && "text-mineshaft-200", + "rounded px-3 py-2 text-xs hover:cursor-pointer" + ), + noOptionsMessage: () => "text-mineshaft-400 p-2 rounded-md" + }} + {...props} + /> + ); +}; diff --git a/frontend/src/components/v2/Select/components/index.tsx b/frontend/src/components/v2/Select/components/index.tsx index 8d0e57d87..7e91c792d 100644 --- a/frontend/src/components/v2/Select/components/index.tsx +++ b/frontend/src/components/v2/Select/components/index.tsx @@ -2,6 +2,7 @@ import { ClearIndicatorProps, components, DropdownIndicatorProps, + GroupProps, MultiValueRemoveProps, OptionProps } from "react-select"; @@ -45,3 +46,7 @@ export const Option = ({ isSelected, children, ...props }: OptionProps) = ); }; + +export const Group = (props: GroupProps) => { + return ; +}; diff --git a/frontend/src/pages/organization/AccessManagementPage/components/OrgMembersTab/components/OrgMembersSection/AddOrgMemberModal.tsx b/frontend/src/pages/organization/AccessManagementPage/components/OrgMembersTab/components/OrgMembersSection/AddOrgMemberModal.tsx index f516d43fe..afde75e22 100644 --- a/frontend/src/pages/organization/AccessManagementPage/components/OrgMembersTab/components/OrgMembersSection/AddOrgMemberModal.tsx +++ b/frontend/src/pages/organization/AccessManagementPage/components/OrgMembersTab/components/OrgMembersSection/AddOrgMemberModal.tsx @@ -23,7 +23,7 @@ import { useGetUserWorkspaces } from "@app/hooks/api"; import { ProjectMembershipRole } from "@app/hooks/api/roles/types"; -import { ProjectVersion } from "@app/hooks/api/workspace/types"; +import { ProjectType, ProjectVersion } from "@app/hooks/api/workspace/types"; import { UsePopUpState } from "@app/hooks/usePopUp"; import { OrgInviteLink } from "./OrgInviteLink"; @@ -168,6 +168,21 @@ export const AddOrgMemberModal = ({ reset(); }; + const getGroupHeaderLabel = (type: ProjectType) => { + switch (type) { + case ProjectType.SecretManager: + return "Secrets"; + case ProjectType.CertificateManager: + return "PKI"; + case ProjectType.KMS: + return "KMS"; + case ProjectType.SSH: + return "SSH"; + default: + return "All"; + } + }; + return ( project.name} getOptionValue={(project) => project.id} options={projects} + groupBy="type" + getGroupHeaderLabel={getGroupHeaderLabel} placeholder="Select projects..." />