/* 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 (
(
{t('Enable io.net deployments')} {t('Enable io.net model deployment service in console')}
field.onChange(v)} disabled={updateOption.isPending || isSubmitting} />
)} /> {enabled ? ( <> ( {t('io.net API Key')}
{t('Used to authenticate with io.net deployment API')}
)} /> {t('How to get an io.net API Key')}
  • {t('Open the io.net console API Keys page')}
  • {t( 'Set Project to io.cloud when creating/selecting key' )}
  • {t('Copy the key and paste it here')}
{testState.ok === true ? (
{t('Connection successful')} {t('Connected to io.net service normally.')}
) : null} {testState.ok === false && testState.error ? (
{t('Connection failed')} {t(testState.error)}
) : null} ) : null}
) }