You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
3.7 KiB
124 lines
3.7 KiB
import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons' |
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' |
|
import { Alert, Button, Card, Form, Input, Space, Spin, Typography } from 'antd' |
|
import { useState } from 'react' |
|
import { useNavigate } from 'react-router-dom' |
|
|
|
import llmApi from '../../api/llm' |
|
import settingsApi from '../../api/settings' |
|
|
|
const { Title, Text } = Typography |
|
|
|
const API_KEY = 'deepseek_api_key' |
|
|
|
export default function Settings() { |
|
const navigate = useNavigate() |
|
const queryClient = useQueryClient() |
|
const [checkResult, setCheckResult] = useState<{ ok: boolean; message: string } | null>(null) |
|
|
|
const { data: setting, isLoading } = useQuery({ |
|
queryKey: ['settings', API_KEY], |
|
queryFn: () => settingsApi.get(API_KEY), |
|
}) |
|
|
|
const saveMutation = useMutation({ |
|
mutationFn: (value: string) => settingsApi.update(API_KEY, value || null), |
|
onSuccess: () => { |
|
queryClient.invalidateQueries({ queryKey: ['settings', API_KEY] }) |
|
setCheckResult(null) |
|
}, |
|
}) |
|
|
|
const checkMutation = useMutation({ |
|
mutationFn: () => llmApi.check(), |
|
onSuccess: (data) => setCheckResult(data), |
|
}) |
|
|
|
const handleSave = (values: { api_key: string }) => { |
|
saveMutation.mutate(values.api_key) |
|
} |
|
|
|
if (isLoading) { |
|
return ( |
|
<div style={{ display: 'flex', justifyContent: 'center', padding: 80 }}> |
|
<Spin size="large" /> |
|
</div> |
|
) |
|
} |
|
|
|
return ( |
|
<div style={{ maxWidth: 600, margin: '40px auto', padding: '0 24px' }}> |
|
<Title level={2}>Настройки</Title> |
|
|
|
<Card title="AI-помощник (DeepSeek)"> |
|
<Text type="secondary" style={{ display: 'block', marginBottom: 16 }}> |
|
Введите API ключ DeepSeek для активации AI-функций при создании и редактировании |
|
тестов. Ключ хранится только на сервере. |
|
</Text> |
|
|
|
<Form layout="vertical" onFinish={handleSave} initialValues={{ api_key: setting?.value ?? '' }}> |
|
<Form.Item |
|
name="api_key" |
|
label="API ключ DeepSeek" |
|
> |
|
<Input.Password |
|
placeholder="sk-..." |
|
visibilityToggle |
|
style={{ fontFamily: 'monospace' }} |
|
/> |
|
</Form.Item> |
|
|
|
<Form.Item style={{ marginBottom: 0 }}> |
|
<Space wrap> |
|
<Button |
|
type="primary" |
|
htmlType="submit" |
|
loading={saveMutation.isPending} |
|
> |
|
Сохранить |
|
</Button> |
|
<Button |
|
onClick={() => checkMutation.mutate()} |
|
loading={checkMutation.isPending} |
|
disabled={!setting?.value} |
|
> |
|
Проверить подключение |
|
</Button> |
|
<Button onClick={() => navigate('/')}> |
|
На главную |
|
</Button> |
|
</Space> |
|
</Form.Item> |
|
</Form> |
|
|
|
{saveMutation.isSuccess && ( |
|
<Alert |
|
type="success" |
|
message="Ключ сохранён" |
|
showIcon |
|
style={{ marginTop: 16 }} |
|
closable |
|
/> |
|
)} |
|
|
|
{checkResult && ( |
|
<Alert |
|
type={checkResult.ok ? 'success' : 'error'} |
|
message={checkResult.message} |
|
icon={ |
|
checkResult.ok ? ( |
|
<CheckCircleOutlined /> |
|
) : ( |
|
<CloseCircleOutlined /> |
|
) |
|
} |
|
showIcon |
|
style={{ marginTop: 16 }} |
|
closable |
|
onClose={() => setCheckResult(null)} |
|
/> |
|
)} |
|
</Card> |
|
</div> |
|
) |
|
}
|
|
|