"use client" import { useEffect, useState } from "react" import { useParams, useRouter } from "next/navigation" import { closeVote, completeAssembly, downloadProtocol, getAssemblyDetail, sendInvitations, startAssembly, } from "@/services/assemblies" import { ArrowLeft, CheckCircle, FileDown, Play, Send, Square, Users, Vote, } from "lucide-react" import type { AssemblyDetail, AssemblyStatus } from "@/services/assemblies" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Progress } from "@/components/ui/progress" const statusLabels: Record = { PLANNED: "Geplant", INVITED: "Eingeladen", IN_PROGRESS: "Läuft", COMPLETED: "Abgeschlossen", CANCELLED: "Abgesagt", } const statusColors: Record = { PLANNED: "bg-gray-500/20 text-gray-400", INVITED: "bg-blue-500/20 text-blue-400", IN_PROGRESS: "bg-green-500/20 text-green-400", COMPLETED: "bg-emerald-500/20 text-emerald-400", CANCELLED: "bg-red-500/20 text-red-400", } export default function AssemblyDetailPage() { const { id } = useParams<{ id: string }>() const router = useRouter() const [detail, setDetail] = useState(null) const [loading, setLoading] = useState(true) useEffect(() => { if (id) loadDetail() // eslint-disable-next-line react-hooks/exhaustive-deps }, [id]) async function loadDetail() { try { const data = await getAssemblyDetail(id) setDetail(data) } catch (e) { console.error("Failed to load assembly", e) } finally { setLoading(false) } } async function handleSendInvitations() { await sendInvitations(id) loadDetail() } async function handleStart() { await startAssembly(id) loadDetail() } async function handleComplete() { await completeAssembly(id) loadDetail() } async function handleDownloadProtocol() { const blob = await downloadProtocol(id) const url = window.URL.createObjectURL(blob) const a = document.createElement("a") a.href = url a.download = `protokoll-${id}.pdf` a.click() window.URL.revokeObjectURL(url) } async function handleCloseVote(voteId: string) { await closeVote(voteId) loadDetail() } if (loading || !detail) { return (

Laden...

) } const { assembly, agendaItems, attendees, votes, quorum } = detail const quorumPercent = quorum.totalMembers > 0 ? Math.round((quorum.attendees / quorum.totalMembers) * 100) : 0 return (

{assembly.title}

{statusLabels[assembly.status]}

{new Date(assembly.scheduledAt).toLocaleDateString("de-DE", { weekday: "long", day: "2-digit", month: "long", year: "numeric", hour: "2-digit", minute: "2-digit", })} {assembly.location && ` • ${assembly.location}`}

{assembly.status === "PLANNED" && ( )} {(assembly.status === "PLANNED" || assembly.status === "INVITED") && ( )} {assembly.status === "IN_PROGRESS" && ( )} {assembly.status === "COMPLETED" && ( )}
{/* Quorum Card */} Beschlussfähigkeit
{quorum.attendees} / {quorum.totalMembers} {quorum.quorumMet ? "Beschlussfähig" : `${quorum.required} benötigt`}
{/* Agenda */} Tagesordnung {agendaItems.map((item) => (
TOP {item.position}

{item.title}

{item.description && (

{item.description}

)} {item.itemType}
))} {agendaItems.length === 0 && (

Keine Tagesordnungspunkte

)}
{/* Votes */} Abstimmungen {votes.map((vote) => (

{vote.title}

{vote.result ? ( {vote.result === "ACCEPTED" ? "Angenommen" : "Abgelehnt"} ) : ( )}
✓ {vote.yesCount} Ja ✗ {vote.noCount} Nein ○ {vote.abstainCount} Enthaltung
))} {votes.length === 0 && (

Keine Abstimmungen

)}
{/* Attendees */} Anwesende ({attendees.length}) {attendees.length > 0 ? (
{attendees.map((a) => (
{a.memberId.slice(0, 8)}... {a.proxyForMemberId && ( Vollmacht )}
))}
) : (

Noch keine Anwesenden eingecheckt

)}
) }