"use client"; import { useState, useEffect } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { RefreshCw, CheckCircle2, AlertCircle, Clock, ArrowUpDown, } from "lucide-react"; import { api } from "@/lib/api"; import type { Tenant, CalDAVConfig, CalDAVSyncResponse, } from "@/lib/types"; const SYNC_INTERVALS = [ { value: 5, label: "5 Minuten" }, { value: 15, label: "15 Minuten" }, { value: 30, label: "30 Minuten" }, { value: 60, label: "1 Stunde" }, { value: 120, label: "2 Stunden" }, { value: 360, label: "6 Stunden" }, ]; const emptyConfig: CalDAVConfig = { url: "", username: "", password: "", calendar_path: "", sync_enabled: false, sync_interval_minutes: 15, }; export function CalDAVSettings({ tenant }: { tenant: Tenant }) { const queryClient = useQueryClient(); const existing = (tenant.settings as Record)?.caldav as | Partial | undefined; const [config, setConfig] = useState({ ...emptyConfig, ...existing, }); const [showPassword, setShowPassword] = useState(false); // Reset form when tenant changes useEffect(() => { const caldav = (tenant.settings as Record)?.caldav as | Partial | undefined; setConfig({ ...emptyConfig, ...caldav }); }, [tenant]); // Fetch sync status const { data: syncStatus } = useQuery({ queryKey: ["caldav-status"], queryFn: () => api.get("/caldav/status"), refetchInterval: 30_000, }); // Save settings const saveMutation = useMutation({ mutationFn: (cfg: CalDAVConfig) => { const tenantId = typeof window !== "undefined" ? localStorage.getItem("kanzlai_tenant_id") : null; return api.put(`/tenants/${tenantId}/settings`, { caldav: cfg, }); }, onSuccess: (updated) => { queryClient.setQueryData(["tenant-current"], updated); toast.success("CalDAV-Einstellungen gespeichert"); }, onError: () => { toast.error("Fehler beim Speichern der CalDAV-Einstellungen"); }, }); // Trigger sync const syncMutation = useMutation({ mutationFn: () => api.post("/caldav/sync"), onSuccess: (result) => { queryClient.invalidateQueries({ queryKey: ["caldav-status"] }); if (result.status === "ok") { toast.success( `Synchronisierung abgeschlossen: ${result.sync.items_pushed} gesendet, ${result.sync.items_pulled} empfangen` ); } else { toast.error("Synchronisierung mit Fehlern abgeschlossen"); } }, onError: () => { toast.error("Fehler bei der Synchronisierung"); }, }); const handleSave = (e: React.FormEvent) => { e.preventDefault(); saveMutation.mutate(config); }; const hasConfig = config.url && config.username && config.password; return (
{/* CalDAV Configuration Form */}
setConfig((c) => ({ ...c, url: e.target.value })) } placeholder="https://dav.example.com/dav" className="w-full rounded-md border border-neutral-200 px-3 py-1.5 text-sm outline-none focus:border-neutral-400 focus:ring-1 focus:ring-neutral-400" />
setConfig((c) => ({ ...c, username: e.target.value })) } placeholder="user@example.com" className="w-full rounded-md border border-neutral-200 px-3 py-1.5 text-sm outline-none focus:border-neutral-400 focus:ring-1 focus:ring-neutral-400" />
setConfig((c) => ({ ...c, password: e.target.value })) } placeholder="••••••••" className="w-full rounded-md border border-neutral-200 px-3 py-1.5 pr-16 text-sm outline-none focus:border-neutral-400 focus:ring-1 focus:ring-neutral-400" />
setConfig((c) => ({ ...c, calendar_path: e.target.value })) } placeholder="/dav/calendars/user/default/" className="w-full rounded-md border border-neutral-200 px-3 py-1.5 text-sm outline-none focus:border-neutral-400 focus:ring-1 focus:ring-neutral-400" />

Pfad zum Kalender auf dem CalDAV-Server

{/* Sync Settings */}
{hasConfig && ( )}
{/* Sync Status */} {syncStatus && syncStatus.last_sync_at !== null && ( )}
); } function SyncStatusDisplay({ data }: { data: CalDAVSyncResponse }) { const hasErrors = data.sync?.errors && data.sync.errors.length > 0; const lastSync = data.sync?.last_sync_at ? new Date(data.sync.last_sync_at) : null; return (
{hasErrors ? ( ) : ( )}

{hasErrors ? "Letzte Synchronisierung mit Fehlern" : "Letzte Synchronisierung erfolgreich"}

{lastSync && ( {lastSync.toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit", year: "numeric", })}{" "} {lastSync.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit", })} )} {data.sync.items_pushed} gesendet, {data.sync.items_pulled}{" "} empfangen {data.sync.sync_duration && ( Dauer: {data.sync.sync_duration} )}
{hasErrors && (
{data.sync.errors!.map((err, i) => (

{err}

))}
)}
); }