/*
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 { useState } from 'react'
import { z } from 'zod'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { CheckCircle2, Loader2, XCircle } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
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 { Switch } from '@/components/ui/switch'
import { testDeploymentConnectionWithKey } from '@/features/models/api'
import { SettingsSection } from '../components/settings-section'
import { useUpdateOption } from '../hooks/use-update-option'
const schema = z.object({
enabled: z.boolean(),
apiKey: z.string().optional(),
})
// NOTE: react-hook-form resolver uses the schema input type
type Values = z.input
export function IoNetDeploymentSettingsSection({
defaultValues,
}: {
defaultValues: {
enabled: boolean
apiKey: string
}
}) {
const { t } = useTranslation()
const updateOption = useUpdateOption()
const form = useForm({
resolver: zodResolver(schema),
defaultValues: {
enabled: defaultValues.enabled,
apiKey: defaultValues.apiKey ?? '',
},
})
const { isDirty, isSubmitting } = form.formState
const enabled = form.watch('enabled')
const [testState, setTestState] = useState<{
loading: boolean
ok: boolean | null
error: string | null
}>({ loading: false, ok: null, error: null })
async function onSubmit(values: Values) {
const updates: Array<{ key: string; value: string }> = []
if (values.enabled !== defaultValues.enabled) {
updates.push({
key: 'model_deployment.ionet.enabled',
value: String(values.enabled),
})
}
if ((values.apiKey || '') !== (defaultValues.apiKey || '')) {
updates.push({
key: 'model_deployment.ionet.api_key',
value: String(values.apiKey || ''),
})
}
if (updates.length === 0) {
toast.info(t('No changes to save'))
return
}
for (const update of updates) {
await updateOption.mutateAsync(update)
}
form.reset(values)
}
const handleTestConnection = async () => {
setTestState({ loading: true, ok: null, error: null })
try {
const apiKey = form.getValues('apiKey')
const res = await testDeploymentConnectionWithKey(apiKey)
if (res?.success) {
setTestState({ loading: false, ok: true, error: null })
return
}
setTestState({
loading: false,
ok: false,
error: res?.message || t('Connection failed'),
})
} catch (err) {
setTestState({
loading: false,
ok: false,
error: err instanceof Error ? err.message : t('Connection failed'),
})
}
}
return (
)
}