From 543542713b0b17c93ee120d664c13225c7e427e4 Mon Sep 17 00:00:00 2001 From: Fred KISSIE Date: Tue, 31 Mar 2026 22:44:18 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/InternalResourceForm.tsx | 2 +- src/components/machines-selector.tsx | 120 +++++++++++++----------- src/components/multi-select-tags.tsx | 77 +++++++++++++++ src/components/tags/tag-input.tsx | 6 +- 4 files changed, 146 insertions(+), 59 deletions(-) create mode 100644 src/components/multi-select-tags.tsx diff --git a/src/components/InternalResourceForm.tsx b/src/components/InternalResourceForm.tsx index 2de907079..3f83205a7 100644 --- a/src/components/InternalResourceForm.tsx +++ b/src/components/InternalResourceForm.tsx @@ -1063,7 +1063,7 @@ export function InternalResourceForm({ ] ) } - enableAutocomplete={true} + enableAutocomplete autocompleteOptions={ allRoles } diff --git a/src/components/machines-selector.tsx b/src/components/machines-selector.tsx index 9c31a0bd3..99515135e 100644 --- a/src/components/machines-selector.tsx +++ b/src/components/machines-selector.tsx @@ -3,18 +3,9 @@ import type { ListClientsResponse } from "@server/routers/client"; import { useQuery } from "@tanstack/react-query"; import { useMemo, useState } from "react"; import { useDebounce } from "use-debounce"; -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList -} from "./ui/command"; -import { cn } from "@app/lib/cn"; -import { CheckIcon } from "lucide-react"; import { useTranslations } from "next-intl"; +import { MultiSelectTags } from "./multi-select-tags"; export type SelectedMachine = Pick< ListClientsResponse["clients"][number], @@ -57,52 +48,71 @@ export function MachinesSelector({ return allMachines; }, [machines, selectedMachines, debouncedValue]); - const selectedMachinesIds = new Set( - selectedMachines.map((m) => m.clientId) - ); + // const selectedMachinesIds = new Set( + // selectedMachines.map((m) => m.clientId) + // ); return ( - - - - {t("machineNotFound")} - - {machinesShown.map((m) => ( - { - let newMachineClients = []; - if (selectedMachinesIds.has(m.clientId)) { - newMachineClients = selectedMachines.filter( - (mc) => mc.clientId !== m.clientId - ); - } else { - newMachineClients = [ - ...selectedMachines, - m - ]; - } - onSelectMachines(newMachineClients); - }} - > - - {`${m.name}`} - - ))} - - - + ({ + ...m, + text: m.name, + id: m.clientId.toString() + }))} + onChange={(values) => { + onSelectMachines(values); + }} + options={machinesShown.map((m) => ({ + ...m, + id: m.clientId.toString(), + text: m.name + }))} + onSearch={setMachineSearchQuery} + searchQuery={machineSearchQuery} + /> + // + // + // + // {t("machineNotFound")} + // + // {machinesShown.map((m) => ( + // { + // let newMachineClients = []; + // if (selectedMachinesIds.has(m.clientId)) { + // newMachineClients = selectedMachines.filter( + // (mc) => mc.clientId !== m.clientId + // ); + // } else { + // newMachineClients = [ + // ...selectedMachines, + // m + // ]; + // } + // onSelectMachines(newMachineClients); + // }} + // > + // + // {`${m.name}`} + // + // ))} + // + // + // ); } diff --git a/src/components/multi-select-tags.tsx b/src/components/multi-select-tags.tsx new file mode 100644 index 000000000..2fb9b097d --- /dev/null +++ b/src/components/multi-select-tags.tsx @@ -0,0 +1,77 @@ +import type { Ref } from "react"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from "./ui/command"; +import { cn } from "@app/lib/cn"; +import { CheckIcon } from "lucide-react"; + +export type TagValue = { text: string; id: string }; + +export type MultiSelectTagsProps = { + emptyPlaceholder: string; + searchPlaceholder: string; + searchQuery?: string; + options: Array; + value: Array; + onChange: (newValue: Array) => void; + onSearch: (query: string) => void; + ref?: Ref; +}; + +export function MultiSelectTags({ + emptyPlaceholder, + searchPlaceholder, + searchQuery, + value, + options, + onSearch, + onChange +}: MultiSelectTagsProps) { + const selectedValues = new Set(value.map((v) => v.id)); + return ( + + + + {emptyPlaceholder} + + {options.map((option) => ( + { + let newValues = []; + if (selectedValues.has(option.id)) { + newValues = value.filter( + (v) => v.id !== option.id + ); + } else { + newValues = [...value, option]; + } + onChange(newValues); + }} + > + + {`${option.text}`} + + ))} + + + + ); +} diff --git a/src/components/tags/tag-input.tsx b/src/components/tags/tag-input.tsx index 36a173911..fafd2144f 100644 --- a/src/components/tags/tag-input.tsx +++ b/src/components/tags/tag-input.tsx @@ -522,7 +522,7 @@ export function TagInput({ ref, ...props }: TagInputProps) { onBlur={handleInputBlur} {...inputProps} className={cn( - "border-0 h-5 bg-transparent focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 flex-1 w-fit shadow-none inset-shadow-none", + "border-0 px-0 h-5 bg-transparent focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 flex-1 w-fit shadow-none inset-shadow-none", // className, styleClasses?.input )} @@ -692,7 +692,7 @@ export function TagInput({ ref, ...props }: TagInputProps) { onBlur={handleInputBlur} {...inputProps} className={cn( - "border-0 h-5 bg-transparent focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 flex-1 w-fit shadow-none inset-shadow-none", + "border-0 px-0 h-5 bg-transparent focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 flex-1 w-fit shadow-none inset-shadow-none", // className, styleClasses?.input )} @@ -770,7 +770,7 @@ export function TagInput({ ref, ...props }: TagInputProps) { onBlur={handleInputBlur} {...inputProps} className={cn( - "border-0 h-5 bg-transparent focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 flex-1 w-fit shadow-none inset-shadow-none", + "border-0 px-0 h-5 bg-transparent focus-visible:ring-0 focus-visible:ring-transparent focus-visible:ring-offset-0 flex-1 w-fit shadow-none inset-shadow-none", // className, styleClasses?.input )}