From 26662784f6b8429e782eeb7c5d904030b900243f Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:55:18 -0500 Subject: [PATCH] add reusable component for combined name / internal name form field --- web/public/locales/en/common.json | 9 +- web/src/components/input/NameAndIdFields.tsx | 105 +++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 web/src/components/input/NameAndIdFields.tsx diff --git a/web/public/locales/en/common.json b/web/public/locales/en/common.json index 924dc0d0e..2f976fb0c 100644 --- a/web/public/locales/en/common.json +++ b/web/public/locales/en/common.json @@ -93,7 +93,14 @@ } }, "label": { - "back": "Go back" + "back": "Go back", + "hide": "Hide {{item}}", + "show": "Show {{item}}", + "ID": "ID" + }, + "field": { + "optional": "Optional", + "internalID": "The Internal ID Frigate uses in the configuration and database" }, "button": { "apply": "Apply", diff --git a/web/src/components/input/NameAndIdFields.tsx b/web/src/components/input/NameAndIdFields.tsx new file mode 100644 index 000000000..cf9f8a076 --- /dev/null +++ b/web/src/components/input/NameAndIdFields.tsx @@ -0,0 +1,105 @@ +import { Control } from "react-hook-form"; +import { + FormField, + FormItem, + FormLabel, + FormControl, + FormDescription, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { useState, useEffect } from "react"; +import { useFormContext } from "react-hook-form"; +import { useTranslation } from "react-i18next"; + +type NameAndIdFieldsProps = { + control: Control; + nameField: string; + idField: string; + nameLabel: string; + nameDescription?: string; + idLabel?: string; + idDescription?: string; + processId: (name: string) => string; + placeholderName?: string; + placeholderId?: string; +}; + +export default function NameAndIdFields({ + control, + nameField, + idField, + nameLabel, + nameDescription, + idLabel, + idDescription, + processId, + placeholderName, + placeholderId, +}: NameAndIdFieldsProps) { + const { t } = useTranslation(["common"]); + const { watch, setValue, trigger } = useFormContext(); + const [isIdVisible, setIsIdVisible] = useState(false); + + useEffect(() => { + const subscription = watch((value, { name }) => { + if (name === nameField) { + const processedId = processId(value[nameField] || ""); + setValue(idField, processedId); + trigger(idField); + } + }); + return () => subscription.unsubscribe(); + }, [watch, setValue, trigger, nameField, idField, processId]); + + return ( + <> + ( + +
+ {nameLabel} + setIsIdVisible(!isIdVisible)} + > + ( + {isIdVisible + ? `${t("label.hide")} ${idLabel ?? t("label.ID")}` + : `${t("label.show")} ${idLabel ?? t("label.ID")}`} + ) + +
+ + + + {nameDescription && ( + {nameDescription} + )} + +
+ )} + /> + {isIdVisible && ( + ( + + {idLabel} + + + + + {idDescription ?? t("field.description")} + + + + )} + /> + )} + + ); +}