"use client"; import * as React from "react"; import { toast } from "sonner"; import { useTheme } from "next-themes"; import { Loader2, Save, Sun, Moon, Monitor, Bell, HardDrive, Trash2, RefreshCw, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Switch } from "@/components/ui/switch"; import { Card, CardContent, CardHeader, CardTitle, CardDescription, } from "@/components/ui/card"; const NOTIFICATION_STORAGE_KEY = "echo-ocr-notification-prefs"; type NotificationPrefs = { processingComplete: boolean; processingErrors: boolean; folderWatchAlerts: boolean; cleanupReminders: boolean; }; const DEFAULT_NOTIFICATION_PREFS: NotificationPrefs = { processingComplete: true, processingErrors: true, folderWatchAlerts: true, cleanupReminders: false, }; function loadNotificationPrefs(): NotificationPrefs { if (typeof window === "undefined") return DEFAULT_NOTIFICATION_PREFS; try { const raw = localStorage.getItem(NOTIFICATION_STORAGE_KEY); if (raw) return { ...DEFAULT_NOTIFICATION_PREFS, ...JSON.parse(raw) }; } catch {} return DEFAULT_NOTIFICATION_PREFS; } export default function GeneralSettingsPage() { const { theme, setTheme } = useTheme(); const [sourceRetention, setSourceRetention] = React.useState(30); const [imageRetention, setImageRetention] = React.useState(180); const [loading, setLoading] = React.useState(true); const [saving, setSaving] = React.useState(false); const [cleanupStatus, setCleanupStatus] = React.useState<{ sourcesEligible: number; imagesEligible: number; } | null>(null); const [cleaning, setCleaning] = React.useState(false); const [notifPrefs, setNotifPrefs] = React.useState( DEFAULT_NOTIFICATION_PREFS ); React.useEffect(() => { setNotifPrefs(loadNotificationPrefs()); }, []); React.useEffect(() => { fetch("/api/settings") .then((r) => r.json()) .then((data) => { setSourceRetention(data.sourceRetentionDays || 30); setImageRetention(data.imageRetentionDays || 180); setLoading(false); }) .catch(() => setLoading(false)); fetch("/api/cleanup") .then((r) => r.json()) .then((data) => { if (data.sourcesEligible !== undefined) setCleanupStatus(data); }) .catch(() => {}); }, []); const updateNotifPref = (key: keyof NotificationPrefs, value: boolean) => { setNotifPrefs((prev) => { const next = { ...prev, [key]: value }; try { localStorage.setItem(NOTIFICATION_STORAGE_KEY, JSON.stringify(next)); } catch {} return next; }); toast.success("Notification preference updated"); }; const handleSave = async () => { setSaving(true); try { const res = await fetch("/api/settings", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sourceRetentionDays: sourceRetention, imageRetentionDays: imageRetention, }), }); if (!res.ok) throw new Error(); toast.success("Settings saved"); } catch { toast.error("Failed to save settings"); } finally { setSaving(false); } }; const runCleanup = async () => { setCleaning(true); try { const res = await fetch("/api/cleanup", { method: "POST" }); if (res.ok) { const data = await res.json(); toast.success( `Cleaned ${data.sourcesDeleted ?? 0} sources and ${data.imagesDeleted ?? 0} images` ); setCleanupStatus({ sourcesEligible: 0, imagesEligible: 0 }); } } catch { toast.error("Cleanup failed"); } finally { setCleaning(false); } }; if (loading) { return (
); } return (
{/* Appearance */} Appearance
{[ { value: "light", icon: Sun, label: "Light" }, { value: "dark", icon: Moon, label: "Dark" }, { value: "system", icon: Monitor, label: "System" }, ].map((opt) => ( ))}
{/* Notifications */} Notifications {( [ ["processingComplete", "Processing Complete"], ["processingErrors", "Processing Errors"], ["folderWatchAlerts", "Folder Watch Alerts"], ["cleanupReminders", "Cleanup Reminders"], ] as const ).map(([key, label]) => (
updateNotifPref(key, v)} />
))}
{/* Storage & Retention */} Storage & Retention Automatically clean up old files to save storage.
setSourceRetention(parseInt(e.target.value) || 30) } />
setImageRetention(parseInt(e.target.value) || 180) } />
{cleanupStatus && (
{cleanupStatus.sourcesEligible} sources,{" "} {cleanupStatus.imagesEligible} images eligible for cleanup
)}
); }