- replace hardcoded green/red/blue colors with standard shadcn semantic colors - use text-primary, text-secondary-foreground, text-muted-foreground instead of text-green-600/blue-600 - replace colored backgrounds with bg-secondary, bg-muted, bg-primary/5 - maintain proper contrast and accessibility with semantic color tokens - ensure consistent black/white theme across all components
117 lines
3.2 KiB
TypeScript
117 lines
3.2 KiB
TypeScript
import { useDeviceId } from "@/shared/hooks/use-device-id"
|
|
import { useEffect, useState } from "react"
|
|
import { useLegislation } from "../hooks/use-legislation"
|
|
import { useUserVote } from "../hooks/use-user-vote"
|
|
import { fetchLegislationResults } from "../lib/legislation-api"
|
|
import { VoteResult } from "./vote-result"
|
|
import { VoteWidget } from "./vote-widget"
|
|
|
|
interface LegislationDetailProps {
|
|
legislationId: number
|
|
}
|
|
|
|
export function LegislationDetail({ legislationId }: LegislationDetailProps) {
|
|
const { legislation, loading, error } = useLegislation(legislationId)
|
|
const { vote, castVote } = useUserVote(legislationId)
|
|
const [showFullText, setShowFullText] = useState(false)
|
|
const deviceId = useDeviceId()
|
|
const [results, setResults] = useState<{
|
|
pollId: number
|
|
votes: {
|
|
politicianId: number
|
|
name: string
|
|
fraction: string | null
|
|
vote: string
|
|
}[]
|
|
} | null>(null)
|
|
|
|
useEffect(() => {
|
|
if (!deviceId) return
|
|
fetchLegislationResults(legislationId, deviceId)
|
|
.then(setResults)
|
|
.catch(() => null)
|
|
}, [legislationId, deviceId])
|
|
|
|
if (loading) {
|
|
return <div className="p-4 text-center text-muted-foreground">Laden…</div>
|
|
}
|
|
|
|
if (error || !legislation) {
|
|
return (
|
|
<div className="p-4 text-center text-destructive">
|
|
{error ?? "Gesetzesvorlage nicht gefunden"}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="p-4 space-y-6">
|
|
<div>
|
|
<h1 className="text-lg font-semibold leading-tight">
|
|
{legislation.title}
|
|
</h1>
|
|
{legislation.beratungsstand && (
|
|
<span className="inline-block mt-2 text-xs px-2 py-1 rounded-full bg-secondary text-secondary-foreground">
|
|
{legislation.beratungsstand}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
{/* Summary or abstract */}
|
|
<div className="space-y-2">
|
|
{legislation.summary ? (
|
|
<p className="text-sm leading-relaxed">{legislation.summary}</p>
|
|
) : legislation.abstract ? (
|
|
<p className="text-sm leading-relaxed text-muted-foreground">
|
|
{legislation.abstract}
|
|
</p>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground italic">
|
|
Keine Zusammenfassung verfügbar
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Full text toggle */}
|
|
{legislation.fullText && (
|
|
<div>
|
|
<button
|
|
type="button"
|
|
onClick={() => setShowFullText(!showFullText)}
|
|
className="text-sm text-primary hover:underline"
|
|
>
|
|
{showFullText ? "Volltext ausblenden" : "Volltext anzeigen"}
|
|
</button>
|
|
{showFullText && (
|
|
<pre className="mt-2 p-3 bg-muted rounded-lg text-xs overflow-auto max-h-96 whitespace-pre-wrap">
|
|
{legislation.fullText}
|
|
</pre>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
{/* Vote section */}
|
|
<div className="space-y-3">
|
|
<h2 className="text-sm font-medium">Dein Vote</h2>
|
|
<VoteWidget currentVote={vote} onVote={castVote} />
|
|
{vote && (
|
|
<p className="text-xs text-muted-foreground text-center">
|
|
Du hast mit{" "}
|
|
<span className="font-medium">
|
|
{vote === "ja" ? "Ja" : vote === "nein" ? "Nein" : "Enthaltung"}
|
|
</span>{" "}
|
|
gestimmt. Du kannst deine Stimme jederzeit ändern.
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{results && vote && (
|
|
<div className="space-y-3">
|
|
<h2 className="text-sm font-medium">Ergebnis</h2>
|
|
<VoteResult userVote={vote} politicianVotes={results.votes} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|