"use client"; import { useEffect, useState } from "react"; import { signOut, useSession } from "next-auth/react"; export default function SettingsPage() { const { data } = useSession(); const [viewToken, setViewToken] = useState(null); const [viewId, setViewId] = useState(null); const [subscribedCategories, setSubscribedCategories] = useState>(new Set()); const [allCategories, setAllCategories] = useState<{ id: string; name: string }[]>([]); const [categoryError, setCategoryError] = useState(null); const [categoryStatus, setCategoryStatus] = useState(null); const [error, setError] = useState(null); const [status, setStatus] = useState(null); const [profileError, setProfileError] = useState(null); const [profileStatus, setProfileStatus] = useState(null); const [theme, setTheme] = useState<"light" | "dark">("light"); const [copyStatus, setCopyStatus] = useState<"success" | "error" | null>(null); const loadView = async () => { try { const response = await fetch("/api/views/default"); if (!response.ok) return; const payload = await response.json(); setViewToken(payload.token); setViewId(payload.id); const ids = new Set( (payload.categories || []).map((item: { categoryId: string }) => item.categoryId) ); setSubscribedCategories(ids); } catch { // ignore } }; const loadCategories = async () => { try { const response = await fetch("/api/categories"); if (!response.ok) return; setAllCategories(await response.json()); } catch { // ignore } }; useEffect(() => { if (data?.user) { loadView(); loadCategories(); } }, [data?.user]); useEffect(() => { if (typeof window === "undefined") return; const saved = window.localStorage.getItem("theme"); const next = saved === "dark" ? "dark" : "light"; setTheme(next); document.documentElement.dataset.theme = next; }, []); const rotateToken = async () => { setError(null); setStatus(null); setCopyStatus(null); const response = await fetch("/api/views/default/rotate", { method: "POST" }); if (!response.ok) { const payload = await response.json(); setError(payload.error || "Token konnte nicht erneuert werden."); return; } const payload = await response.json(); setViewToken(payload.token); setStatus("Neuer iCal-Link erstellt."); }; const updateProfile = async (event: React.FormEvent) => { event.preventDefault(); setProfileError(null); setProfileStatus(null); const formData = new FormData(event.currentTarget); const payload = { currentPassword: formData.get("currentPassword"), newEmail: formData.get("newEmail"), newPassword: formData.get("newPassword") }; const response = await fetch("/api/profile", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); if (!response.ok) { const data = await response.json(); setProfileError(data.error || "Profil konnte nicht aktualisiert werden."); return; } const dataRes = await response.json(); setProfileStatus("Profil aktualisiert."); if (dataRes.changedEmail || dataRes.changedPassword) { setProfileStatus("Profil aktualisiert. Bitte erneut anmelden."); await signOut({ callbackUrl: "/login" }); } }; const baseUrl = typeof window === "undefined" ? "" : window.location.origin; const icalUrl = viewToken ? `${baseUrl}/api/ical/${viewToken}` : ""; const applyTheme = (next: "light" | "dark") => { setTheme(next); if (typeof window !== "undefined") { window.localStorage.setItem("theme", next); document.documentElement.dataset.theme = next; } }; const copyIcalUrl = async () => { if (!icalUrl) return; setCopyStatus(null); try { if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(icalUrl); } else { const textarea = document.createElement("textarea"); textarea.value = icalUrl; textarea.setAttribute("readonly", "true"); textarea.style.position = "absolute"; textarea.style.left = "-9999px"; document.body.appendChild(textarea); textarea.select(); document.execCommand("copy"); document.body.removeChild(textarea); } setCopyStatus("success"); } catch { setCopyStatus("error"); } window.setTimeout(() => setCopyStatus(null), 2500); }; const toggleCategory = async (categoryId: string) => { if (!viewId) return; setCategoryError(null); setCategoryStatus(null); const isSubscribed = subscribedCategories.has(categoryId); const response = await fetch(`/api/views/${viewId}/categories`, { method: isSubscribed ? "DELETE" : "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ categoryId }) }); if (!response.ok) { const payload = await response.json(); setCategoryError(payload.error || "Kategorien konnten nicht aktualisiert werden."); return; } const next = new Set(subscribedCategories); if (isSubscribed) { next.delete(categoryId); setCategoryStatus("Kategorie entfernt."); } else { next.add(categoryId); setCategoryStatus("Kategorie abonniert."); } setSubscribedCategories(next); }; return (

Profil

Einstellungen

{profileStatus && (

{profileStatus}

)} {profileError &&

{profileError}

}

Darstellung

Theme

iCal

Persönlicher Kalenderlink

Dein Link kann in externen Kalender-Apps abonniert werden.

{viewToken ? (

iCal URL

{copyStatus && (
{copyStatus === "success" ? "Kopiert" : "Fehler"}
)}

{icalUrl}

) : (

iCal-Link wird geladen...

)} {status &&

{status}

} {error &&

{error}

}

Kategorien

Kategorien abonnieren

{allCategories.length === 0 ? (

Noch keine Kategorien vorhanden.

) : (
{allCategories.map((category) => { const isSubscribed = subscribedCategories.has(category.id); return ( ); })}
)} {categoryStatus && (

{categoryStatus}

)} {categoryError &&

{categoryError}

}
); }