This commit is contained in:
Josh Hawkins 2026-01-28 11:00:41 -06:00
parent e916f5b582
commit 7f6887d8b2
3 changed files with 18 additions and 14 deletions

View File

@ -106,7 +106,7 @@ export function FieldTemplate(props: FieldTemplateProps) {
return ( return (
<div <div
className={cn( className={cn(
"space-y-2", "space-y-1",
isAdvanced && "border-l-2 border-muted pl-4", isAdvanced && "border-l-2 border-muted pl-4",
isBoolean && "flex items-center justify-between gap-4", isBoolean && "flex items-center justify-between gap-4",
)} )}
@ -144,10 +144,10 @@ export function FieldTemplate(props: FieldTemplateProps) {
</div> </div>
) : ( ) : (
<> <>
{children}
{finalDescription && !isMultiSchemaWrapper && !isObjectField && ( {finalDescription && !isMultiSchemaWrapper && !isObjectField && (
<p className="text-xs text-muted-foreground">{finalDescription}</p> <p className="text-xs text-muted-foreground">{finalDescription}</p>
)} )}
{children}
</> </>
)} )}

View File

@ -12,6 +12,14 @@ import { LuChevronDown, LuChevronRight } from "react-icons/lu";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
/**
* Build the i18n translation key path for nested fields using the field path
* provided by RJSF. This avoids ambiguity with underscores in field names.
*/
function buildTranslationPath(path: Array<string | number>): string {
return path.filter((segment) => typeof segment === "string").join(".");
}
export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) { export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
const { title, description, properties, uiSchema, registry, schema } = props; const { title, description, properties, uiSchema, registry, schema } = props;
type FormContext = { i18nNamespace?: string }; type FormContext = { i18nNamespace?: string };
@ -45,13 +53,16 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
const toTitle = (value: string) => const toTitle = (value: string) =>
value.replace(/_/g, " ").replace(/\b\w/g, (char) => char.toUpperCase()); value.replace(/_/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
// Get the property name from the field path (e.g., "alerts" from path) // Get the full translation path from the field path
const fieldPathId = ( const fieldPathId = (
props as { fieldPathId?: { path?: (string | number)[] } } props as { fieldPathId?: { path?: (string | number)[] } }
).fieldPathId; ).fieldPathId;
let propertyName: string | undefined; let propertyName: string | undefined;
let translationPath: string | undefined;
const path = fieldPathId?.path; const path = fieldPathId?.path;
if (path) { if (path) {
translationPath = buildTranslationPath(path);
// Also get the last property name for fallback label generation
for (let i = path.length - 1; i >= 0; i -= 1) { for (let i = path.length - 1; i >= 0; i -= 1) {
const segment = path[i]; const segment = path[i];
if (typeof segment === "string") { if (typeof segment === "string") {
@ -65,8 +76,8 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
const i18nNs = formContext?.i18nNamespace; const i18nNs = formContext?.i18nNamespace;
let inferredLabel: string | undefined; let inferredLabel: string | undefined;
if (i18nNs && propertyName) { if (i18nNs && translationPath) {
const translated = t(`${propertyName}.label`, { const translated = t(`${translationPath}.label`, {
ns: i18nNs, ns: i18nNs,
defaultValue: "", defaultValue: "",
}); });
@ -78,8 +89,8 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
inferredLabel = inferredLabel ?? fallbackLabel; inferredLabel = inferredLabel ?? fallbackLabel;
let inferredDescription: string | undefined; let inferredDescription: string | undefined;
if (i18nNs && propertyName) { if (i18nNs && translationPath) {
const translated = t(`${propertyName}.description`, { const translated = t(`${translationPath}.description`, {
ns: i18nNs, ns: i18nNs,
defaultValue: "", defaultValue: "",
}); });

View File

@ -21,7 +21,6 @@ import { LprSection } from "@/components/config-form/sections/LprSection";
import { NotificationsSection } from "@/components/config-form/sections/NotificationsSection"; import { NotificationsSection } from "@/components/config-form/sections/NotificationsSection";
import { OnvifSection } from "@/components/config-form/sections/OnvifSection"; import { OnvifSection } from "@/components/config-form/sections/OnvifSection";
import { LiveSection } from "@/components/config-form/sections/LiveSection"; import { LiveSection } from "@/components/config-form/sections/LiveSection";
import { SemanticSearchSection } from "@/components/config-form/sections/SemanticSearchSection";
import { TimestampSection } from "@/components/config-form/sections/TimestampSection"; import { TimestampSection } from "@/components/config-form/sections/TimestampSection";
import { useAllCameraOverrides } from "@/hooks/use-config-override"; import { useAllCameraOverrides } from "@/hooks/use-config-override";
import type { FrigateConfig } from "@/types/frigateConfig"; import type { FrigateConfig } from "@/types/frigateConfig";
@ -291,12 +290,6 @@ const CameraConfigContent = memo(function CameraConfigContent({
component: LprSection, component: LprSection,
showOverrideIndicator: true, showOverrideIndicator: true,
}, },
{
key: "semantic_search",
i18nNamespace: "config/semantic_search",
component: SemanticSearchSection,
showOverrideIndicator: false,
},
{ {
key: "mqtt", key: "mqtt",
i18nNamespace: "config/camera_mqtt", i18nNamespace: "config/camera_mqtt",