import { createFileRoute, Link } from "@tanstack/react-router"; import { useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useApiKeys, useAuth, useCreateApiKey, useDeleteApiKey, } from "../hooks/useAuth"; import { useCurrency } from "../hooks/useCurrency"; import { useExportItems, useImportItems } from "../hooks/useItems"; import { useLanguage } from "../hooks/useLanguage"; import { useUpdateSetting } from "../hooks/useSettings"; import { useWeightUnit } from "../hooks/useWeightUnit"; import type { Currency, WeightUnit } from "../lib/formatters"; import i18n from "../lib/i18n"; const LANGUAGES = [ { value: "en", label: "English" }, { value: "de", label: "Deutsch" }, ]; const UNITS: WeightUnit[] = ["g", "oz", "lb", "kg"]; const CURRENCIES: { value: Currency; label: string }[] = [ { value: "USD", label: "$" }, { value: "EUR", label: "\u20AC" }, { value: "GBP", label: "\u00A3" }, { value: "JPY", label: "\u00A5" }, { value: "CAD", label: "CA$" }, { value: "AUD", label: "A$" }, ]; export const Route = createFileRoute("/settings")({ component: SettingsPage, }); function ApiKeySection() { const { t } = useTranslation("settings"); const { data: keys } = useApiKeys(); const createKey = useCreateApiKey(); const deleteKey = useDeleteApiKey(); const [name, setName] = useState(""); const [newKey, setNewKey] = useState(null); async function handleCreate(e: React.FormEvent) { e.preventDefault(); const result = await createKey.mutateAsync({ name }); setNewKey(result.key); setName(""); } return (

{t("apiKeys.title")}

{t("apiKeys.description")}

{newKey && (

{t("apiKeys.copyWarning")}

{newKey}
)}
setName(e.target.value)} required className="flex-1 px-3 py-2 border border-gray-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-gray-200" />
{keys && keys.length > 0 && (
{keys.map((key) => (
{key.name} {key.keyPrefix}...
))}
)}
); } function ImportExportSection() { const { t } = useTranslation("settings"); const exportItems = useExportItems(); const importItems = useImportItems(); const fileInputRef = useRef(null); const [importResult, setImportResult] = useState<{ imported: number; createdCategories: string[]; errors: string[]; } | null>(null); async function handleFileChange(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; setImportResult(null); try { const result = await importItems.mutateAsync(file); setImportResult(result); } catch (err) { setImportResult({ imported: 0, createdCategories: [], errors: [(err as Error).message], }); } // Reset so the same file can be imported again if needed if (fileInputRef.current) fileInputRef.current.value = ""; } return (

{t("importExport.title")}

{t("importExport.description")}

{importResult && (
0 && importResult.imported === 0 ? "bg-red-50 border border-red-200 text-red-700" : "bg-green-50 border border-green-200 text-green-700" }`} > {importResult.imported > 0 && (

{t("importExport.imported", { count: importResult.imported })}

)} {importResult.createdCategories.length > 0 && (

{t("importExport.newCategories", { categories: importResult.createdCategories.join(", "), })}

)} {importResult.errors.map((err, i) => ( // biome-ignore lint/suspicious/noArrayIndexKey: static error list

{err}

))} {importResult.imported === 0 && importResult.errors.length === 0 && (

{t("importExport.noItemsFound")}

)}
)}
); } const LOCALE_CURRENCY_MAP: Record = { de: "EUR", fr: "EUR", es: "EUR", it: "EUR", nl: "EUR", pt: "EUR", en: "USD", ja: "JPY", }; function getSuggestedCurrency(): Currency | null { try { const lang = navigator.language; // Check full locale first (e.g., en-GB → GBP) if (lang.startsWith("en-GB")) return "GBP"; if (lang.startsWith("en-AU")) return "AUD"; if (lang.startsWith("en-CA") || lang.startsWith("fr-CA")) return "CAD"; // Fall back to language prefix const prefix = lang.split("-")[0]; return LOCALE_CURRENCY_MAP[prefix] ?? null; } catch { return null; } } function SettingsPage() { const { t } = useTranslation("settings"); const unit = useWeightUnit(); const language = useLanguage(); const { currency, showConversions } = useCurrency(); const updateSetting = useUpdateSetting(); const { data: auth } = useAuth(); const [suggestionDismissed, setSuggestionDismissed] = useState(false); const suggestedCurrency = getSuggestedCurrency(); const showSuggestion = !suggestionDismissed && suggestedCurrency && suggestedCurrency !== currency; return (
← {t("common:actions.back")}

{t("title")}

{showSuggestion && (
Based on your location, we suggest{" "} {CURRENCIES.find((c) => c.value === suggestedCurrency)?.label ?? suggestedCurrency}{" "} ({suggestedCurrency})
)}

{t("language.title")}

{t("language.description")}

{LANGUAGES.map((lang) => ( ))}

{t("weightUnit.title")}

{t("weightUnit.description")}

{UNITS.map((u) => ( ))}

{t("currency.title")}

{t("currency.description")}

{CURRENCIES.map((c) => ( ))}

Show Converted Prices

Display approximate conversions when local price is not available

{auth?.authenticated && (
)}
); }