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 (
{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}
)}
))}
)
}