import { useEffect, useRef, useState } from "react"; const STORAGE_KEY = "changelog"; type ChangeType = | "Hinzugefügt" | "Geändert" | "Behoben" | "Entfernt" | "Verbessert" | "Sicherheit" | "Veraltet" | string; type ChangeEntry = { type: ChangeType; text: string | string[]; // aus localStorage kann es eine Liste sein }; type ChangelogItem = { version?: string; date: string; changes: ChangeEntry[]; }; type StoredChangelog = { title: string; items: ChangelogItem[]; }; const typeStyles: Record = { Hinzugefügt: "bg-emerald-500/15 text-emerald-300 ring-1 ring-inset ring-emerald-500/30", Geändert: "bg-blue-500/15 text-blue-300 ring-1 ring-inset ring-blue-500/30", Behoben: "bg-amber-500/15 text-amber-300 ring-1 ring-inset ring-amber-500/30", Entfernt: "bg-rose-500/15 text-rose-300 ring-1 ring-inset ring-rose-500/30", Verbessert: "bg-indigo-500/15 text-indigo-300 ring-1 ring-inset ring-indigo-500/30", Sicherheit: "bg-red-500/15 text-red-300 ring-1 ring-inset ring-red-500/30", Veraltet: "bg-zinc-700/30 text-zinc-300 ring-1 ring-inset ring-zinc-600/40", }; export default function Changelog() { const [open, setOpen] = useState(true); const [mounted, setMounted] = useState(false); const [data, setData] = useState(null); const [error, setError] = useState(null); const cardRef = useRef(null); useEffect(() => setMounted(true), []); const loadFromStorage = () => { try { setError(null); const raw = typeof window !== "undefined" ? localStorage.getItem(STORAGE_KEY) : null; if (!raw) { setData(null); return; } const parsed = JSON.parse(raw) as StoredChangelog; if (!parsed || !Array.isArray(parsed.items)) { throw new Error("Ungültiges Format"); } setData(parsed); } catch (e) { setError("Changelog konnte nicht aus localStorage geladen werden."); setData(null); } }; useEffect(() => { loadFromStorage(); }, []); useEffect(() => { const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") setOpen(false); }; const onClickOutside = (e: MouseEvent) => { if (cardRef.current && !cardRef.current.contains(e.target as Node)) { setOpen(false); } }; const onStorage = (e: StorageEvent) => { if (e.key === STORAGE_KEY) loadFromStorage(); }; window.addEventListener("keydown", onKey); document.addEventListener("mousedown", onClickOutside); window.addEventListener("storage", onStorage); return () => { window.removeEventListener("keydown", onKey); document.removeEventListener("mousedown", onClickOutside); window.removeEventListener("storage", onStorage); }; }, []); if (!open) return null; const title = data?.title ?? "Changelog"; const items = data?.items ?? []; return (
{/* Gradient border wrapper */}
{/* Card */}
{/* Accent top line */}
{/* Close button */} {/* Header */}

{title}

Aktuelle Änderungen und Updates

{/* Body */}
{error && (
{error}
)} {!error && items.length === 0 && (

Kein Changelog im localStorage gefunden (Key: {STORAGE_KEY} ).

)}
    {items.map((entry, idx) => (
  • {/* Kopfzeile je Release */}
    {entry.version && ( {entry.version} )}
    {/* Zweispaltiges Layout: Typ links, Text rechts (mit schöner Leselänge) */}
    {entry.changes.map((c, i) => (
    {c.type}
    {Array.isArray(c.text) ? (
      {c.text.map((t, k) => (
    • {t}
    • ))}
    ) : (

    {c.text}

    )}
    ))}
  • ))}
{/* soft bottom glow */}
); }