mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-21 03:09:02 +03:00
clean up now obsolete namespaces
This commit is contained in:
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const AudioSection = createConfigSection({
|
||||
sectionPath: "audio",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const AudioTranscriptionSection = createConfigSection({
|
||||
sectionPath: "audio_transcription",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["enabled", "language", "device", "model_size", "live_enabled"],
|
||||
hiddenFields: ["enabled_in_config"],
|
||||
|
||||
@@ -81,8 +81,6 @@ export interface BaseSectionProps {
|
||||
export interface CreateSectionOptions {
|
||||
/** The config path for this section (e.g., "detect", "record") */
|
||||
sectionPath: string;
|
||||
/** i18n namespace for this section (e.g., "config/detect") */
|
||||
i18nNamespace: string;
|
||||
/** Default section configuration */
|
||||
defaultConfig: SectionConfig;
|
||||
}
|
||||
@@ -92,7 +90,6 @@ export interface CreateSectionOptions {
|
||||
*/
|
||||
export function createConfigSection({
|
||||
sectionPath,
|
||||
i18nNamespace,
|
||||
defaultConfig,
|
||||
}: CreateSectionOptions) {
|
||||
const cameraUpdateTopicMap: Record<string, string> = {
|
||||
@@ -131,7 +128,7 @@ export function createConfigSection({
|
||||
showTitle,
|
||||
}: BaseSectionProps) {
|
||||
const { t, i18n } = useTranslation([
|
||||
i18nNamespace,
|
||||
level === "camera" ? "config/cameras" : "config/global",
|
||||
"config/cameras",
|
||||
"views/settings",
|
||||
"common",
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const BirdseyeSection = createConfigSection({
|
||||
sectionPath: "birdseye",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["enabled", "mode", "order"],
|
||||
hiddenFields: [],
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const CameraMqttSection = createConfigSection({
|
||||
sectionPath: "mqtt",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const CameraUiSection = createConfigSection({
|
||||
sectionPath: "ui",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["dashboard", "order"],
|
||||
hiddenFields: [],
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const DetectSection = createConfigSection({
|
||||
sectionPath: "detect",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const FaceRecognitionSection = createConfigSection({
|
||||
sectionPath: "face_recognition",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["enabled", "min_area"],
|
||||
hiddenFields: [],
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const FfmpegSection = createConfigSection({
|
||||
sectionPath: "ffmpeg",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: [
|
||||
"inputs",
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const LiveSection = createConfigSection({
|
||||
sectionPath: "live",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["stream_name", "height", "quality"],
|
||||
fieldGroups: {},
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const LprSection = createConfigSection({
|
||||
sectionPath: "lpr",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["enabled", "expire_time", "min_area", "enhancement"],
|
||||
hiddenFields: [],
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const MotionSection = createConfigSection({
|
||||
sectionPath: "motion",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const NotificationsSection = createConfigSection({
|
||||
sectionPath: "notifications",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["enabled", "email"],
|
||||
fieldGroups: {},
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const ObjectsSection = createConfigSection({
|
||||
sectionPath: "objects",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["track", "alert", "detect", "filters"],
|
||||
fieldGroups: {
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const OnvifSection = createConfigSection({
|
||||
sectionPath: "onvif",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: [
|
||||
"host",
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const RecordSection = createConfigSection({
|
||||
sectionPath: "record",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const ReviewSection = createConfigSection({
|
||||
sectionPath: "review",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["alerts", "detections", "genai"],
|
||||
fieldGroups: {},
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const SemanticSearchSection = createConfigSection({
|
||||
sectionPath: "semantic_search",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["triggers"],
|
||||
hiddenFields: [],
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const SnapshotsSection = createConfigSection({
|
||||
sectionPath: "snapshots",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
|
||||
@@ -5,7 +5,6 @@ import { createConfigSection } from "./BaseSection";
|
||||
|
||||
export const TimestampSection = createConfigSection({
|
||||
sectionPath: "timestamp_style",
|
||||
i18nNamespace: "config/global",
|
||||
defaultConfig: {
|
||||
fieldOrder: ["position", "format", "color", "thickness"],
|
||||
hiddenFields: ["effect", "enabled_in_config"],
|
||||
|
||||
@@ -11,13 +11,9 @@ export function DescriptionFieldTemplate(props: DescriptionFieldProps) {
|
||||
|
||||
const isCameraLevel = formContext?.level === "camera";
|
||||
const sectionI18nPrefix = formContext?.sectionI18nPrefix;
|
||||
const i18nNamespace = formContext?.i18nNamespace;
|
||||
const effectiveNamespace = isCameraLevel ? "config/cameras" : i18nNamespace;
|
||||
const effectiveNamespace = isCameraLevel ? "config/cameras" : "config/global";
|
||||
|
||||
const { t, i18n } = useTranslation([
|
||||
effectiveNamespace || i18nNamespace || "common",
|
||||
i18nNamespace || "common",
|
||||
]);
|
||||
const { t, i18n } = useTranslation([effectiveNamespace, "common"]);
|
||||
|
||||
let resolvedDescription = description;
|
||||
|
||||
|
||||
@@ -69,14 +69,11 @@ export function ObjectFieldTemplate(props: ObjectFieldTemplateProps) {
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
|
||||
const isCameraLevel = formContext?.level === "camera";
|
||||
const effectiveNamespace = isCameraLevel
|
||||
? "config/cameras"
|
||||
: formContext?.i18nNamespace;
|
||||
const effectiveNamespace = isCameraLevel ? "config/cameras" : "config/global";
|
||||
const sectionI18nPrefix = formContext?.sectionI18nPrefix;
|
||||
|
||||
const { t, i18n } = useTranslation([
|
||||
effectiveNamespace || formContext?.i18nNamespace || "common",
|
||||
formContext?.i18nNamespace || "common",
|
||||
effectiveNamespace,
|
||||
"config/groups",
|
||||
"common",
|
||||
]);
|
||||
|
||||
@@ -52,10 +52,8 @@ i18n
|
||||
"views/system",
|
||||
"views/exports",
|
||||
"views/explore",
|
||||
// Config namespaces: single consolidated global file + camera-level keys
|
||||
"config/global",
|
||||
"config/cameras",
|
||||
// keep these for backwards compatibility with explicit ns usage
|
||||
"config/validation",
|
||||
"config/groups",
|
||||
],
|
||||
|
||||
@@ -203,98 +203,81 @@ const CameraConfigContent = memo(function CameraConfigContent({
|
||||
|
||||
const sections: Array<{
|
||||
key: string;
|
||||
i18nNamespace: string;
|
||||
component: typeof DetectSection;
|
||||
showOverrideIndicator?: boolean;
|
||||
}> = [
|
||||
{
|
||||
key: "detect",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: DetectSection,
|
||||
},
|
||||
{
|
||||
key: "ffmpeg",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: FfmpegSection,
|
||||
showOverrideIndicator: true,
|
||||
},
|
||||
{
|
||||
key: "record",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: RecordSection,
|
||||
},
|
||||
{
|
||||
key: "snapshots",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: SnapshotsSection,
|
||||
},
|
||||
{
|
||||
key: "motion",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: MotionSection,
|
||||
},
|
||||
{
|
||||
key: "objects",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: ObjectsSection,
|
||||
},
|
||||
{
|
||||
key: "review",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: ReviewSection,
|
||||
},
|
||||
{ key: "audio", i18nNamespace: "config/cameras", component: AudioSection },
|
||||
{ key: "audio", component: AudioSection },
|
||||
{
|
||||
key: "audio_transcription",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: AudioTranscriptionSection,
|
||||
showOverrideIndicator: true,
|
||||
},
|
||||
{
|
||||
key: "notifications",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: NotificationsSection,
|
||||
},
|
||||
{ key: "live", i18nNamespace: "config/cameras", component: LiveSection },
|
||||
{ key: "live", component: LiveSection },
|
||||
{
|
||||
key: "birdseye",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: BirdseyeSection,
|
||||
showOverrideIndicator: true,
|
||||
},
|
||||
{
|
||||
key: "face_recognition",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: FaceRecognitionSection,
|
||||
showOverrideIndicator: true,
|
||||
},
|
||||
{
|
||||
key: "lpr",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: LprSection,
|
||||
showOverrideIndicator: true,
|
||||
},
|
||||
{
|
||||
key: "mqtt",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: CameraMqttSection,
|
||||
showOverrideIndicator: false,
|
||||
},
|
||||
{
|
||||
key: "onvif",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: OnvifSection,
|
||||
showOverrideIndicator: false,
|
||||
},
|
||||
{
|
||||
key: "ui",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: CameraUiSection,
|
||||
showOverrideIndicator: false,
|
||||
},
|
||||
{
|
||||
key: "timestamp_style",
|
||||
i18nNamespace: "config/cameras",
|
||||
component: TimestampSection,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -30,25 +30,22 @@ import { cn } from "@/lib/utils";
|
||||
|
||||
// Shared sections that can be overridden at camera level
|
||||
const sharedSections = [
|
||||
{ key: "detect", i18nNamespace: "config/global", component: DetectSection },
|
||||
{ key: "record", i18nNamespace: "config/global", component: RecordSection },
|
||||
{ key: "detect", component: DetectSection },
|
||||
{ key: "record", component: RecordSection },
|
||||
{
|
||||
key: "snapshots",
|
||||
i18nNamespace: "config/global",
|
||||
component: SnapshotsSection,
|
||||
},
|
||||
{ key: "motion", i18nNamespace: "config/global", component: MotionSection },
|
||||
{ key: "motion", component: MotionSection },
|
||||
{
|
||||
key: "objects",
|
||||
i18nNamespace: "config/global",
|
||||
component: ObjectsSection,
|
||||
},
|
||||
{ key: "review", i18nNamespace: "config/global", component: ReviewSection },
|
||||
{ key: "audio", i18nNamespace: "config/global", component: AudioSection },
|
||||
{ key: "live", i18nNamespace: "config/global", component: LiveSection },
|
||||
{ key: "review", component: ReviewSection },
|
||||
{ key: "audio", component: AudioSection },
|
||||
{ key: "live", component: LiveSection },
|
||||
{
|
||||
key: "timestamp_style",
|
||||
i18nNamespace: "config/global",
|
||||
component: TimestampSection,
|
||||
},
|
||||
];
|
||||
@@ -61,12 +58,10 @@ const globalSectionConfigs: Record<
|
||||
hiddenFields?: string[];
|
||||
advancedFields?: string[];
|
||||
liveValidate?: boolean;
|
||||
i18nNamespace: string;
|
||||
uiSchema?: Record<string, unknown>;
|
||||
}
|
||||
> = {
|
||||
mqtt: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
"host",
|
||||
@@ -93,12 +88,10 @@ const globalSectionConfigs: Record<
|
||||
liveValidate: true,
|
||||
},
|
||||
database: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: ["path"],
|
||||
advancedFields: [],
|
||||
},
|
||||
auth: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
"reset_admin_password",
|
||||
@@ -130,17 +123,14 @@ const globalSectionConfigs: Record<
|
||||
},
|
||||
},
|
||||
tls: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: ["enabled", "cert", "key"],
|
||||
advancedFields: [],
|
||||
},
|
||||
networking: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: ["ipv6"],
|
||||
advancedFields: [],
|
||||
},
|
||||
proxy: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [
|
||||
"header_map",
|
||||
"logout_url",
|
||||
@@ -152,7 +142,6 @@ const globalSectionConfigs: Record<
|
||||
liveValidate: true,
|
||||
},
|
||||
ui: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [
|
||||
"timezone",
|
||||
"time_format",
|
||||
@@ -163,22 +152,18 @@ const globalSectionConfigs: Record<
|
||||
advancedFields: [],
|
||||
},
|
||||
logger: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: ["default", "logs"],
|
||||
advancedFields: ["logs"],
|
||||
},
|
||||
environment_vars: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [],
|
||||
advancedFields: [],
|
||||
},
|
||||
telemetry: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: ["network_interfaces", "stats", "version_check"],
|
||||
advancedFields: [],
|
||||
},
|
||||
birdseye: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
"restream",
|
||||
@@ -193,7 +178,6 @@ const globalSectionConfigs: Record<
|
||||
advancedFields: ["width", "height", "quality", "inactivity_threshold"],
|
||||
},
|
||||
ffmpeg: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [
|
||||
"path",
|
||||
"global_args",
|
||||
@@ -253,12 +237,10 @@ const globalSectionConfigs: Record<
|
||||
},
|
||||
},
|
||||
detectors: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [],
|
||||
advancedFields: [],
|
||||
},
|
||||
model: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [
|
||||
"path",
|
||||
"labelmap_path",
|
||||
@@ -278,7 +260,6 @@ const globalSectionConfigs: Record<
|
||||
hiddenFields: ["labelmap", "attributes_map"],
|
||||
},
|
||||
genai: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [
|
||||
"provider",
|
||||
"api_key",
|
||||
@@ -291,22 +272,18 @@ const globalSectionConfigs: Record<
|
||||
hiddenFields: ["genai.enabled_in_config"],
|
||||
},
|
||||
classification: {
|
||||
i18nNamespace: "config/global",
|
||||
hiddenFields: ["custom"],
|
||||
advancedFields: [],
|
||||
},
|
||||
semantic_search: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: ["enabled", "reindex", "model", "model_size", "device"],
|
||||
advancedFields: ["reindex", "device"],
|
||||
},
|
||||
audio_transcription: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: ["enabled", "language", "device", "model_size", "live_enabled"],
|
||||
advancedFields: ["language", "device", "model_size"],
|
||||
},
|
||||
face_recognition: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
"model_size",
|
||||
@@ -331,7 +308,6 @@ const globalSectionConfigs: Record<
|
||||
],
|
||||
},
|
||||
lpr: {
|
||||
i18nNamespace: "config/global",
|
||||
fieldOrder: [
|
||||
"enabled",
|
||||
"model_size",
|
||||
@@ -406,11 +382,7 @@ function GlobalConfigSection({
|
||||
title,
|
||||
}: GlobalConfigSectionProps) {
|
||||
const sectionConfig = globalSectionConfigs[sectionKey];
|
||||
const { t } = useTranslation([
|
||||
sectionConfig?.i18nNamespace || "common",
|
||||
"views/settings",
|
||||
"common",
|
||||
]);
|
||||
const { t } = useTranslation(["config/global", "views/settings", "common"]);
|
||||
const [pendingData, setPendingData] = useState<unknown | null>(null);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [formKey, setFormKey] = useState(0);
|
||||
@@ -522,8 +494,8 @@ function GlobalConfigSection({
|
||||
liveValidate={sectionConfig.liveValidate}
|
||||
uiSchema={sectionConfig.uiSchema}
|
||||
showSubmit={false}
|
||||
formContext={{ sectionI18nPrefix: sectionKey }}
|
||||
i18nNamespace="config/global"
|
||||
formContext={{ level: "global", sectionI18nPrefix: sectionKey }}
|
||||
disabled={isSaving}
|
||||
/>
|
||||
|
||||
@@ -585,13 +557,11 @@ export default function GlobalConfigView() {
|
||||
} else if (activeTab === "system") {
|
||||
return systemSections.map((key) => ({
|
||||
key,
|
||||
i18nNamespace: globalSectionConfigs[key].i18nNamespace,
|
||||
component: null, // Uses GlobalConfigSection instead
|
||||
}));
|
||||
} else {
|
||||
return integrationSections.map((key) => ({
|
||||
key,
|
||||
i18nNamespace: globalSectionConfigs[key].i18nNamespace,
|
||||
component: null,
|
||||
}));
|
||||
}
|
||||
@@ -721,21 +691,12 @@ export default function GlobalConfigView() {
|
||||
{activeTab === "system" && (
|
||||
<>
|
||||
{systemSections.map((sectionKey) => {
|
||||
const ns = globalSectionConfigs[sectionKey].i18nNamespace;
|
||||
const sectionTitle =
|
||||
ns === "config/global"
|
||||
? t(`${sectionKey}.label`, {
|
||||
ns: "config/global",
|
||||
defaultValue:
|
||||
sectionKey.charAt(0).toUpperCase() +
|
||||
sectionKey.slice(1).replace(/_/g, " "),
|
||||
})
|
||||
: t("label", {
|
||||
ns,
|
||||
defaultValue:
|
||||
sectionKey.charAt(0).toUpperCase() +
|
||||
sectionKey.slice(1).replace(/_/g, " "),
|
||||
});
|
||||
const sectionTitle = t(`${sectionKey}.label`, {
|
||||
ns: "config/global",
|
||||
defaultValue:
|
||||
sectionKey.charAt(0).toUpperCase() +
|
||||
sectionKey.slice(1).replace(/_/g, " "),
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -760,21 +721,12 @@ export default function GlobalConfigView() {
|
||||
{activeTab === "integrations" && (
|
||||
<>
|
||||
{integrationSections.map((sectionKey) => {
|
||||
const ns = globalSectionConfigs[sectionKey].i18nNamespace;
|
||||
const sectionTitle =
|
||||
ns === "config/global"
|
||||
? t(`${sectionKey}.label`, {
|
||||
ns: "config/global",
|
||||
defaultValue:
|
||||
sectionKey.charAt(0).toUpperCase() +
|
||||
sectionKey.slice(1).replace(/_/g, " "),
|
||||
})
|
||||
: t("label", {
|
||||
ns,
|
||||
defaultValue:
|
||||
sectionKey.charAt(0).toUpperCase() +
|
||||
sectionKey.slice(1).replace(/_/g, " "),
|
||||
});
|
||||
const sectionTitle = t(`${sectionKey}.label`, {
|
||||
ns: "config/global",
|
||||
defaultValue:
|
||||
sectionKey.charAt(0).toUpperCase() +
|
||||
sectionKey.slice(1).replace(/_/g, " "),
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user