/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { zodResolver } from '@hookform/resolvers/zod' import { useEffect, useMemo, useRef } from 'react' import { useForm } from 'react-hook-form' import { useTranslation } from 'react-i18next' import * as z from 'zod' import { JsonCodeEditor } from '@/components/json-code-editor' import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form' import { Switch } from '@/components/ui/switch' import { SettingsAccordion } from '../components/settings-accordion' import { SettingsForm, SettingsSwitchContent, SettingsSwitchItem, } from '../components/settings-form-layout' import { SettingsPageFormActions } from '../components/settings-page-context' import { useUpdateOption } from '../hooks/use-update-option' import { formatJsonForEditor, normalizeJsonString } from './utils' type JsonToggleSectionProps = { value: string title: string toggleDescription?: string optionKey: string enabledKey: string defaultEnabled: boolean defaultValue: string fallbackValue?: string textareaLabel: string textareaDescription?: string placeholder?: string example?: string submitLabel?: string validate?: (parsed: unknown) => { valid: boolean; message?: string } } type JsonToggleFormValues = { enabled: boolean json: string } export function JsonToggleSection({ value, title, toggleDescription, optionKey, enabledKey, defaultEnabled, defaultValue, fallbackValue = '[]', textareaLabel, textareaDescription, placeholder, example, submitLabel = 'Save Changes', validate, }: JsonToggleSectionProps) { const { t } = useTranslation() const updateOption = useUpdateOption() const formattedDefault = useMemo( () => formatJsonForEditor(defaultValue, fallbackValue), [defaultValue, fallbackValue] ) const form = useForm({ mode: 'onChange', // Enable real-time validation resolver: zodResolver( z.object({ enabled: z.boolean(), json: z.string().superRefine((value, ctx) => { try { const normalized = normalizeJsonString(value, fallbackValue) const parsed = JSON.parse(normalized) if (validate) { const result = validate(parsed) if (!result.valid) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: result.message || 'JSON structure is invalid for this setting', }) } } } catch (error: unknown) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: (error instanceof Error ? error.message : null) || 'Invalid JSON data', }) } }), }) ), defaultValues: { enabled: defaultEnabled, json: formattedDefault, }, }) const initialNormalizedRef = useRef( normalizeJsonString(defaultValue, fallbackValue) ) const initialEnabledRef = useRef(defaultEnabled) useEffect(() => { initialNormalizedRef.current = normalizeJsonString( defaultValue, fallbackValue ) initialEnabledRef.current = defaultEnabled form.reset({ enabled: defaultEnabled, json: formatJsonForEditor(defaultValue, fallbackValue), }) }, [defaultEnabled, defaultValue, fallbackValue, form]) const onSubmit = async (values: JsonToggleFormValues) => { const updates: Array<{ key: string; value: string | boolean }> = [] if (values.enabled !== initialEnabledRef.current) { updates.push({ key: enabledKey, value: values.enabled }) } const normalized = normalizeJsonString(values.json, fallbackValue) if (normalized !== initialNormalizedRef.current) { updates.push({ key: optionKey, value: normalized }) } for (const update of updates) { await updateOption.mutateAsync(update) } } return (
{/* eslint-disable-next-line react-hooks/refs */} ( {t('Module availability')} {toggleDescription && ( {t(toggleDescription)} )} )} /> ( {textareaLabel} {textareaDescription && ( {t(textareaDescription)} )} {example && (
{example}
)}
)} />
) }