diff --git a/web/src/views/settings/CameraConfigView.tsx b/web/src/views/settings/CameraConfigView.tsx deleted file mode 100644 index d073dacbb..000000000 --- a/web/src/views/settings/CameraConfigView.tsx +++ /dev/null @@ -1,274 +0,0 @@ -// Camera Configuration View -// Per-camera configuration with tab navigation and override indicators - -import { useMemo, useCallback, useState, memo } from "react"; -import useSWR from "swr"; -import { useTranslation } from "react-i18next"; -import { ConfigSectionTemplate } from "@/components/config-form/sections"; -import { useAllCameraOverrides } from "@/hooks/use-config-override"; -import type { FrigateConfig } from "@/types/frigateConfig"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { Badge } from "@/components/ui/badge"; -import ActivityIndicator from "@/components/indicators/activity-indicator"; -import Heading from "@/components/ui/heading"; -import { cn } from "@/lib/utils"; - -interface CameraConfigViewProps { - /** Currently selected camera (from parent) */ - selectedCamera?: string; - /** Callback when unsaved changes state changes */ - setUnsavedChanges?: React.Dispatch>; -} - -export default function CameraConfigView({ - selectedCamera: externalSelectedCamera, - setUnsavedChanges, -}: CameraConfigViewProps) { - const { t } = useTranslation(["views/settings"]); - - const { data: config, mutate: refreshConfig } = - useSWR("config"); - - // Get list of cameras - const cameras = useMemo(() => { - if (!config?.cameras) return []; - return Object.keys(config.cameras).sort(); - }, [config]); - - // Selected camera state (use external if provided, else internal) - const [internalSelectedCamera, setInternalSelectedCamera] = useState( - cameras[0] || "", - ); - const selectedCamera = externalSelectedCamera || internalSelectedCamera; - - // Get overridden sections for current camera - const overriddenSections = useAllCameraOverrides(config, selectedCamera); - - const handleSave = useCallback(() => { - refreshConfig(); - setUnsavedChanges?.(false); - }, [refreshConfig, setUnsavedChanges]); - - const handleCameraChange = useCallback((camera: string) => { - setInternalSelectedCamera(camera); - }, []); - - if (!config) { - return ( -
- -
- ); - } - - if (cameras.length === 0) { - return ( -
- {t("configForm.camera.noCameras", { - defaultValue: "No cameras configured", - })} -
- ); - } - - return ( -
-
- - {t("configForm.camera.title", { - defaultValue: "Camera Configuration", - })} - -

- {t("configForm.camera.description", { - defaultValue: - "Configure settings for individual cameras. Overridden settings are highlighted.", - })} -

-
- - {/* Camera Tabs - Only show if not externally controlled */} - {!externalSelectedCamera && ( - - - - {cameras.map((camera) => { - const cameraOverrides = overriddenSections.filter((s) => - s.startsWith(camera), - ); - const hasOverrides = cameraOverrides.length > 0; - const cameraConfig = config.cameras[camera]; - const displayName = cameraConfig?.name || camera; - - return ( - - {displayName} - {hasOverrides && ( - - {cameraOverrides.length} - - )} - - ); - })} - - - - {cameras.map((camera) => ( - - - - ))} - - )} - - {/* Direct content when externally controlled */} - {externalSelectedCamera && ( - - )} -
- ); -} - -interface CameraConfigContentProps { - cameraName: string; - config: FrigateConfig; - overriddenSections: string[]; - onSave: () => void; -} - -const CameraConfigContent = memo(function CameraConfigContent({ - cameraName, - config, - overriddenSections, - onSave, -}: CameraConfigContentProps) { - const { t } = useTranslation([ - "config/cameras", - "config/cameras", - "views/settings", - "common", - ]); - - const [activeSection, setActiveSection] = useState("detect"); - - const cameraConfig = config.cameras?.[cameraName]; - - if (!cameraConfig) { - return ( -
- {t("configForm.camera.notFound", { - ns: "views/settings", - defaultValue: "Camera not found", - })} -
- ); - } - - const sections: Array<{ - key: string; - showOverrideIndicator?: boolean; - }> = [ - { key: "detect" }, - { key: "ffmpeg", showOverrideIndicator: true }, - { key: "record" }, - { key: "snapshots" }, - { key: "motion" }, - { key: "objects" }, - { key: "review" }, - { key: "audio" }, - { key: "audio_transcription", showOverrideIndicator: true }, - { key: "notifications" }, - { key: "live" }, - { key: "birdseye", showOverrideIndicator: true }, - { key: "face_recognition", showOverrideIndicator: true }, - { key: "lpr", showOverrideIndicator: true }, - { key: "mqtt", showOverrideIndicator: false }, - { key: "onvif", showOverrideIndicator: false }, - { key: "ui", showOverrideIndicator: false }, - { key: "timestamp_style" }, - ]; - - return ( -
- {/* Section Navigation */} - - - {/* Section Content */} -
- {sections.map((section) => ( -
- -
- ))} -
-
- ); -}); diff --git a/web/src/views/settings/GlobalConfigView.tsx b/web/src/views/settings/GlobalConfigView.tsx deleted file mode 100644 index d06f928fa..000000000 --- a/web/src/views/settings/GlobalConfigView.tsx +++ /dev/null @@ -1,194 +0,0 @@ -// Global Configuration View -// Main view for configuring global Frigate settings - -import { useMemo, useCallback, useState } from "react"; -import useSWR from "swr"; -import { useTranslation } from "react-i18next"; -import { ConfigSectionTemplate } from "@/components/config-form/sections"; -import type { FrigateConfig } from "@/types/frigateConfig"; -import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import ActivityIndicator from "@/components/indicators/activity-indicator"; -import Heading from "@/components/ui/heading"; -import { cn } from "@/lib/utils"; -// Shared sections that can be overridden at camera level -const sharedSections = [ - { key: "detect" }, - { key: "record" }, - { key: "snapshots" }, - { key: "motion" }, - { key: "objects" }, - { key: "review" }, - { key: "audio" }, - { key: "live" }, - { key: "timestamp_style" }, -]; - -// System sections (global only) -const systemSections = [ - { key: "database" }, - { key: "tls" }, - { key: "auth" }, - { key: "networking" }, - { key: "proxy" }, - { key: "ui" }, - { key: "logger" }, - { key: "environment_vars" }, - { key: "telemetry" }, - { key: "birdseye" }, - { key: "ffmpeg" }, - { key: "detectors" }, - { key: "model" }, -]; - -// Integration sections (global only) -const integrationSections = [ - { key: "mqtt" }, - { key: "semantic_search" }, - { key: "genai" }, - { key: "face_recognition" }, - { key: "lpr" }, - { key: "classification" }, - { key: "audio_transcription" }, -]; - -export default function GlobalConfigView() { - const { t } = useTranslation(["views/settings", "config/global", "common"]); - const defaultSharedSection = sharedSections[0]?.key ?? ""; - const defaultSystemSection = systemSections[0]?.key ?? ""; - const defaultIntegrationSection = integrationSections[0]?.key ?? ""; - const [activeTab, setActiveTab] = useState("shared"); - const [activeSection, setActiveSection] = useState(defaultSharedSection); - - const { data: config, mutate: refreshConfig } = - useSWR("config"); - - const handleSave = useCallback(() => { - refreshConfig(); - }, [refreshConfig]); - - // Get the sections for the current tab - const currentSections = useMemo(() => { - if (activeTab === "shared") { - return sharedSections; - } - if (activeTab === "system") { - return systemSections; - } - return integrationSections; - }, [activeTab]); - - // Reset active section when tab changes - const handleTabChange = useCallback( - (tab: string) => { - setActiveTab(tab); - if (tab === "shared") { - setActiveSection(defaultSharedSection); - } else if (tab === "system") { - setActiveSection(defaultSystemSection); - } else { - setActiveSection(defaultIntegrationSection); - } - }, - [defaultSharedSection, defaultSystemSection, defaultIntegrationSection], - ); - - if (!config) { - return ( -
- -
- ); - } - - return ( -
-
- - {t("configForm.global.title", { - defaultValue: "Global Configuration", - })} - -

- {t("configForm.global.description", { - defaultValue: - "Configure global settings that apply to all cameras by default.", - })} -

-
- - - - - {t("configForm.global.tabs.shared", { - defaultValue: "Shared Defaults", - })} - - - {t("configForm.global.tabs.system", { defaultValue: "System" })} - - - {t("configForm.global.tabs.integrations", { - defaultValue: "Integrations", - })} - - - -
- {/* Section Navigation */} - - - {/* Section Content */} -
- {currentSections.map((section) => ( -
- -
- ))} -
-
-
-
- ); -}