feat(kb): AI Draft button in KB article editor
All checks were successful
build-and-release / build (push) Successful in 1m51s
All checks were successful
build-and-release / build (push) Successful in 1m51s
- Imports Sparkles icon from lucide-solid - Adds [drafting, setDrafting] signal - Adds aiDraft() function: calls POST /api/admin/kb/articles/ai-draft with current title + resolved category name; fills content field and summary field (only if summary is currently empty) from the response - AI Draft button appears inline with the Content label, styled in the orange/amber palette; shows 'Generating...' while in flight - Adds monospace font to the content textarea and a helper hint line reminding admins to review before saving Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8c03cd0eac
commit
11041ba91c
1 changed files with 48 additions and 3 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { A } from '@solidjs/router';
|
||||
import { createMemo, createResource, createSignal, For, Show } from 'solid-js';
|
||||
import { BookOpen, Plus } from 'lucide-solid';
|
||||
import { BookOpen, Plus, Sparkles } from 'lucide-solid';
|
||||
|
||||
const API = '';
|
||||
|
||||
|
|
@ -233,6 +233,7 @@ export default function KnowledgeBasePage(props: KnowledgeBasePageProps) {
|
|||
const [categoryId, setCategoryId] = createSignal('');
|
||||
const [status, setStatus] = createSignal<'PUBLISHED' | 'DRAFT'>('DRAFT');
|
||||
const [saving, setSaving] = createSignal(false);
|
||||
const [drafting, setDrafting] = createSignal(false);
|
||||
const [formError, setFormError] = createSignal('');
|
||||
|
||||
const [catFormId, setCatFormId] = createSignal('');
|
||||
|
|
@ -306,6 +307,38 @@ export default function KnowledgeBasePage(props: KnowledgeBasePageProps) {
|
|||
setTab('editor');
|
||||
};
|
||||
|
||||
const aiDraft = async () => {
|
||||
if (!title().trim()) {
|
||||
setFormError('Enter a title before generating a draft.');
|
||||
return;
|
||||
}
|
||||
setFormError('');
|
||||
setDrafting(true);
|
||||
try {
|
||||
const catName = (categories() || []).find((c) => c.id === categoryId())?.name;
|
||||
const res = await fetch(`${API}/api/admin/kb/articles/ai-draft`, {
|
||||
method: 'POST',
|
||||
headers: authHeaders(true),
|
||||
body: JSON.stringify({
|
||||
title: title(),
|
||||
category: catName,
|
||||
topic_hints: undefined,
|
||||
}),
|
||||
});
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok) {
|
||||
setFormError(data.error || 'AI generation failed');
|
||||
return;
|
||||
}
|
||||
if (data.content) setContent(data.content);
|
||||
if (data.summary && !summary().trim()) setSummary(data.summary);
|
||||
} catch {
|
||||
setFormError('AI generation failed — check your connection.');
|
||||
} finally {
|
||||
setDrafting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveArticle = async () => {
|
||||
if (!title().trim()) {
|
||||
setFormError('Title is required');
|
||||
|
|
@ -675,8 +708,20 @@ export default function KnowledgeBasePage(props: KnowledgeBasePageProps) {
|
|||
<input value={summary()} onInput={(e) => setSummary(e.currentTarget.value)} style="width:100%;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px" />
|
||||
</div>
|
||||
<div style="grid-column:1 / -1">
|
||||
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:4px">Content</label>
|
||||
<textarea rows={14} value={content()} onInput={(e) => setContent(e.currentTarget.value)} style="width:100%;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px;resize:vertical" />
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px">
|
||||
<label style="display:block;font-size:13px;font-weight:600">Content</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={aiDraft}
|
||||
disabled={drafting()}
|
||||
style={`display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border-radius:6px;border:1px solid #FDBA74;background:${drafting() ? '#F3F4F6' : '#FFF5F0'};font-size:11px;font-weight:700;color:${drafting() ? '#9CA3AF' : '#92400E'};cursor:${drafting() ? 'default' : 'pointer'}`}
|
||||
>
|
||||
<Sparkles size={12} />
|
||||
{drafting() ? 'Generating…' : 'AI Draft'}
|
||||
</button>
|
||||
</div>
|
||||
<textarea rows={14} value={content()} onInput={(e) => setContent(e.currentTarget.value)} style="width:100%;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:14px;resize:vertical;font-family:monospace" />
|
||||
<p style="margin:4px 0 0;font-size:11px;color:#9CA3AF">Markdown supported. AI Draft fills this from the title + category — review before saving.</p>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:8px">
|
||||
<input type="checkbox" checked={status() === 'PUBLISHED'} onChange={(e) => setStatus(e.currentTarget.checked ? 'PUBLISHED' : 'DRAFT')} />
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue