Files
Homelab-manager/web/src/App.tsx
T
bobbanandClaude Sonnet 5 f92f8de96e Add a Date & Time setting and fix inconsistent date formats app-wide
Different tables used different date formats: Servers & Tasks used a
fixed "YYYY-MM-DD HH:mm:ss" (24h), while Audit Log, DNS, Integrations
(Tailscale/Semaphore), and Users called plain .toLocaleString() with
no options, which renders using the browser's own locale — different
per browser/OS, and inconsistent with the other pages' fixed format.

- New Settings -> Date & Time page: pick date order (YYYY-MM-DD,
  DD/MM/YYYY, MM/DD/YYYY) and 12h vs 24h clock, with a live preview.
- utils/date.ts's formatDateTime() now reads these settings instead of
  being hardcoded to sv-SE/24h; the setting is fetched once at app
  startup (alongside /api/me) via a new non-secret GET
  /api/settings/display (any signed-in user, same rationale as the
  badge-color endpoints) and applied immediately on save too, without
  needing a page reload.
- Switched every remaining raw new Date(...).toLocaleString() call
  (Audit Log, DNS zone sync time, Tailscale/Semaphore last-seen,
  Users' last login) over to the shared formatter, so every table now
  renders dates identically.

Verified the formatter's date-order x 12h logic against all six
combinations plus the midnight/noon 12h edge cases, and the settings
endpoints end-to-end (defaults, partial updates, validation
rejection, audit logging) against the real dev server.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 22:17:06 +02:00

109 lines
3.4 KiB
TypeScript

import { useEffect, useState, type ReactNode } from "react";
import { Routes, Route, Navigate } from "react-router-dom";
import { api, type CurrentUser, type UserRole, UnauthorizedError } from "./api/client";
import Login from "./pages/Login";
import Dashboard from "./pages/Dashboard";
import Users from "./pages/Users";
import AuditLog from "./pages/AuditLog";
import Secrets from "./pages/Secrets";
import Ipam from "./pages/Ipam";
import Dns from "./pages/Dns";
import ServersTasks from "./pages/ServersTasks";
import ServerDetail from "./pages/ServerDetail";
import Integrations from "./pages/Integrations";
import Settings from "./pages/Settings";
import NotificationSettings from "./pages/settings/NotificationSettings";
import BadgeSettings from "./pages/settings/BadgeSettings";
import DateTimeSettings from "./pages/settings/DateTimeSettings";
import CacheSettings from "./pages/settings/CacheSettings";
import AppShell from "./layout/AppShell";
import { setDateTimeSettings } from "./utils/date";
const roleRank: Record<UserRole, number> = { viewer: 0, operator: 1, admin: 2 };
function RequireRole({
user,
minRole,
children,
}: {
user: CurrentUser;
minRole: UserRole;
children: ReactNode;
}) {
if (roleRank[user.role] < roleRank[minRole]) {
return (
<div className="alert alert-danger">You don't have permission to view this page.</div>
);
}
return <>{children}</>;
}
export default function App() {
const [user, setUser] = useState<CurrentUser | null | undefined>(undefined);
useEffect(() => {
api.me().then(
(res) => setUser(res.user),
(err) => {
if (!(err instanceof UnauthorizedError)) {
console.error(err);
}
setUser(null);
},
);
api.settings.display().then((res) => setDateTimeSettings(res.display)).catch(() => {});
}, []);
if (user === undefined) {
return <div className="loading-screen">Loading…</div>;
}
if (user === null) {
return <Login />;
}
return (
<AppShell user={user}>
<Routes>
<Route path="/" element={<Dashboard user={user} />} />
<Route path="/servers" element={<ServersTasks user={user} />} />
<Route path="/servers/:id" element={<ServerDetail user={user} />} />
<Route path="/dns" element={<Dns user={user} />} />
<Route path="/ipam" element={<Ipam user={user} />} />
<Route path="/secrets" element={<Secrets user={user} />} />
<Route path="/integrations" element={<Integrations user={user} />} />
<Route
path="/users"
element={
<RequireRole user={user} minRole="admin">
<Users />
</RequireRole>
}
/>
<Route
path="/audit-log"
element={
<RequireRole user={user} minRole="operator">
<AuditLog />
</RequireRole>
}
/>
<Route
path="/settings"
element={
<RequireRole user={user} minRole="admin">
<Settings />
</RequireRole>
}
>
<Route index element={<Navigate to="notifications" replace />} />
<Route path="notifications" element={<NotificationSettings />} />
<Route path="badges" element={<BadgeSettings />} />
<Route path="date-time" element={<DateTimeSettings />} />
<Route path="cache" element={<CacheSettings />} />
</Route>
</Routes>
</AppShell>
);
}