/*
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 { useEffect, useMemo, useRef } from 'react'
import * as z from 'zod'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
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 { Switch } from '@/components/ui/switch'
import {
SettingsForm,
SettingsSwitchContent,
SettingsSwitchItem,
} from '../components/settings-form-layout'
import { SettingsPageFormActions } from '../components/settings-page-context'
import { SettingsSection } from '../components/settings-section'
import { useResetForm } from '../hooks/use-reset-form'
import { useUpdateOption } from '../hooks/use-update-option'
import { safeNumberFieldProps } from '../utils/numeric-field'
const numericString = z.string().refine((value) => {
const trimmed = value.trim()
if (!trimmed) return true
return !Number.isNaN(Number(trimmed)) && Number(trimmed) >= 0
}, 'Enter a non-negative number or leave empty')
const monitoringSchema = z.object({
QuotaRemindThreshold: numericString,
perf_metrics_setting: z.object({
enabled: z.boolean(),
flush_interval: z.coerce.number().min(1),
bucket_time: z.enum(['minute', '5min', 'hour']),
retention_days: z.coerce.number().min(0),
}),
})
type MonitoringFormInput = z.input
type MonitoringFormValues = z.output
type FlatMonitoringDefaults = {
QuotaRemindThreshold: string
'perf_metrics_setting.enabled': boolean
'perf_metrics_setting.flush_interval': number
'perf_metrics_setting.bucket_time': 'minute' | '5min' | 'hour'
'perf_metrics_setting.retention_days': number
}
type MonitoringSettingsSectionProps = {
defaultValues: FlatMonitoringDefaults
}
const buildFormDefaults = (
defaults: MonitoringSettingsSectionProps['defaultValues']
): MonitoringFormInput => ({
QuotaRemindThreshold: defaults.QuotaRemindThreshold ?? '',
perf_metrics_setting: {
enabled: defaults['perf_metrics_setting.enabled'],
flush_interval: defaults['perf_metrics_setting.flush_interval'],
bucket_time: defaults['perf_metrics_setting.bucket_time'],
retention_days: defaults['perf_metrics_setting.retention_days'],
},
})
const normalizeDefaults = (
defaults: MonitoringSettingsSectionProps['defaultValues']
): FlatMonitoringDefaults => ({
QuotaRemindThreshold: (defaults.QuotaRemindThreshold ?? '').trim(),
'perf_metrics_setting.enabled': defaults['perf_metrics_setting.enabled'],
'perf_metrics_setting.flush_interval':
defaults['perf_metrics_setting.flush_interval'],
'perf_metrics_setting.bucket_time': defaults['perf_metrics_setting.bucket_time'],
'perf_metrics_setting.retention_days':
defaults['perf_metrics_setting.retention_days'],
})
const normalizeFormValues = (
values: MonitoringFormValues
): FlatMonitoringDefaults => ({
QuotaRemindThreshold: values.QuotaRemindThreshold.trim(),
'perf_metrics_setting.enabled': values.perf_metrics_setting.enabled,
'perf_metrics_setting.flush_interval':
values.perf_metrics_setting.flush_interval,
'perf_metrics_setting.bucket_time': values.perf_metrics_setting.bucket_time,
'perf_metrics_setting.retention_days':
values.perf_metrics_setting.retention_days,
})
export function MonitoringSettingsSection({
defaultValues,
}: MonitoringSettingsSectionProps) {
const { t } = useTranslation()
const updateOption = useUpdateOption()
const baselineRef = useRef(
normalizeDefaults(defaultValues)
)
const baselineSerializedRef = useRef(
JSON.stringify(normalizeDefaults(defaultValues))
)
const formDefaults = useMemo(
() => buildFormDefaults(defaultValues),
[defaultValues]
)
const form = useForm({
resolver: zodResolver(monitoringSchema),
defaultValues: formDefaults,
})
useResetForm(form, formDefaults)
useEffect(() => {
const normalized = normalizeDefaults(defaultValues)
const serialized = JSON.stringify(normalized)
if (serialized === baselineSerializedRef.current) return
baselineRef.current = normalized
baselineSerializedRef.current = serialized
}, [defaultValues])
const perfMetricsEnabled = form.watch('perf_metrics_setting.enabled')
const onSubmit = async (values: MonitoringFormValues) => {
const normalized = normalizeFormValues(values)
const updates = (
Object.keys(normalized) as Array
).filter((key) => normalized[key] !== baselineRef.current[key])
if (updates.length === 0) {
toast.info(t('No changes to save'))
return
}
for (const key of updates) {
await updateOption.mutateAsync({
key,
value: normalized[key],
})
}
baselineRef.current = normalized
baselineSerializedRef.current = JSON.stringify(normalized)
}
return (
)
}