feat: Content Verwaltung — ContentHub + Object Creation

Adds a full Content Management section:
- Nav link "Content" in header (all pages)
- ContentHub at /content with 3 tiles (Object/Statement/Content Creation)
- ObjectCreation at /content/objects:
  - Top bar: ← → pagination through all "uploaded" pictures
  - Left panel (1/5): existing objects per picture with their words, + button to start draw mode
  - Center: image on dark bg with canvas overlay for polygon drawing
  - Right words panel (1/5): picture words + new object words (each with search/create)
  - Right toolbar (1/5): draw instructions, "Auswahl hinzufügen", numbered selections list, "Objekt speichern" (requires ≥1 selection + ≥1 object word)
- Canvas drawing: click=add point, dblclick=close polygon, live preview line to mouse cursor
- Selections stored as [{points:[{x,y},...]}] (relative 0-1 coords) in objects.selections JSONB
- Object saved with status "draft", linked picture + words

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 21:53:23 +02:00
parent 6c8eaab034
commit e1d5390386
4 changed files with 774 additions and 4 deletions

View File

@@ -1,7 +1,7 @@
import { useNavigate } from 'react-router-dom';
import { getUser, logout } from '../lib/api';
export default function Layout({ children, back }) {
export default function Layout({ children, back, fullHeight = false }) {
const navigate = useNavigate();
const user = getUser();
@@ -11,8 +11,8 @@ export default function Layout({ children, back }) {
}
return (
<div className="min-h-screen bg-slate-100">
<header className="bg-indigo-700 text-white px-6 py-3 flex items-center justify-between shadow">
<div className={fullHeight ? 'h-screen flex flex-col bg-slate-100 overflow-hidden' : 'min-h-screen bg-slate-100'}>
<header className="bg-indigo-700 text-white px-6 py-3 flex items-center justify-between shadow flex-shrink-0">
<div className="flex items-center gap-3">
{back && (
<button
@@ -24,6 +24,11 @@ export default function Layout({ children, back }) {
)}
<span className="text-xl font-bold tracking-tight">🐟 snakkimo CMT</span>
</div>
<nav className="flex items-center gap-1 text-sm">
<button onClick={() => navigate('/')} className="text-indigo-200 hover:text-white px-3 py-1 rounded-lg transition-colors">Dashboard</button>
<button onClick={() => navigate('/db')} className="text-indigo-200 hover:text-white px-3 py-1 rounded-lg transition-colors">Datenbank</button>
<button onClick={() => navigate('/content')} className="text-indigo-200 hover:text-white px-3 py-1 rounded-lg transition-colors">Content</button>
</nav>
<div className="flex items-center gap-4 text-sm">
<span className="text-indigo-200">{user?.email}</span>
<button
@@ -34,7 +39,7 @@ export default function Layout({ children, back }) {
</button>
</div>
</header>
<main className="p-6">{children}</main>
<main className={fullHeight ? 'flex-1 overflow-hidden' : 'p-6'}>{children}</main>
</div>
);
}