feat(frontend): admin lesson tree CRUD

This commit is contained in:
2026-05-20 21:15:54 +02:00
parent 1d501ee50a
commit 1fd31e1001
3 changed files with 88 additions and 1 deletions

View File

@@ -0,0 +1,57 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';
import type { LessonTreeNode } from '@flashcard/shared';
import { lessonsApi } from '../api/lessons.js';
import { useLessons } from '../stores/lessonsStore.js';
export function LessonTree({ nodes, depth = 0 }: { nodes: LessonTreeNode[]; depth?: number }) {
const refresh = useLessons((s) => s.refresh);
const [addingTo, setAddingTo] = useState<number | null>(null);
const [name, setName] = useState('');
async function addChild(parentId: number | null) {
if (!name.trim()) return;
await lessonsApi.create({ name: name.trim(), parentId });
setName(''); setAddingTo(null);
await refresh();
}
async function rename(id: number, current: string) {
const next = prompt('Nieuwe naam', current);
if (next && next.trim() && next !== current) {
await lessonsApi.update(id, { name: next.trim() });
await refresh();
}
}
async function remove(id: number) {
if (!confirm('Verwijder les en alle onderliggende lessen en kaarten?')) return;
await lessonsApi.remove(id);
await refresh();
}
return (
<ul className="space-y-1">
{nodes.map((n) => (
<li key={n.id} style={{ paddingLeft: depth * 16 }}>
<div className="group flex items-center gap-2 rounded px-2 py-1 hover:bg-slate-100 dark:hover:bg-slate-800">
<Link to={`/admin/lessons/${n.id}`} className="flex-1">
{n.name} <span className="text-xs text-slate-500">({n.cardCount})</span>
</Link>
<button className="text-xs opacity-0 group-hover:opacity-100" onClick={() => setAddingTo(n.id)}>+ subles</button>
<button className="text-xs opacity-0 group-hover:opacity-100" onClick={() => rename(n.id, n.name)}>rename</button>
<button className="text-xs text-red-600 opacity-0 group-hover:opacity-100" onClick={() => remove(n.id)}>delete</button>
</div>
{addingTo === n.id && (
<div className="ml-6 flex gap-1 py-1">
<input className="rounded border px-2 py-1 text-sm dark:bg-slate-900" value={name} onChange={(e) => setName(e.target.value)} placeholder="Naam" />
<button className="rounded bg-blue-600 px-2 py-1 text-sm text-white" onClick={() => addChild(n.id)}>Toevoegen</button>
<button className="text-sm" onClick={() => setAddingTo(null)}>Annuleren</button>
</div>
)}
{n.children.length > 0 && <LessonTree nodes={n.children} depth={depth + 1} />}
</li>
))}
</ul>
);
}