mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-03 02:21:13 +03:00
Add config to UI
This commit is contained in:
parent
6e1a0b7982
commit
f46458c5f5
@ -492,12 +492,16 @@
|
|||||||
"details": {
|
"details": {
|
||||||
"edit": "Edit camera details",
|
"edit": "Edit camera details",
|
||||||
"title": "Edit Camera Details",
|
"title": "Edit Camera Details",
|
||||||
"description": "Update the display name and external URL used for this camera throughout the Frigate UI.",
|
"description": "Update the display name, external URL, and visibility used for this camera throughout the Frigate UI.",
|
||||||
"friendlyNameLabel": "Display Name",
|
"friendlyNameLabel": "Display Name",
|
||||||
"friendlyNameHelp": "Friendly name shown for this camera throughout the Frigate UI. Leave blank to use the camera ID.",
|
"friendlyNameHelp": "Friendly name shown for this camera throughout the Frigate UI. Leave blank to use the camera ID.",
|
||||||
"webuiUrlLabel": "Camera Web UI URL",
|
"webuiUrlLabel": "Camera Web UI URL",
|
||||||
"webuiUrlHelp": "URL to visit the camera's web UI directly from the Debug view. Leave blank to disable the link.",
|
"webuiUrlHelp": "URL to visit the camera's web UI directly from the Debug view. Leave blank to disable the link.",
|
||||||
"webuiUrlInvalid": "Must be a valid URL (e.g., https://example.com)."
|
"webuiUrlInvalid": "Must be a valid URL (e.g., https://example.com).",
|
||||||
|
"dashboardLabel": "Show on Live dashboard",
|
||||||
|
"dashboardHelp": "Show this camera on the Live dashboard.",
|
||||||
|
"reviewLabel": "Show in Review",
|
||||||
|
"reviewHelp": "Show this camera in Review, including the camera filter, motion review, and the history view."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cameraConfig": {
|
"cameraConfig": {
|
||||||
|
|||||||
@ -75,6 +75,7 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@ -704,6 +705,8 @@ type CameraDetailsEditorProps = {
|
|||||||
type CameraDetailsFormValues = {
|
type CameraDetailsFormValues = {
|
||||||
friendlyName: string;
|
friendlyName: string;
|
||||||
webuiUrl: string;
|
webuiUrl: string;
|
||||||
|
dashboard: boolean;
|
||||||
|
review: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
function CameraDetailsEditor({
|
function CameraDetailsEditor({
|
||||||
@ -717,11 +720,15 @@ function CameraDetailsEditor({
|
|||||||
|
|
||||||
const currentFriendlyName = config?.cameras?.[cameraName]?.friendly_name;
|
const currentFriendlyName = config?.cameras?.[cameraName]?.friendly_name;
|
||||||
const currentWebuiUrl = config?.cameras?.[cameraName]?.webui_url;
|
const currentWebuiUrl = config?.cameras?.[cameraName]?.webui_url;
|
||||||
|
const currentDashboard = config?.cameras?.[cameraName]?.ui?.dashboard ?? true;
|
||||||
|
const currentReview = config?.cameras?.[cameraName]?.ui?.review ?? true;
|
||||||
|
|
||||||
const formSchema = useMemo(
|
const formSchema = useMemo(
|
||||||
() =>
|
() =>
|
||||||
z.object({
|
z.object({
|
||||||
friendlyName: z.string(),
|
friendlyName: z.string(),
|
||||||
|
dashboard: z.boolean(),
|
||||||
|
review: z.boolean(),
|
||||||
webuiUrl: z.string().refine(
|
webuiUrl: z.string().refine(
|
||||||
(val) => {
|
(val) => {
|
||||||
const trimmed = val.trim();
|
const trimmed = val.trim();
|
||||||
@ -748,6 +755,8 @@ function CameraDetailsEditor({
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
friendlyName: currentFriendlyName ?? "",
|
friendlyName: currentFriendlyName ?? "",
|
||||||
webuiUrl: currentWebuiUrl ?? "",
|
webuiUrl: currentWebuiUrl ?? "",
|
||||||
|
dashboard: currentDashboard,
|
||||||
|
review: currentReview,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -757,9 +766,18 @@ function CameraDetailsEditor({
|
|||||||
form.reset({
|
form.reset({
|
||||||
friendlyName: currentFriendlyName ?? "",
|
friendlyName: currentFriendlyName ?? "",
|
||||||
webuiUrl: currentWebuiUrl ?? "",
|
webuiUrl: currentWebuiUrl ?? "",
|
||||||
|
dashboard: currentDashboard,
|
||||||
|
review: currentReview,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [open, currentFriendlyName, currentWebuiUrl, form]);
|
}, [
|
||||||
|
open,
|
||||||
|
currentFriendlyName,
|
||||||
|
currentWebuiUrl,
|
||||||
|
currentDashboard,
|
||||||
|
currentReview,
|
||||||
|
form,
|
||||||
|
]);
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
async (values: CameraDetailsFormValues) => {
|
async (values: CameraDetailsFormValues) => {
|
||||||
@ -768,7 +786,7 @@ function CameraDetailsEditor({
|
|||||||
// only send fields the user actually changed
|
// only send fields the user actually changed
|
||||||
const newFriendly = values.friendlyName.trim() || null;
|
const newFriendly = values.friendlyName.trim() || null;
|
||||||
const newWebui = values.webuiUrl.trim() || null;
|
const newWebui = values.webuiUrl.trim() || null;
|
||||||
const cameraUpdate: Record<string, string | null> = {};
|
const cameraUpdate: Record<string, unknown> = {};
|
||||||
if (newFriendly !== (currentFriendlyName ?? null)) {
|
if (newFriendly !== (currentFriendlyName ?? null)) {
|
||||||
cameraUpdate.friendly_name = newFriendly;
|
cameraUpdate.friendly_name = newFriendly;
|
||||||
}
|
}
|
||||||
@ -776,6 +794,17 @@ function CameraDetailsEditor({
|
|||||||
cameraUpdate.webui_url = newWebui;
|
cameraUpdate.webui_url = newWebui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uiUpdate: Record<string, boolean> = {};
|
||||||
|
if (values.dashboard !== currentDashboard) {
|
||||||
|
uiUpdate.dashboard = values.dashboard;
|
||||||
|
}
|
||||||
|
if (values.review !== currentReview) {
|
||||||
|
uiUpdate.review = values.review;
|
||||||
|
}
|
||||||
|
if (Object.keys(uiUpdate).length > 0) {
|
||||||
|
cameraUpdate.ui = uiUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.keys(cameraUpdate).length === 0) {
|
if (Object.keys(cameraUpdate).length === 0) {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
return;
|
return;
|
||||||
@ -818,6 +847,8 @@ function CameraDetailsEditor({
|
|||||||
cameraName,
|
cameraName,
|
||||||
currentFriendlyName,
|
currentFriendlyName,
|
||||||
currentWebuiUrl,
|
currentWebuiUrl,
|
||||||
|
currentDashboard,
|
||||||
|
currentReview,
|
||||||
isSaving,
|
isSaving,
|
||||||
onConfigChanged,
|
onConfigChanged,
|
||||||
t,
|
t,
|
||||||
@ -914,6 +945,60 @@ function CameraDetailsEditor({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="dashboard"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between gap-3">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel>
|
||||||
|
{t("cameraManagement.streams.details.dashboardLabel", {
|
||||||
|
ns: "views/settings",
|
||||||
|
})}
|
||||||
|
</FormLabel>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{t("cameraManagement.streams.details.dashboardHelp", {
|
||||||
|
ns: "views/settings",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
disabled={isSaving}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="review"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between gap-3">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel>
|
||||||
|
{t("cameraManagement.streams.details.reviewLabel", {
|
||||||
|
ns: "views/settings",
|
||||||
|
})}
|
||||||
|
</FormLabel>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{t("cameraManagement.streams.details.reviewHelp", {
|
||||||
|
ns: "views/settings",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
disabled={isSaving}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<DialogFooter className="pt-2">
|
<DialogFooter className="pt-2">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user