mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-06-21 03:41:55 +03:00
Rename to Detectors and model, float Modified badge, use ConfigMessageBanner for mismatch
This commit is contained in:
parent
7126428307
commit
73e0d1b0eb
@ -1,19 +1,19 @@
|
||||
/**
|
||||
* Detectors & Model settings page tests -- HIGH tier.
|
||||
* Detectors and model settings page tests -- HIGH tier.
|
||||
*
|
||||
* Tests rendering of the merged page and navigation from the Frigate+ page.
|
||||
*/
|
||||
|
||||
import { test, expect } from "../../fixtures/frigate-test";
|
||||
|
||||
test.describe("Detectors & Model Settings @high", () => {
|
||||
test.describe("Detectors and model Settings @high", () => {
|
||||
test("page renders with detector and model cards", async ({ frigateApp }) => {
|
||||
await frigateApp.goto("/settings?page=systemDetectorsAndModel");
|
||||
await frigateApp.page.waitForTimeout(2000);
|
||||
await expect(frigateApp.page.locator("#pageRoot")).toBeVisible();
|
||||
|
||||
const text = await frigateApp.page.textContent("#pageRoot");
|
||||
expect(text).toContain("Detectors & Model");
|
||||
expect(text).toContain("Detectors and model");
|
||||
expect(text?.toLowerCase()).toContain("detector hardware");
|
||||
expect(text?.toLowerCase()).toContain("detection model");
|
||||
});
|
||||
@ -23,7 +23,7 @@ test.describe("Detectors & Model Settings @high", () => {
|
||||
await frigateApp.page.waitForTimeout(2000);
|
||||
|
||||
const button = frigateApp.page.getByRole("button", {
|
||||
name: /Change in Detectors & Model/,
|
||||
name: /Change in Detectors and model/,
|
||||
});
|
||||
|
||||
// Button only appears when Frigate+ is enabled in the test config; skip
|
||||
@ -32,7 +32,7 @@ test.describe("Detectors & Model Settings @high", () => {
|
||||
await button.first().click();
|
||||
await frigateApp.page.waitForURL(/page=systemDetectorsAndModel/);
|
||||
await expect(frigateApp.page.locator("#pageRoot")).toContainText(
|
||||
"Detectors & Model",
|
||||
"Detectors and model",
|
||||
);
|
||||
} else {
|
||||
test.skip(
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"globalConfig": "Global Configuration - Frigate",
|
||||
"cameraConfig": "Camera Configuration - Frigate",
|
||||
"frigatePlus": "Frigate+ Settings - Frigate",
|
||||
"detectorsAndModel": "Detectors & Model - Frigate",
|
||||
"detectorsAndModel": "Detectors and model - Frigate",
|
||||
"notifications": "Notification Settings - Frigate",
|
||||
"maintenance": "Maintenance - Frigate",
|
||||
"profiles": "Profiles - Frigate"
|
||||
@ -70,7 +70,7 @@
|
||||
"systemTelemetry": "Telemetry",
|
||||
"systemBirdseye": "Birdseye",
|
||||
"systemFfmpeg": "FFmpeg",
|
||||
"systemDetectorsAndModel": "Detectors & Model",
|
||||
"systemDetectorsAndModel": "Detectors and model",
|
||||
"systemMqtt": "MQTT",
|
||||
"systemGo2rtcStreams": "go2rtc streams",
|
||||
"integrationSemanticSearch": "Semantic search",
|
||||
@ -1146,7 +1146,7 @@
|
||||
},
|
||||
"modelSelect": "Your available models on Frigate+ can be selected here. Note that only models compatible with your current detector configuration can be selected."
|
||||
},
|
||||
"changeInDetectorsAndModel": "Change in Detectors & Model →",
|
||||
"changeInDetectorsAndModel": "Change model",
|
||||
"unsavedChanges": "Unsaved Frigate+ settings changes",
|
||||
"restart_required": "Restart required (Frigate+ model changed)",
|
||||
"toast": {
|
||||
@ -1155,7 +1155,7 @@
|
||||
}
|
||||
},
|
||||
"detectorsAndModel": {
|
||||
"title": "Detectors & Model",
|
||||
"title": "Detectors and model",
|
||||
"description": "Configure the detector backend that runs object detection and the model it uses. Changes are saved together so the detector and model stay in sync.",
|
||||
"cardTitles": {
|
||||
"detector": "Detector Hardware",
|
||||
@ -1166,7 +1166,7 @@
|
||||
"custom": "Custom Model"
|
||||
},
|
||||
"mismatch": {
|
||||
"warning": "The current Frigate+ model <0>{{model}}</0> requires the <1>{{required}}</1> detector. Pick a compatible model below or switch to Custom Model before saving."
|
||||
"warning": "The current Frigate+ model \"{{model}}\" requires the {{required}} detector. Pick a compatible model below or switch to Custom Model before saving."
|
||||
},
|
||||
"plusModel": {
|
||||
"requiresDetector": "Requires: {{detector}}",
|
||||
|
||||
@ -44,7 +44,7 @@ export function ConfigMessageBanner({ messages }: ConfigMessageBannerProps) {
|
||||
className="flex items-center [&>svg+div]:translate-y-0 [&>svg]:static [&>svg~*]:pl-2"
|
||||
>
|
||||
<SeverityIcon severity={msg.severity} />
|
||||
<AlertDescription>{t(msg.messageKey)}</AlertDescription>
|
||||
<AlertDescription>{t(msg.messageKey, msg.values)}</AlertDescription>
|
||||
</Alert>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -24,6 +24,8 @@ export type ConditionalMessage = {
|
||||
severity: MessageSeverity;
|
||||
/** Function returning true when the message should be shown */
|
||||
condition: (ctx: MessageConditionContext) => boolean;
|
||||
/** Optional interpolation values passed to t() for {{var}} substitution */
|
||||
values?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
/** Field-level conditional message, adds field targeting */
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { LuExternalLink, LuFilter } from "react-icons/lu";
|
||||
import { toast } from "sonner";
|
||||
@ -39,6 +39,7 @@ import type {
|
||||
import type { ConfigSectionData } from "@/types/configForm";
|
||||
import { SettingsGroupCard } from "@/components/card/SettingsGroupCard";
|
||||
import { ConfigSectionTemplate } from "@/components/config-form/sections";
|
||||
import { ConfigMessageBanner } from "@/components/config-form/ConfigMessageBanner";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
|
||||
type ModelTab = "plus" | "custom";
|
||||
@ -358,35 +359,34 @@ export default function DetectorsAndModelSettingsView({
|
||||
return (
|
||||
<div className="flex size-full flex-col md:pr-2">
|
||||
<Toaster position="top-center" closeButton={true} />
|
||||
<div className="w-full max-w-5xl space-y-6 pt-2">
|
||||
<div className="mb-1 flex items-center justify-between gap-4">
|
||||
<div className="flex flex-col">
|
||||
<Heading as="h4">{t("detectorsAndModel.title")}</Heading>
|
||||
<div className="my-1 text-sm text-muted-foreground">
|
||||
{t("detectorsAndModel.description")}
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-primary-variant">
|
||||
<Link
|
||||
to={getLocaleDocUrl("/configuration/object_detectors")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
>
|
||||
{t("readTheDocumentation", { ns: "common" })}
|
||||
<LuExternalLink className="ml-2 inline-flex size-3" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className="mb-1 flex items-center justify-between gap-4 pt-2">
|
||||
<div className="flex max-w-5xl flex-col">
|
||||
<Heading as="h4">{t("detectorsAndModel.title")}</Heading>
|
||||
<div className="my-1 text-sm text-muted-foreground">
|
||||
{t("detectorsAndModel.description")}
|
||||
</div>
|
||||
{isDirty && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="cursor-default bg-unsaved text-xs text-black hover:bg-unsaved"
|
||||
<div className="flex items-center text-sm text-primary-variant">
|
||||
<Link
|
||||
to={getLocaleDocUrl("/configuration/object_detectors")}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline"
|
||||
>
|
||||
{t("button.modified", { ns: "common", defaultValue: "Modified" })}
|
||||
</Badge>
|
||||
)}
|
||||
{t("readTheDocumentation", { ns: "common" })}
|
||||
<LuExternalLink className="ml-2 inline-flex size-3" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isDirty && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="cursor-default bg-unsaved text-xs text-black hover:bg-unsaved"
|
||||
>
|
||||
{t("button.modified", { ns: "common", defaultValue: "Modified" })}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-full max-w-5xl space-y-6 pt-4">
|
||||
<div className="space-y-6">
|
||||
<SettingsGroupCard title={t("detectorsAndModel.cardTitles.detector")}>
|
||||
<ConfigSectionTemplate
|
||||
@ -401,20 +401,20 @@ export default function DetectorsAndModelSettingsView({
|
||||
/>
|
||||
</SettingsGroupCard>
|
||||
{plusMismatch && selectedPlusModel && (
|
||||
<div className="rounded-md border border-danger bg-danger/10 px-4 py-3 text-sm text-danger">
|
||||
<Trans
|
||||
ns="views/settings"
|
||||
i18nKey="detectorsAndModel.mismatch.warning"
|
||||
values={{
|
||||
model: selectedPlusModel.name,
|
||||
required: selectedPlusModel.supportedDetectors.join(", "),
|
||||
}}
|
||||
components={{
|
||||
0: <strong />,
|
||||
1: <strong />,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ConfigMessageBanner
|
||||
messages={[
|
||||
{
|
||||
key: "plus-mismatch",
|
||||
messageKey: "detectorsAndModel.mismatch.warning",
|
||||
severity: "warning",
|
||||
condition: () => true,
|
||||
values: {
|
||||
model: selectedPlusModel.name,
|
||||
required: selectedPlusModel.supportedDetectors.join(", "),
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<SettingsGroupCard title={t("detectorsAndModel.cardTitles.model")}>
|
||||
<Tabs
|
||||
|
||||
Loading…
Reference in New Issue
Block a user