feat: member portal Sprint 10 — Knowledge Base
- KnowledgeBoard + KnowledgeItem models + migration (DRAFT/PUBLISHED/ARCHIVED status, slug per tenant) - KnowledgeModule: admin CRUD for boards + items (auto-slug, status workflow) - Member endpoint GET /my/knowledge?q= — published items grouped by board, optional text search over question/answer - /my/knowledge page: search box + collapsible boards with accordion FAQ items - KnowledgeItem accordion + KnowledgeSearch (debounced URL-driven) client components - Knowledge nav item; register KnowledgeModule Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
interface Props {
|
||||
question: string;
|
||||
answer: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export function KnowledgeItem({ question, answer, tags }: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="border-b border-gray-100 last:border-0">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
className="w-full flex items-center justify-between gap-3 py-3.5 text-left"
|
||||
>
|
||||
<span className="text-sm font-medium text-gray-900">{question}</span>
|
||||
<span className={`text-gray-400 transition-transform shrink-0 ${open ? 'rotate-180' : ''}`}>⌄</span>
|
||||
</button>
|
||||
{open && (
|
||||
<div className="pb-4">
|
||||
<p className="text-sm text-gray-600 whitespace-pre-line leading-relaxed">{answer}</p>
|
||||
{tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1.5 mt-2">
|
||||
{tags.map((t) => (
|
||||
<span key={t} className="text-[10px] bg-gray-100 text-gray-500 rounded-full px-2 py-0.5">{t}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user