feat: implement knowledge testing system with leaderboard, quiz generation, and PocketBase integration
This commit is contained in:
@@ -5,6 +5,7 @@ import Tag from '../../components/ui/Tag';
|
||||
import Button from '../../components/ui/Button';
|
||||
import Input from '../../components/ui/Input';
|
||||
import { storage } from '../../lib/storage';
|
||||
import * as db from '../../lib/db';
|
||||
import UploadZone from '../../components/admin/UploadZone';
|
||||
import KnowledgeGraph from '../../components/admin/KnowledgeGraph';
|
||||
import ContentManager from '../../components/admin/ContentManager';
|
||||
@@ -20,8 +21,9 @@ const Admin = () => {
|
||||
const [useSimulation, setUseSimulation] = useState(false);
|
||||
const [saveStatus, setSaveStatus] = useState(null);
|
||||
|
||||
const loadSources = () => {
|
||||
setSources(storage.get('admin:sources', []));
|
||||
const loadSources = async () => {
|
||||
const data = await db.getSources();
|
||||
setSources(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -44,11 +46,10 @@ const Admin = () => {
|
||||
setTimeout(() => setSaveStatus(null), 3000);
|
||||
};
|
||||
|
||||
const handleDeleteSource = (id) => {
|
||||
const handleDeleteSource = async (id) => {
|
||||
if (confirm('Are you sure you want to delete this source? This will not delete topics already extracted.')) {
|
||||
const updated = sources.filter(s => s.id !== id);
|
||||
storage.set('admin:sources', updated);
|
||||
setSources(updated);
|
||||
await db.deleteSource(id);
|
||||
await loadSources();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -63,10 +64,9 @@ const Admin = () => {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col md:flex-row h-[calc(100vh-64px)] overflow-hidden">
|
||||
{/* Admin Sidebar */}
|
||||
<div className="w-full md:w-64 bg-paper border-r border-bg-warm flex-shrink-0 flex flex-row md:flex-col p-4 gap-2 overflow-x-auto md:overflow-y-auto">
|
||||
<h2 className="hidden md:block text-teal font-bold text-lg mb-4 px-2">Knowledge Mgmt</h2>
|
||||
|
||||
|
||||
{navItems.map(({ key, icon: Icon, label, bottom }) => (
|
||||
<button
|
||||
key={key}
|
||||
@@ -79,15 +79,13 @@ const Admin = () => {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Admin Main Content */}
|
||||
<div className="flex-1 overflow-y-auto p-4 md:p-8 bg-bg pb-24 md:pb-8">
|
||||
|
||||
{/* ── Sources ─────────────────────────────── */}
|
||||
{activeTab === 'sources' && (
|
||||
<div className="animate-in fade-in duration-300 max-w-4xl mx-auto">
|
||||
<h1 className="text-3xl text-teal mb-2">Source Material</h1>
|
||||
<p className="text-fg-muted mb-8">Upload files to feed the AI knowledge extraction pipeline.</p>
|
||||
|
||||
|
||||
<UploadZone onUploadComplete={loadSources} />
|
||||
|
||||
<h3 className="text-xl mb-4 mt-12">Recent Sources</h3>
|
||||
@@ -109,7 +107,7 @@ const Admin = () => {
|
||||
<div>
|
||||
<p className="font-medium">{source.name}</p>
|
||||
<p className="text-xs text-fg-muted flex items-center gap-1 mt-1">
|
||||
<Clock size={12} /> {new Date(source.date).toLocaleString()}
|
||||
<Clock size={12} /> {new Date(source.created).toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,7 +127,6 @@ const Admin = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Content Manager ──────────────────────── */}
|
||||
{activeTab === 'content' && (
|
||||
<div className="animate-in fade-in duration-300 max-w-4xl mx-auto">
|
||||
<h1 className="text-3xl text-teal mb-2">Learning Content</h1>
|
||||
@@ -138,7 +135,6 @@ const Admin = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Test Manager ─────────────────────────── */}
|
||||
{activeTab === 'tests' && (
|
||||
<div className="animate-in fade-in duration-300 max-w-4xl mx-auto">
|
||||
<h1 className="text-3xl text-teal mb-2">Question Banks</h1>
|
||||
@@ -147,18 +143,16 @@ const Admin = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Knowledge Graph ──────────────────────── */}
|
||||
{activeTab === 'graph' && (
|
||||
<div className="animate-in fade-in duration-300 h-full flex flex-col">
|
||||
<h1 className="text-3xl text-teal mb-2">Knowledge Graph</h1>
|
||||
<p className="text-fg-muted mb-4">Visual map of all extracted Respellion knowledge.</p>
|
||||
<Card className="flex-1 p-0 border border-bg-warm overflow-hidden bg-paper min-h-[400px]">
|
||||
<KnowledgeGraph />
|
||||
<KnowledgeGraph />
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Team ────────────────────────────────── */}
|
||||
{activeTab === 'team' && (
|
||||
<div className="animate-in fade-in duration-300 max-w-4xl mx-auto">
|
||||
<h1 className="text-3xl text-teal mb-2">Team Management</h1>
|
||||
@@ -167,17 +161,16 @@ const Admin = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* ── Settings ────────────────────────────── */}
|
||||
{activeTab === 'settings' && (
|
||||
<div className="animate-in fade-in duration-300 max-w-2xl">
|
||||
<h1 className="text-3xl text-teal mb-2">Settings</h1>
|
||||
<p className="text-fg-muted mb-8">Manage API keys and application configuration.</p>
|
||||
|
||||
|
||||
<Card className="border border-bg-warm">
|
||||
<form onSubmit={saveSettings} className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-medium mb-4">AI Configuration</h3>
|
||||
<Input
|
||||
<Input
|
||||
label="Anthropic API Key"
|
||||
type="password"
|
||||
placeholder="sk-ant-..."
|
||||
@@ -185,7 +178,7 @@ const Admin = () => {
|
||||
onChange={(e) => setApiKey(e.target.value)}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<Input
|
||||
<Input
|
||||
label="Model ID"
|
||||
placeholder="claude-sonnet-4-20250514"
|
||||
value={model}
|
||||
@@ -205,9 +198,9 @@ const Admin = () => {
|
||||
<p className="text-sm text-fg-muted">Use simulated AI responses when the API is unavailable.</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="sr-only peer"
|
||||
<input
|
||||
type="checkbox"
|
||||
className="sr-only peer"
|
||||
checked={useSimulation}
|
||||
onChange={(e) => setUseSimulation(e.target.checked)}
|
||||
/>
|
||||
@@ -221,7 +214,7 @@ const Admin = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex items-center gap-4 pt-4">
|
||||
<Button type="submit">
|
||||
<Save size={18} className="mr-2" /> Save Settings
|
||||
|
||||
Reference in New Issue
Block a user