diff --git a/pkgs/clan-app/ui/src/components/Form/MachineTags.tsx b/pkgs/clan-app/ui/src/components/Form/MachineTags.tsx index 9d272ac34..34a510233 100644 --- a/pkgs/clan-app/ui/src/components/Form/MachineTags.tsx +++ b/pkgs/clan-app/ui/src/components/Form/MachineTags.tsx @@ -11,7 +11,7 @@ import { Label } from "@/src/components/Form/Label"; import { Orienter } from "@/src/components/Form/Orienter"; import { CollectionNode } from "@kobalte/core"; -interface MachineTag { +export interface MachineTag { value: string; disabled?: boolean; new?: boolean; @@ -24,11 +24,10 @@ export type MachineTagsProps = FieldProps & { disabled?: boolean; required?: boolean; defaultValue?: string[]; + defaultOptions?: string[]; + readonlyOptions?: string[]; }; -// tags which are applied to all machines and cannot be removed -const staticOptions = [{ value: "all", disabled: true }]; - const uniqueOptions = (options: MachineTag[]) => { const record: Record = {}; options.forEach((option) => { @@ -39,13 +38,8 @@ const uniqueOptions = (options: MachineTag[]) => { return Object.values(record); }; -const sortedOptions = (options: MachineTag[]) => { - return options.sort((a, b) => { - if (a.new && !b.new) return -1; - if (a.disabled && !b.disabled) return -1; - return a.value.localeCompare(b.value); - }); -}; +const sortedOptions = (options: MachineTag[]) => + options.sort((a, b) => a.value.localeCompare(b.value)); const sortedAndUniqueOptions = (options: MachineTag[]) => sortedOptions(uniqueOptions(options)); @@ -72,9 +66,15 @@ export const MachineTags = (props: MachineTagsProps) => { (props.defaultValue || []).map((value) => ({ value })), ); - // todo this should be the superset of tags used across the entire clan and be passed in via a prop + // convert default options string[] into MachineTag[] const [availableOptions, setAvailableOptions] = createSignal( - sortedAndUniqueOptions([...staticOptions, ...defaultValue]), + sortedAndUniqueOptions([ + ...(props.readonlyOptions || []).map((value) => ({ + value, + disabled: true, + })), + ...(props.defaultOptions || []).map((value) => ({ value })), + ]), ); const onKeyDown = (event: KeyboardEvent) => { diff --git a/pkgs/clan-app/ui/src/hooks/queries.ts b/pkgs/clan-app/ui/src/hooks/queries.ts index 28c6c74cd..1dd98c365 100644 --- a/pkgs/clan-app/ui/src/hooks/queries.ts +++ b/pkgs/clan-app/ui/src/hooks/queries.ts @@ -13,11 +13,13 @@ export type FieldSchema = { }; }; +export type Tags = SuccessData<"list_tags">; export type Machine = SuccessData<"get_machine">; export type ListMachines = SuccessData<"list_machines">; export type MachineDetails = SuccessData<"get_machine_details">; export interface MachineDetail { + tags: Tags; machine: Machine; fieldsSchema: FieldSchema; } @@ -50,7 +52,12 @@ export const useMachineQuery = (clanURI: string, machineName: string) => { return useQuery(() => ({ queryKey: ["clans", encodeBase64(clanURI), "machine", machineName], queryFn: async () => { - const [machineCall, schemaCall] = await Promise.all([ + const [tagsCall, machineCall, schemaCall] = await Promise.all([ + client.fetch("list_tags", { + flake: { + identifier: clanURI, + }, + }), client.fetch("get_machine", { name: machineName, flake: { @@ -67,6 +74,11 @@ export const useMachineQuery = (clanURI: string, machineName: string) => { }), ]); + const tags = await tagsCall.result; + if (tags.status === "error") { + throw new Error("Error fetching tags: " + tags.errors[0].message); + } + const machine = await machineCall.result; if (machine.status === "error") { throw new Error("Error fetching machine: " + machine.errors[0].message); @@ -81,6 +93,7 @@ export const useMachineQuery = (clanURI: string, machineName: string) => { } return { + tags: tags.data, machine: machine.data, fieldsSchema: writeSchema.data, }; diff --git a/pkgs/clan-app/ui/src/routes/Machine/SectionTags.tsx b/pkgs/clan-app/ui/src/routes/Machine/SectionTags.tsx index dcb20454d..2b173a9c9 100644 --- a/pkgs/clan-app/ui/src/routes/Machine/SectionTags.tsx +++ b/pkgs/clan-app/ui/src/routes/Machine/SectionTags.tsx @@ -4,7 +4,8 @@ import { MachineDetail } from "@/src/hooks/queries"; import { SidebarSectionForm } from "@/src/components/Sidebar/SidebarSectionForm"; import { pick } from "@/src/util"; import { UseQueryResult } from "@tanstack/solid-query"; -import { MachineTags } from "@/src/components/Form/MachineTags"; +import { MachineTag, MachineTags } from "@/src/components/Form/MachineTags"; +import { machineNameParam } from "@/src/hooks/clan"; const schema = v.object({ tags: v.pipe(v.optional(v.array(v.string()))), @@ -30,6 +31,28 @@ export const SectionTags = (props: SectionTags) => { return pick(machineQuery.data.machine, ["tags"]) satisfies FormValues; }; + const readonlyOptions = () => { + if (!machineQuery.isSuccess) { + return []; + } + + const result: string[] = ["all"]; + + if (machineQuery.data.machine.machineClass) { + result.push(machineQuery.data.machine.machineClass); + } + + return result; + }; + + const defaultOptions = () => { + if (!machineQuery.isSuccess) { + return []; + } + + return machineQuery.data.tags?.options ?? []; + }; + return ( { readOnly={!editing} orientation="horizontal" defaultValue={field.value} + defaultOptions={defaultOptions()} + readonlyOptions={readonlyOptions()} input={input} /> )}