mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
refactor(v1.5): apply simplify-review safe-to-apply suggestions
Drops dead `id` props and `scroll-mt-28` classes from admin section components (load-bearing in v1.4 monolithic admin, dead since the v1.5 phase-4b dynamic-route split). Also removes the unused `LEGACY_ANCHOR_TO_SLUG` map, the `admin-shell` re-export shim, the `BERLIN_TZ` and duplicate `MS_PER_DAY` constants, the verbose `AdminSectionPage` docstring, the `useEffect` paraphrase comment in `ai-section`, and rewrites the `_exhaustive: never` exhaustiveness guard to the modern `slug satisfies never` form. `UserManagementSection` now reads `currentUserId` via `useAuth()` instead of accepting it as a prop. Findings 1, 2, 3, 4, 6, 7, 8, 11, 15, 16, 17 from .planning/phase-6-simplify-findings.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
79864bb8c2
commit
88c8db1abc
22 files changed
+41
-147
No files matched your search
@@ -106,7 +106,7 @@ describe("admin sections — SSR smoke", () => {
|
||||
});
|
||||
|
||||
it("<UserManagementSection> renders with filter pills", () => {
|
||||
const html = render(<UserManagementSection currentUserId="u1" />);
|
||||
const html = render(<UserManagementSection />);
|
||||
expect(html).toContain("User Management");
|
||||
// Filter pills painted from i18n
|
||||
expect(html).toContain("All");
|
||||
|
||||
@@ -33,17 +33,7 @@ import {
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import {
|
||||
ADMIN_SECTION_SLUGS,
|
||||
isAdminSectionSlug,
|
||||
type AdminSectionSlug,
|
||||
} from "./section-slugs";
|
||||
|
||||
// Re-export so callers don't have to chase two modules. New server-side
|
||||
// callers (e.g. `generateStaticParams()`) should still import from
|
||||
// `./section-slugs` directly — values exported through this client
|
||||
// boundary become unusable proxies on the server.
|
||||
export { ADMIN_SECTION_SLUGS, isAdminSectionSlug, type AdminSectionSlug };
|
||||
import { isAdminSectionSlug, type AdminSectionSlug } from "./section-slugs";
|
||||
|
||||
interface AdminSection {
|
||||
slug: AdminSectionSlug;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { formatDate, formatDateTime } from "@/lib/format";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { type ApiTokenInfo } from "./_shared";
|
||||
|
||||
export function ApiTokenOverviewSection({ id }: { id?: string } = {}) {
|
||||
export function ApiTokenOverviewSection() {
|
||||
const { t } = useTranslations();
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
@@ -24,10 +24,7 @@ export function ApiTokenOverviewSection({ id }: { id?: string } = {}) {
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Key className="text-primary h-5 w-5" />
|
||||
|
||||
@@ -29,7 +29,7 @@ function formatBytes(bytes: number, fmt: ReturnType<typeof useFormatters>) {
|
||||
return `${fmt.number(bytes / 1024 / 1024, 2)} MB`;
|
||||
}
|
||||
|
||||
export function BackupsSection({ id }: { id?: string } = {}) {
|
||||
export function BackupsSection() {
|
||||
const { t } = useTranslations();
|
||||
const fmt = useFormatters();
|
||||
const queryClient = useQueryClient();
|
||||
@@ -72,10 +72,7 @@ export function BackupsSection({ id }: { id?: string } = {}) {
|
||||
const rows: BackupRow[] = data?.rows ?? [];
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Database className="text-primary h-5 w-5" />
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Switch } from "@/components/ui/switch";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { PasswordInput, useAdminSettings, useUpdateSettings } from "./_shared";
|
||||
|
||||
export function BugReportSection({ id }: { id?: string } = {}) {
|
||||
export function BugReportSection() {
|
||||
const { t } = useTranslations();
|
||||
const { data: settings } = useAdminSettings();
|
||||
const updateSettings = useUpdateSettings();
|
||||
@@ -45,10 +45,7 @@ export function BugReportSection({ id }: { id?: string } = {}) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex flex-wrap items-start justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Bug className="text-primary h-5 w-5" />
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
|
||||
export function DangerZoneSection({ id }: { id?: string } = {}) {
|
||||
export function DangerZoneSection() {
|
||||
const { t } = useTranslations();
|
||||
const queryClient = useQueryClient();
|
||||
const [wipeMsg, setWipeMsg] = useState<string | null>(null);
|
||||
@@ -65,10 +65,7 @@ export function DangerZoneSection({ id }: { id?: string } = {}) {
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-destructive/5 border-destructive/30 scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-destructive/5 border-destructive/30 rounded-xl border p-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertTriangle className="text-destructive h-5 w-5" />
|
||||
<h2 className="text-destructive text-lg font-semibold">
|
||||
|
||||
@@ -45,7 +45,7 @@ import {
|
||||
useSystemStatus,
|
||||
} from "./_shared";
|
||||
|
||||
export function FeedbackInboxSection({ id }: { id?: string } = {}) {
|
||||
export function FeedbackInboxSection() {
|
||||
const { t } = useTranslations();
|
||||
const queryClient = useQueryClient();
|
||||
const { data: status } = useSystemStatus();
|
||||
@@ -72,10 +72,7 @@ export function FeedbackInboxSection({ id }: { id?: string } = {}) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Inbox className="text-primary h-5 w-5" />
|
||||
<h2 className="text-lg font-semibold">{t("admin.feedback.title")}</h2>
|
||||
|
||||
@@ -4,16 +4,13 @@ import { Settings } from "lucide-react";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { SettingsToggle, useAdminSettings, useUpdateSettings } from "./_shared";
|
||||
|
||||
export function GeneralSettingsSection({ id }: { id?: string } = {}) {
|
||||
export function GeneralSettingsSection() {
|
||||
const { t } = useTranslations();
|
||||
const { data: settings } = useAdminSettings();
|
||||
const updateSettings = useUpdateSettings();
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings className="text-primary h-5 w-5" />
|
||||
<h2 className="text-lg font-semibold">{t("admin.appSettings")}</h2>
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
useUpdateSettings,
|
||||
} from "./_shared";
|
||||
|
||||
export function GlitchtipSection({ id }: { id?: string } = {}) {
|
||||
export function GlitchtipSection() {
|
||||
const { t } = useTranslations();
|
||||
const { data: settings } = useAdminSettings();
|
||||
const updateSettings = useUpdateSettings();
|
||||
@@ -72,10 +72,7 @@ export function GlitchtipSection({ id }: { id?: string } = {}) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex flex-wrap items-start justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertTriangle className="text-primary h-5 w-5" />
|
||||
|
||||
@@ -14,7 +14,7 @@ import { formatDateTime } from "@/lib/format";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { type AdminAuditEntry } from "./_shared";
|
||||
|
||||
export function LoginOverviewSection({ id }: { id?: string } = {}) {
|
||||
export function LoginOverviewSection() {
|
||||
const { t } = useTranslations();
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const [filter, setFilter] = useState<"all" | "failed">("all");
|
||||
@@ -49,10 +49,7 @@ export function LoginOverviewSection({ id }: { id?: string } = {}) {
|
||||
: data?.entries;
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<ScrollText className="text-primary h-5 w-5" />
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
useUpdateSettings,
|
||||
} from "./_shared";
|
||||
|
||||
export function RemindersSection({ id }: { id?: string } = {}) {
|
||||
export function RemindersSection() {
|
||||
const { t } = useTranslations();
|
||||
const { data: settings } = useAdminSettings();
|
||||
const updateSettings = useUpdateSettings();
|
||||
@@ -113,10 +113,7 @@ export function RemindersSection({ id }: { id?: string } = {}) {
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Clock className="text-primary h-5 w-5" />
|
||||
<h2 className="text-lg font-semibold">
|
||||
|
||||
@@ -28,25 +28,3 @@ export type AdminSectionSlug = (typeof ADMIN_SECTION_SLUGS)[number];
|
||||
export function isAdminSectionSlug(value: string): value is AdminSectionSlug {
|
||||
return (ADMIN_SECTION_SLUGS as readonly string[]).includes(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy hash-anchor → new slug mapping. Used by `<StatusCardGrid>` to
|
||||
* rewrite hrefs and by `proxy.ts` to handle any server-side legacy URL
|
||||
* forms (path-style anchors are redirectable; pure `#fragments` never
|
||||
* reach the server, so those are fixed at the call-sites instead).
|
||||
*/
|
||||
export const LEGACY_ANCHOR_TO_SLUG: Record<string, AdminSectionSlug> = {
|
||||
"section-system-status": "system-status",
|
||||
"section-admin-general": "general",
|
||||
"section-admin-services": "services",
|
||||
"section-admin-umami": "integrations",
|
||||
"section-admin-glitchtip": "integrations",
|
||||
"section-admin-webpush": "integrations",
|
||||
"section-admin-bugreport": "integrations",
|
||||
"section-admin-feedback": "feedback",
|
||||
"section-admin-reminders": "reminders",
|
||||
"section-user-management": "users",
|
||||
"section-api-tokens": "api-tokens",
|
||||
"section-login-overview": "login-overview",
|
||||
"section-danger-zone": "danger-zone",
|
||||
};
|
||||
@@ -4,16 +4,13 @@ import { Bell, Globe, Key, MessageCircle } from "lucide-react";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { SettingsToggle, useAdminSettings, useUpdateSettings } from "./_shared";
|
||||
|
||||
export function ServicesSection({ id }: { id?: string } = {}) {
|
||||
export function ServicesSection() {
|
||||
const { t } = useTranslations();
|
||||
const { data: settings } = useAdminSettings();
|
||||
const updateSettings = useUpdateSettings();
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Globe className="text-primary h-5 w-5" />
|
||||
<h2 className="text-lg font-semibold">{t("admin.servicesGlobal")}</h2>
|
||||
|
||||
@@ -19,16 +19,13 @@ import { formatDateTime } from "@/lib/format";
|
||||
import { useFormatters, useTranslations } from "@/lib/i18n/context";
|
||||
import { StatusItem, useSystemStatus } from "./_shared";
|
||||
|
||||
export function SystemStatusSection({ id }: { id?: string } = {}) {
|
||||
export function SystemStatusSection() {
|
||||
const { t } = useTranslations();
|
||||
const fmt = useFormatters();
|
||||
const { data: status, isError } = useSystemStatus();
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Server className="text-primary h-5 w-5" />
|
||||
<h2 className="text-lg font-semibold">{t("admin.systemStatus")}</h2>
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
useUpdateSettings,
|
||||
} from "./_shared";
|
||||
|
||||
export function UmamiSection({ id }: { id?: string } = {}) {
|
||||
export function UmamiSection() {
|
||||
const { t } = useTranslations();
|
||||
const { data: settings } = useAdminSettings();
|
||||
const updateSettings = useUpdateSettings();
|
||||
@@ -75,10 +75,7 @@ export function UmamiSection({ id }: { id?: string } = {}) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex flex-wrap items-start justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Activity className="text-primary h-5 w-5" />
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { formatDate } from "@/lib/format";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { type AdminUser, PasswordInput } from "./_shared";
|
||||
@@ -32,14 +33,10 @@ import { type AdminUser, PasswordInput } from "./_shared";
|
||||
*/
|
||||
type UserFilter = "all" | "admin" | "user";
|
||||
|
||||
export function UserManagementSection({
|
||||
id,
|
||||
currentUserId,
|
||||
}: {
|
||||
id?: string;
|
||||
currentUserId: string;
|
||||
}) {
|
||||
export function UserManagementSection() {
|
||||
const { t } = useTranslations();
|
||||
const { user } = useAuth();
|
||||
const currentUserId = user?.id ?? "";
|
||||
const queryClient = useQueryClient();
|
||||
const [editingUser, setEditingUser] = useState<AdminUser | null>(null);
|
||||
const [editUsername, setEditUsername] = useState("");
|
||||
@@ -157,10 +154,7 @@ export function UserManagementSection({
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Users className="text-primary h-5 w-5" />
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Label } from "@/components/ui/label";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { PasswordInput, useAdminSettings, useUpdateSettings } from "./_shared";
|
||||
|
||||
export function WebPushVapidSection({ id }: { id?: string } = {}) {
|
||||
export function WebPushVapidSection() {
|
||||
const { t } = useTranslations();
|
||||
const { data: settings } = useAdminSettings();
|
||||
const updateSettings = useUpdateSettings();
|
||||
@@ -48,10 +48,7 @@ export function WebPushVapidSection({ id }: { id?: string } = {}) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
className="bg-card border-border scroll-mt-28 rounded-xl border p-6"
|
||||
>
|
||||
<div className="bg-card border-border rounded-xl border p-6">
|
||||
<div className="flex flex-wrap items-start justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<BellRing className="text-primary h-5 w-5" />
|
||||
|
||||
@@ -214,14 +214,7 @@ function InsightsSettingsCard({
|
||||
// 15-minute device-code window expires. Polling stops as soon as
|
||||
// `deviceCode` is cleared (success / cancel / unmount).
|
||||
useEffect(() => {
|
||||
if (!deviceCode) {
|
||||
// No active device code → make sure the spinner is off. setState
|
||||
// inside an effect is intentional here; the alternative (deriving
|
||||
// `devicePolling` from `deviceCode`) would force every error
|
||||
// path to also reset `deviceCode`, which we don't want — the
|
||||
// start handler resets `devicePolling` itself when fetch fails.
|
||||
return;
|
||||
}
|
||||
if (!deviceCode) return;
|
||||
let cancelled = false;
|
||||
const intervalMs = Math.max(deviceCode.intervalSeconds, 3) * 1000;
|
||||
|
||||
|
||||
Reference in new issue
Block a user