This commit is contained in:
timka123 committed 2026-10-18 18:02:05 +03:00
commit 17a0a3c4c9
121 files changed
+17641

No files matched your search

+75
View File
@@ -0,0 +1,75 @@
import { useEffect, useMemo, useState } from "react";
import { Alert, Box, CssBaseline, LinearProgress, Snackbar, ThemeProvider, useMediaQuery } from "@mui/material";
import { Navigate, Route, Routes } from "react-router-dom";
import AppShell from "./components/AppShell";
import { SettingsProvider, useSettings } from "./lib/settings";
import { buildTheme, type ThemeMode } from "./theme";
import BlockerPage from "./pages/BlockerPage";
import ReportPage from "./pages/ReportPage";
import SettingsPage from "./pages/SettingsPage";
import TasksPage from "./pages/TasksPage";
import TimerPage from "./pages/TimerPage";
function Shell() {
const { settings, loading, error } = useSettings();
const prefersDark = useMediaQuery("(prefers-color-scheme: dark)");
const [toast, setToast] = useState<string | null>(null);
const mode: ThemeMode = useMemo(() => {
const preference = settings?.appearance.theme ?? "system";
if (preference === "system") return prefersDark ? "dark" : "light";
return preference;
}, [settings?.appearance.theme, prefersDark]);
const theme = useMemo(
() => buildTheme(mode, settings?.appearance.accent || "#6750A4", settings?.appearance.compact ?? false),
[mode, settings?.appearance.accent, settings?.appearance.compact],
);
// Keep the browser chrome in step with the app's surface.
useEffect(() => {
document.documentElement.dataset.theme = mode;
document.body.style.backgroundColor = theme.palette.background.default;
}, [mode, theme]);
return (
<ThemeProvider theme={theme}>
<CssBaseline />
{loading && <LinearProgress />}
{error && (
<Box sx={{ p: 2 }}>
<Alert severity="error">
The backend is not reachable ({error}). Start it with{" "}
<code>docker compose up</code> or check the API token in Settings.
</Alert>
</Box>
)}
<AppShell onToast={setToast} dark={mode === "dark"}>
<Routes>
<Route path="/" element={<TimerPage onToast={setToast} dark={mode === "dark"} />} />
<Route path="/tasks" element={<TasksPage onToast={setToast} />} />
<Route path="/blocker" element={<BlockerPage onToast={setToast} />} />
<Route path="/report" element={<ReportPage dark={mode === "dark"} onToast={setToast} />} />
<Route path="/settings" element={<SettingsPage onToast={setToast} />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</AppShell>
<Snackbar
open={toast !== null}
autoHideDuration={5000}
onClose={() => setToast(null)}
message={toast ?? ""}
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
/>
</ThemeProvider>
);
}
export default function App() {
return (
<SettingsProvider>
<Shell />
</SettingsProvider>
);
}