import * as React from 'react' import { CopyPlus, Save, Send } from 'lucide-react' import { toast } from 'sonner' import { AppEmptyState } from '@/components/app/AppEmptyState' import { AppField } from '@/components/app/AppField' import { AppPage } from '@/components/app/AppPage' import { AppPageHeader } from '@/components/app/AppPageHeader' import { AppSection } from '@/components/app/AppSection' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { FieldGroup } from '@/components/ui/field' import { Input } from '@/components/ui/input' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Textarea } from '@/components/ui/textarea' import { JsonEditor, parseJsonObject } from '@/features/ai-platform/components/JsonEditor' import { MobileEditGuard } from '@/features/ai-platform/components/MobileEditGuard' import { StatusBadge } from '@/features/ai-platform/status' import { formatDateTime } from '@/lib/format' import { ensureSkillDraft, getSkill, publishSkill, rollbackSkill, updateSkill, updateSkillDraft } from './api' import type { SkillDetail, SkillVersion } from './types' export function SkillDetailPage({ skillId }: { skillId: number }) { const [detail, setDetail] = React.useState(null) const [draft, setDraft] = React.useState(null) const [meta, setMeta] = React.useState({ name: '', description: '' }) const [prompt, setPrompt] = React.useState('') const [inputSchema, setInputSchema] = React.useState('{}') const [outputSchema, setOutputSchema] = React.useState('{}') const [exampleInput, setExampleInput] = React.useState('{}') const [error, setError] = React.useState('') const load = React.useCallback(async () => { try { const item = await getSkill(skillId) setDetail(item); setMeta({ name: item.name, description: item.description }) const nextDraft = item.versions.find((version) => version.status === 'draft') ?? null setDraft(nextDraft) if (nextDraft) { setPrompt(nextDraft.prompt_template) setInputSchema(JSON.stringify(nextDraft.input_schema, null, 2)) setOutputSchema(JSON.stringify(nextDraft.output_schema, null, 2)) setExampleInput(JSON.stringify(nextDraft.example_input, null, 2)) } setError('') } catch (reason) { setError(reason instanceof Error ? reason.message : '加载 Skill 失败') } }, [skillId]) React.useEffect(() => { void load() }, [load]) async function createDraft() { try { await ensureSkillDraft(skillId); toast.success('已从当前发布版本创建新草稿'); await load() } catch (reason) { toast.error(reason instanceof Error ? reason.message : '创建草稿失败') } } async function save() { try { await Promise.all([ updateSkill(skillId, meta), updateSkillDraft(skillId, { prompt_template: prompt, input_schema: parseJsonObject(inputSchema, '输入 Schema'), output_schema: parseJsonObject(outputSchema, '输出 Schema'), example_input: parseJsonObject(exampleInput, '示例输入') }) ]) toast.success('Skill 草稿已保存并通过校验'); await load() } catch (reason) { toast.error(reason instanceof Error ? reason.message : '保存失败') } } async function publish() { try { await publishSkill(skillId); toast.success('Skill 版本已发布'); await load() } catch (reason) { toast.error(reason instanceof Error ? reason.message : '发布失败') } } async function rollback(version: number) { try { await rollbackSkill(skillId, version); toast.success(`已回滚到 v${version}`); await load() } catch (reason) { toast.error(reason instanceof Error ? reason.message : '回滚失败') } } if (error) return }> if (!detail) return }>
return 当前 v{detail.active_version} : 尚未发布} actions={<>{draft ? <> : }} />}> 草稿编辑版本记录 {draft ?
setMeta({ ...meta, name: e.target.value })} /> setMeta({ ...meta, description: e.target.value })} />