Files
certain/web/src/settings.jsx
T
GuidoandClaude Opus 4.8 2c795a57c8 feat: Phase 0 – Gerüst (FastAPI+SQLite, Multi-User-Login, React-App-Shell, Deploy)
Grundgerüst für Certain Finance:
- Backend: FastAPI + SQLAlchemy + SQLite, Alembic-Migrationen (Batch-Mode),
  argon2-Passwort-Hashing, server-seitige Sitzungen im Cookie, Erst-Admin,
  Benutzerverwaltung mit Temporär-Passwort, Einstellungen je Benutzer.
- Ein Prozess, ein Port: uvicorn liefert API (/api) und gebautes SPA aus,
  inkl. SPA-Deeplink-Fallback.
- Frontend: React 18 + Vite + JSX, Design-Tokens (Light/Dark), i18n DE/EN,
  Inline-SVG-Icons (keine Emojis), Login, Passwort-ändern, Dashboard-Platzhalter,
  Benutzer- und Einstellungsseiten.
- Deploy: nativer LXC-Installer + Update-Skript (systemd), env-Vorlage, Doku.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-19 18:27:42 +02:00

76 lines
2.1 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createContext, useCallback, useContext, useEffect, useState } from "react";
import i18n from "./i18n";
import { api } from "./api";
import { useAuth } from "./auth";
import { applyTheme } from "./theme";
import { formatMoney as fmtMoney, formatDate as fmtDate } from "./format";
const DEFAULTS = {
base_currency: "EUR",
locale: "de",
theme: "system",
date_format: "de",
budget_start_day: "1",
};
const SettingsContext = createContext({
...DEFAULTS,
formatMoney: (cents, currency) => fmtMoney(cents, currency),
formatDate: (iso) => fmtDate(iso, "de"),
update: async () => {},
reload: async () => {},
});
export function SettingsProvider({ children }) {
const { user } = useAuth();
const [values, setValues] = useState(DEFAULTS);
// Sprache und Thema anwenden, sobald sich die Werte ändern.
useEffect(() => {
if (i18n.language !== values.locale) i18n.changeLanguage(values.locale);
}, [values.locale]);
useEffect(() => {
applyTheme(values.theme);
}, [values.theme]);
const reload = useCallback(async () => {
try {
const rows = await api.listSettings();
const map = { ...DEFAULTS };
for (const r of rows) {
if (r.value != null) map[r.key] = r.value;
}
setValues(map);
} catch {
/* Einstellungen sind optional – Standardwerte bleiben bestehen. */
}
}, []);
useEffect(() => {
if (user) reload();
else setValues(DEFAULTS); // nach Logout auf Standard zurück
}, [user, reload]);
// Eine Einstellung setzen: erst lokal (sofort sichtbar), dann speichern.
const update = useCallback(async (key, value) => {
setValues((prev) => ({ ...prev, [key]: value }));
await api.setSetting(key, value);
}, []);
const value = {
...values,
baseCurrency: values.base_currency,
formatMoney: (cents, currency) => fmtMoney(cents, currency ?? values.base_currency),
formatDate: (iso) => fmtDate(iso, values.locale),
update,
reload,
};
return <SettingsContext.Provider value={value}>{children}</SettingsContext.Provider>;
}
export function useSettings() {
return useContext(SettingsContext);
}