Files
hejyou_content_creation/frontend/src/pages/Home.tsx
admin d02788bd0e feat: CRM-Dashboard, Content-Verwaltung und Wort-Autocomplete
- Home-Seite nach Login mit Begrüßung und 3 Kacheln (Content erstellen, Content verwalten, User verwalten)
- AuthContext speichert User-Profil + Rolle; AdminRoute blockt Nicht-Admins
- Content verwalten (admin-only): Status-Dashboard pro Collection, Liste/Kachel-View, generisches Edit-Formular
- Nur aktive db_-Collections im Dashboard (alte pictures/objects/words/questions entfernt)
- Wort-Autocomplete in DrawIt: ab dem ersten Buchstaben Vorschläge aus db_words, Tastatur-Navigation, Duplikat-Filter
- Backend: /users/me Proxy, db-words/search Endpoint, generische Collection-Endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 23:37:48 +02:00

124 lines
4.0 KiB
TypeScript

import { useNavigate } from 'react-router-dom'
import { useAuth } from '../context/AuthContext'
import Topbar from '../components/Topbar'
const PenIcon = () => (
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 20h9" />
<path d="M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4 12.5-12.5z" />
</svg>
)
const FolderIcon = () => (
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7z" />
<line x1="8" y1="13" x2="16" y2="13" />
</svg>
)
const UsersIcon = () => (
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
</svg>
)
const LockIcon = () => (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="11" width="18" height="11" rx="2" />
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
)
interface TileProps {
icon: React.ReactNode
title: string
subtitle: string
onClick?: () => void
disabled?: boolean
comingSoon?: boolean
adminOnly?: boolean
locked?: boolean
}
function Tile({ icon, title, subtitle, onClick, disabled, comingSoon, adminOnly, locked }: TileProps) {
return (
<button
type="button"
className={`home-tile${disabled ? ' is-disabled' : ''}`}
onClick={disabled ? undefined : onClick}
disabled={disabled}
>
<div className="home-tile-icon">{icon}</div>
<div className="home-tile-body">
<div className="home-tile-title">
{title}
{adminOnly && (
<span className="home-tile-badge" title="Nur für Admins">
<LockIcon /> Admin
</span>
)}
{comingSoon && <span className="home-tile-badge home-tile-badge-soft">bald</span>}
</div>
<div className="home-tile-subtitle">{subtitle}</div>
</div>
{locked && <div className="home-tile-locked"><LockIcon /></div>}
</button>
)
}
export default function Home() {
const navigate = useNavigate()
const { user, isAdmin } = useAuth()
const firstName = user?.first_name?.trim() || user?.email?.split('@')[0] || 'da'
return (
<div className="app-shell">
<Topbar page="draw" />
<div className="home-page">
<div className="home-greeting">
<h1>Super, {firstName} ist wieder da,</h1>
<p>Was möchtest du heute machen?</p>
</div>
<div className="home-tiles">
<Tile
icon={<PenIcon />}
title="Content erstellen"
subtitle="Bilder annotieren, Objekte zuschneiden, Fragen generieren."
onClick={() => navigate('/draw')}
/>
<Tile
icon={<FolderIcon />}
title="Content verwalten"
subtitle={isAdmin
? 'Übersicht aller Collections nach Status — bearbeiten als Liste oder Kacheln.'
: 'Status-Dashboard für alle Collections. Nur für Admins verfügbar.'}
adminOnly
disabled={!isAdmin}
locked={!isAdmin}
onClick={() => navigate('/content')}
/>
<Tile
icon={<UsersIcon />}
title="User verwalten"
subtitle={isAdmin
? 'Admins, API-Tokens und Endnutzer — inkl. aktive Sessions.'
: 'User-Übersicht und aktive Sessions. Nur für Admins verfügbar.'}
adminOnly
comingSoon
disabled
locked={!isAdmin}
onClick={() => { /* Schritt 3 */ }}
/>
</div>
</div>
</div>
)
}