/* 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 * as z from 'zod' import type { Resolver } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { RotateCcw } from 'lucide-react' import { useTranslation } from 'react-i18next' import { Button } from '@/components/ui/button' import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form' import { Input } from '@/components/ui/input' import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Textarea } from '@/components/ui/textarea' import { FormDirtyIndicator } from '../components/form-dirty-indicator' import { FormNavigationGuard } from '../components/form-navigation-guard' import { SettingsSection } from '../components/settings-section' import { useSettingsForm } from '../hooks/use-settings-form' import { useUpdateOption } from '../hooks/use-update-option' const _systemInfoSchema = z.object({ theme: z.object({ frontend: z.enum(['default', 'classic']), }), SystemName: z.string().min(1), ServerAddress: z.string().optional(), Logo: z.string().url().optional().or(z.literal('')), Footer: z.string().optional(), About: z.string().optional(), HomePageContent: z.string().optional(), legal: z.object({ user_agreement: z.string().optional(), privacy_policy: z.string().optional(), }), }) type SystemInfoFormValues = z.infer type SystemInfoSectionProps = { defaultValues: SystemInfoFormValues } function normalizeValue(value: unknown): string { if (value === undefined || value === null) return '' return typeof value === 'string' ? value : String(value) } export function SystemInfoSection({ defaultValues }: SystemInfoSectionProps) { const { t } = useTranslation() const updateOption = useUpdateOption() const normalizedDefaults: SystemInfoFormValues = { theme: { frontend: defaultValues.theme?.frontend === 'classic' ? 'classic' : 'default', }, SystemName: normalizeValue(defaultValues.SystemName), ServerAddress: normalizeValue(defaultValues.ServerAddress), Logo: normalizeValue(defaultValues.Logo), Footer: normalizeValue(defaultValues.Footer), About: normalizeValue(defaultValues.About), HomePageContent: normalizeValue(defaultValues.HomePageContent), legal: { user_agreement: normalizeValue(defaultValues.legal?.user_agreement), privacy_policy: normalizeValue(defaultValues.legal?.privacy_policy), }, } const systemInfoSchemaWithI18n = z.object({ theme: z.object({ frontend: z.enum(['default', 'classic']), }), SystemName: z.string().min(1, { error: () => t('System name is required'), }), ServerAddress: z.string().optional(), Logo: z.string().url().optional().or(z.literal('')), Footer: z.string().optional(), About: z.string().optional(), HomePageContent: z.string().optional(), legal: z.object({ user_agreement: z.string().optional(), privacy_policy: z.string().optional(), }), }) const { form, handleSubmit, handleReset, isDirty, isSubmitting } = useSettingsForm({ resolver: zodResolver(systemInfoSchemaWithI18n) as Resolver< SystemInfoFormValues, unknown, SystemInfoFormValues >, defaultValues: normalizedDefaults, onSubmit: async (_data, changedFields) => { for (const [key, value] of Object.entries(changedFields)) { let v = normalizeValue(value) if (key === 'ServerAddress') { v = v.replace(/\/+$/, '') } await updateOption.mutateAsync({ key, value: v, }) } }, }) return ( <>
( {t('Frontend Theme')} {t( 'Switch between the new frontend and the classic frontend. Changes take effect after page reload.' )} )} /> ( {t('System Name')} {t('The name displayed across the application')} )} /> ( {t('Server Address')} {t( 'The public URL of your server, used for OAuth callbacks, webhooks, and other external integrations' )} )} /> ( {t('Logo URL')} {t('URL to your logo image (optional)')} )} /> ( {t('Footer')}