diff --git a/web/public/locales/en/views/settings.json b/web/public/locales/en/views/settings.json index b92be7c9d3..c94061aa74 100644 --- a/web/public/locales/en/views/settings.json +++ b/web/public/locales/en/views/settings.json @@ -546,7 +546,7 @@ "saveSuccess": "Updated camera type for {{cameraName}}. Restart Frigate to apply the changes." }, "clone": { - "trigger": "Clone...", + "trigger": "Clone camera or copy settings", "triggerAriaLabel": "Clone settings from {{cameraName}}", "title": "Clone settings from {{cameraName}}", "description": "Copy this camera's configuration to a new or existing camera. Identity (name, friendly name, web UI URL, display order) is never copied.", diff --git a/web/src/components/settings/CloneCameraDialog.tsx b/web/src/components/settings/CloneCameraDialog.tsx index a7d4f2e656..11c9161b95 100644 --- a/web/src/components/settings/CloneCameraDialog.tsx +++ b/web/src/components/settings/CloneCameraDialog.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useMemo, useState } from "react"; -import { useForm } from "react-hook-form"; +import { useForm, useWatch } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { useTranslation } from "react-i18next"; @@ -209,11 +209,12 @@ export default function CloneCameraDialog({ const { send: sendRestart } = useRestart(); const [restartDialogOpen, setRestartDialogOpen] = useState(false); + const watchedNewName = + useWatch({ control: form.control, name: "newName" }) ?? ""; + const previewPayloads = useMemo(() => { if (!config || !fullSchema || !srcCfg) return []; - const targetInput = targetIsNew - ? form.getValues("newName") - : existingTarget; + const targetInput = targetIsNew ? watchedNewName : existingTarget; if (!targetInput) return []; if (!targetIsNew && !config.cameras?.[targetInput]) return []; return buildClonedCameraPayloads({ @@ -231,13 +232,13 @@ export default function CloneCameraDialog({ srcCfg, targetIsNew, existingTarget, + watchedNewName, selectedCategories, sourceCamera, - form, ]); const previewTarget = targetIsNew - ? processCameraName(form.watch("newName") || "").finalCameraName + ? processCameraName(watchedNewName || "").finalCameraName : existingTarget; const previewItems = useMemo( @@ -413,35 +414,35 @@ export default function CloneCameraDialog({ {targetMode === "new" && ( - ( - - - {t( - "cameraManagement.clone.target.newNameLabel", - )} - - - - - -

- {t( - "cameraManagement.clone.target.newStreamsForced", - )} -

-
+ + + {t( + "cameraManagement.clone.target.newNameLabel", + )} + + + + + {form.formState.errors.newName?.message && ( +

+ {String( + form.formState.errors.newName.message, + )} +

)} - /> +

+ {t( + "cameraManagement.clone.target.newStreamsForced", + )} +

+
)}