This commit is contained in:
Josh Hawkins 2026-05-26 09:52:00 -05:00
parent 45bcd0459b
commit 6c5558271a
2 changed files with 36 additions and 35 deletions

View File

@ -546,7 +546,7 @@
"saveSuccess": "Updated camera type for {{cameraName}}. Restart Frigate to apply the changes." "saveSuccess": "Updated camera type for {{cameraName}}. Restart Frigate to apply the changes."
}, },
"clone": { "clone": {
"trigger": "Clone...", "trigger": "Clone camera or copy settings",
"triggerAriaLabel": "Clone settings from {{cameraName}}", "triggerAriaLabel": "Clone settings from {{cameraName}}",
"title": "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.", "description": "Copy this camera's configuration to a new or existing camera. Identity (name, friendly name, web UI URL, display order) is never copied.",

View File

@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from "react"; 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 { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod"; import { z } from "zod";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -209,11 +209,12 @@ export default function CloneCameraDialog({
const { send: sendRestart } = useRestart(); const { send: sendRestart } = useRestart();
const [restartDialogOpen, setRestartDialogOpen] = useState(false); const [restartDialogOpen, setRestartDialogOpen] = useState(false);
const watchedNewName =
useWatch({ control: form.control, name: "newName" }) ?? "";
const previewPayloads = useMemo(() => { const previewPayloads = useMemo(() => {
if (!config || !fullSchema || !srcCfg) return []; if (!config || !fullSchema || !srcCfg) return [];
const targetInput = targetIsNew const targetInput = targetIsNew ? watchedNewName : existingTarget;
? form.getValues("newName")
: existingTarget;
if (!targetInput) return []; if (!targetInput) return [];
if (!targetIsNew && !config.cameras?.[targetInput]) return []; if (!targetIsNew && !config.cameras?.[targetInput]) return [];
return buildClonedCameraPayloads({ return buildClonedCameraPayloads({
@ -231,13 +232,13 @@ export default function CloneCameraDialog({
srcCfg, srcCfg,
targetIsNew, targetIsNew,
existingTarget, existingTarget,
watchedNewName,
selectedCategories, selectedCategories,
sourceCamera, sourceCamera,
form,
]); ]);
const previewTarget = targetIsNew const previewTarget = targetIsNew
? processCameraName(form.watch("newName") || "").finalCameraName ? processCameraName(watchedNewName || "").finalCameraName
: existingTarget; : existingTarget;
const previewItems = useMemo( const previewItems = useMemo(
@ -413,35 +414,35 @@ export default function CloneCameraDialog({
</label> </label>
</div> </div>
{targetMode === "new" && ( {targetMode === "new" && (
<FormField <FormItem className="ml-6">
control={form.control} <FormLabel className="sr-only">
name="newName" {t(
render={({ field: nameField }) => ( "cameraManagement.clone.target.newNameLabel",
<FormItem className="ml-6"> )}
<FormLabel className="sr-only"> </FormLabel>
{t( <FormControl>
"cameraManagement.clone.target.newNameLabel", <Input
)} {...form.register("newName")}
</FormLabel> placeholder={t(
<FormControl> "cameraManagement.clone.target.newNamePlaceholder",
<Input )}
{...nameField} disabled={isSubmitting}
placeholder={t( autoFocus
"cameraManagement.clone.target.newNamePlaceholder", />
)} </FormControl>
disabled={isSubmitting} {form.formState.errors.newName?.message && (
autoFocus <p className="text-sm font-medium text-destructive">
/> {String(
</FormControl> form.formState.errors.newName.message,
<FormMessage /> )}
<p className="text-xs text-muted-foreground"> </p>
{t(
"cameraManagement.clone.target.newStreamsForced",
)}
</p>
</FormItem>
)} )}
/> <p className="text-xs text-muted-foreground">
{t(
"cameraManagement.clone.target.newStreamsForced",
)}
</p>
</FormItem>
)} )}
</div> </div>
<div className="space-y-2"> <div className="space-y-2">