Each page.tsx was >500 LOC (610/602/596). Extracted React components to _components/ and custom hook to _hooks/ per-route, reducing all three page.tsx orchestrators to 107/229/120 LOC respectively. Zero behavior changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
108 lines
4.7 KiB
TypeScript
108 lines
4.7 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { WorkshopSession, statusLabels } from './_components/types'
|
|
import { SessionCard } from './_components/SessionCard'
|
|
import { CreateSessionModal } from './_components/CreateSessionModal'
|
|
import { SessionDetailView } from './_components/SessionDetailView'
|
|
import { useWorkshopSessions } from './_hooks/useWorkshopSessions'
|
|
|
|
export default function WorkshopPage() {
|
|
const { sessions, loading, loadSessions, handleDelete } = useWorkshopSessions()
|
|
const [showCreate, setShowCreate] = useState(false)
|
|
const [selectedSession, setSelectedSession] = useState<WorkshopSession | null>(null)
|
|
const [filter, setFilter] = useState<string>('all')
|
|
|
|
const filteredSessions = filter === 'all'
|
|
? sessions
|
|
: sessions.filter(s => s.status === filter)
|
|
|
|
if (selectedSession) {
|
|
return (
|
|
<div className="p-6 max-w-6xl mx-auto">
|
|
<SessionDetailView
|
|
session={selectedSession}
|
|
onBack={() => { setSelectedSession(null); loadSessions() }}
|
|
onRefresh={() => {
|
|
loadSessions().then(() => {
|
|
const updated = sessions.find(s => s.id === selectedSession.id)
|
|
if (updated) setSelectedSession(updated)
|
|
})
|
|
}}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="p-6 max-w-6xl mx-auto">
|
|
<div className="flex items-center justify-between mb-6">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-gray-900">Compliance Workshops</h1>
|
|
<p className="text-sm text-gray-500 mt-1">
|
|
Kollaborative Workshops fuer DSFA, UCCA und andere Compliance-Prozesse
|
|
</p>
|
|
</div>
|
|
<button onClick={() => setShowCreate(true)}
|
|
className="px-4 py-2 bg-purple-600 text-white text-sm rounded-lg hover:bg-purple-700 flex items-center gap-2">
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
</svg>
|
|
Neuer Workshop
|
|
</button>
|
|
</div>
|
|
|
|
{/* Stats */}
|
|
<div className="grid grid-cols-4 gap-4 mb-6">
|
|
{[
|
|
{ label: 'Gesamt', value: sessions.length, color: 'text-gray-900' },
|
|
{ label: 'Aktiv', value: sessions.filter(s => s.status === 'ACTIVE').length, color: 'text-green-600' },
|
|
{ label: 'Entwurf', value: sessions.filter(s => s.status === 'DRAFT').length, color: 'text-gray-600' },
|
|
{ label: 'Abgeschlossen', value: sessions.filter(s => s.status === 'COMPLETED').length, color: 'text-purple-600' },
|
|
].map(stat => (
|
|
<div key={stat.label} className="bg-white rounded-xl border border-gray-200 p-4 text-center">
|
|
<div className={`text-2xl font-bold ${stat.color}`}>{stat.value}</div>
|
|
<div className="text-xs text-gray-500">{stat.label}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Filter */}
|
|
<div className="flex gap-2 mb-6">
|
|
{['all', 'DRAFT', 'ACTIVE', 'PAUSED', 'COMPLETED'].map(f => (
|
|
<button key={f} onClick={() => setFilter(f)}
|
|
className={`px-3 py-1.5 text-sm rounded-lg ${filter === f ? 'bg-purple-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200'}`}>
|
|
{f === 'all' ? 'Alle' : statusLabels[f] || f}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{loading ? (
|
|
<div className="text-center py-12 text-gray-500">Workshops werden geladen...</div>
|
|
) : filteredSessions.length === 0 ? (
|
|
<div className="text-center py-12">
|
|
<div className="text-gray-400 mb-2">
|
|
<svg className="w-12 h-12 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
</svg>
|
|
</div>
|
|
<p className="text-gray-500">Keine Workshops gefunden</p>
|
|
<button onClick={() => setShowCreate(true)} className="mt-3 text-sm text-purple-600 hover:text-purple-700">
|
|
Ersten Workshop erstellen
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
{filteredSessions.map(s => (
|
|
<SessionCard key={s.id} session={s} onSelect={setSelectedSession} onDelete={handleDelete} />
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{showCreate && (
|
|
<CreateSessionModal onClose={() => setShowCreate(false)} onCreated={() => { setShowCreate(false); loadSessions() }} />
|
|
)}
|
|
</div>
|
|
)
|
|
}
|