5801cbf85b
- 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>
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
import { getMemberToken, getApiBaseUrl } from '../../_lib/api';
|
|
import { KnowledgeItem } from './KnowledgeItem';
|
|
import { KnowledgeSearch } from './KnowledgeSearch';
|
|
import Link from 'next/link';
|
|
|
|
interface Board {
|
|
id: string;
|
|
name: string;
|
|
description: string | null;
|
|
items: Array<{ id: string; question: string; answer: string; tags: string[] }>;
|
|
}
|
|
|
|
async function fetchKnowledge(token: string, q?: string): Promise<Board[]> {
|
|
const params = new URLSearchParams();
|
|
if (q) params.set('q', q);
|
|
const res = await fetch(`${getApiBaseUrl()}/my/knowledge${params.toString() ? `?${params.toString()}` : ''}`, {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
cache: 'no-store',
|
|
}).catch(() => null);
|
|
if (!res || !res.ok) return [];
|
|
return (await res.json()) as Board[];
|
|
}
|
|
|
|
export default async function KnowledgePage({
|
|
searchParams,
|
|
}: {
|
|
searchParams: Promise<{ q?: string }>;
|
|
}) {
|
|
const token = await getMemberToken();
|
|
if (!token) return null;
|
|
|
|
const { q } = await searchParams;
|
|
const boards = await fetchKnowledge(token, q);
|
|
|
|
return (
|
|
<div className="max-w-2xl mx-auto space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-xl font-semibold text-gray-900">Knowledge base</h1>
|
|
<Link href="/my" className="text-sm text-indigo-600 hover:underline">← Dashboard</Link>
|
|
</div>
|
|
|
|
<KnowledgeSearch />
|
|
|
|
{boards.length === 0 ? (
|
|
<div className="text-center py-16 text-gray-400">
|
|
<p className="text-sm">{q ? 'No matching articles.' : 'No knowledge articles yet.'}</p>
|
|
{!q && <p className="text-xs mt-1">Your chapter admin will publish FAQs and guides here.</p>}
|
|
</div>
|
|
) : (
|
|
<div className="space-y-5">
|
|
{boards.map((b) => (
|
|
<section key={b.id} className="bg-white rounded-xl border border-gray-200 p-5">
|
|
<h2 className="text-sm font-semibold text-gray-900">{b.name}</h2>
|
|
{b.description && <p className="text-xs text-gray-400 mt-0.5 mb-1">{b.description}</p>}
|
|
<div className="mt-1">
|
|
{b.items.map((item) => (
|
|
<KnowledgeItem key={item.id} question={item.question} answer={item.answer} tags={item.tags} />
|
|
))}
|
|
</div>
|
|
</section>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|