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,10 +414,6 @@ export default function CloneCameraDialog({
</label> </label>
</div> </div>
{targetMode === "new" && ( {targetMode === "new" && (
<FormField
control={form.control}
name="newName"
render={({ field: nameField }) => (
<FormItem className="ml-6"> <FormItem className="ml-6">
<FormLabel className="sr-only"> <FormLabel className="sr-only">
{t( {t(
@ -425,7 +422,7 @@ export default function CloneCameraDialog({
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
{...nameField} {...form.register("newName")}
placeholder={t( placeholder={t(
"cameraManagement.clone.target.newNamePlaceholder", "cameraManagement.clone.target.newNamePlaceholder",
)} )}
@ -433,7 +430,13 @@ export default function CloneCameraDialog({
autoFocus autoFocus
/> />
</FormControl> </FormControl>
<FormMessage /> {form.formState.errors.newName?.message && (
<p className="text-sm font-medium text-destructive">
{String(
form.formState.errors.newName.message,
)}
</p>
)}
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
{t( {t(
"cameraManagement.clone.target.newStreamsForced", "cameraManagement.clone.target.newStreamsForced",
@ -441,8 +444,6 @@ export default function CloneCameraDialog({
</p> </p>
</FormItem> </FormItem>
)} )}
/>
)}
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">