import { createFileRoute, Link, useNavigate } from "@tanstack/react-router" import { useState } from "react" import { useTranslation } from "react-i18next" import { useMutation } from "@tanstack/react-query" import { ArrowLeft, ChevronRight, Download, Key, LogOut, RefreshCw, ShieldBan, Sparkles, Target, Upload, User, } from "lucide-react" import { Button } from "@/components/ui/button" import { Switch } from "@/components/ui/switch" import { useAuth, useIsAdmin } from "@/components/auth-provider" import { API_URL } from "@/lib/api/client" import { getToken } from "@/lib/auth" import { reindexSearch } from "@/lib/api/users" import { useSettings, useUpdateSettings } from "@/hooks/use-goals" import { useDocumentTitle } from "@/hooks/use-document-title" export const Route = createFileRoute("/_app/settings/")({ component: SettingsPage, }) type SettingsItem = { label: string description?: string to: string icon: React.ReactNode } function SettingsPage() { const { t } = useTranslation() useDocumentTitle(t("settings.title")) const { logout } = useAuth() const isAdmin = useIsAdmin() const navigate = useNavigate() const account: SettingsItem[] = [ { label: t("settings.editProfile"), description: t("settings.editProfileDesc"), to: "/settings/edit-profile", icon: , }, ] const data: SettingsItem[] = [ { label: t("settings.import"), description: t("settings.importDesc"), to: "/settings/import", icon: , }, { label: t("settings.yearWrapUp"), description: t("settings.yearWrapUpDesc"), to: "/settings/wrapup", icon: , }, ] const integrations: SettingsItem[] = [ { label: t("settings.webhookTokens"), description: t("settings.webhookTokensDesc"), to: "/settings/webhooks", icon: , }, ] const social: SettingsItem[] = [ { label: isAdmin ? t("settings.blockedUsersAndDomains") : t("settings.blockedUsers"), description: isAdmin ? t("settings.blockedUsersDescAdmin") : t("settings.blockedUsersDesc"), to: "/settings/blocked", icon: , }, ] const handleLogout = () => { logout() navigate({ to: "/login" }) } return (

{t("settings.title")}

{isAdmin && }
) } function PrivacySection() { const { t } = useTranslation() const { data: settings } = useSettings() const updateMutation = useUpdateSettings() return (

{t("settings.privacy")}

{t("settings.federateGoals")}

{t("settings.federateGoalsDesc")}

updateMutation.mutate({ federate_goals: checked }) } disabled={updateMutation.isPending} />
) } function AdminActions() { const { t } = useTranslation() const reindex = useMutation({ mutationFn: reindexSearch, }) return (

{t("settings.admin")}

{t("settings.rebuildSearch")}

{reindex.isSuccess ? t("settings.rebuildSearchDone") : t("settings.rebuildSearchDesc")}

) } function ExportSection() { const { t } = useTranslation() const [exporting, setExporting] = useState(null) async function handleExport(format: "csv" | "json") { setExporting(format) try { const res = await fetch(`${API_URL}/api/v1/diary/export?format=${format}`, { headers: { Authorization: `Bearer ${getToken()}` }, }) const blob = await res.blob() const url = URL.createObjectURL(blob) const a = document.createElement("a") a.href = url a.download = `diary.${format}` a.click() URL.revokeObjectURL(url) } finally { setExporting(null) } } return (

{t("settings.export")}

{t("settings.export")}

{t("settings.exportDesc")}

) } function SettingsGroup({ label, items, }: { label: string items: SettingsItem[] }) { return (

{label}

{items.map((item) => ( {item.icon}

{item.label}

{item.description && (

{item.description}

)}
))}
) }