Files
HealthLog/src/components/admin/system-status-summary.tsx
T
Marc-André Bombeck a4b204ffe4 refactor(ui): retire the raw dracula utilities for semantic tokens
Migrate every remaining *-dracula-* utility site (127 lint warnings,
193 class occurrences) onto the semantic vocabulary. The swaps are
pixel-identical in both themes — each target token aliases the same
primitive the utility read, including the light-theme AA overrides:

- green/orange/cyan/red → success/warning/info/destructive
- purple → primary (same value in both themes)
- pink → the new --brand-pink token (Coach gradient, Apple-Health
  badge, recording mic): dark #ff79c6 — 6.86:1 card / 5.97:1 bg;
  light #a3134d — 7.32:1 card / 6.83:1 bg (computed)
- yellow sites resolve to their status meaning: warn-level log icon,
  update-available pill and offline-geo hint → warning; the admin
  helpful-rate ramp → success/warning/destructive; the confidence
  meter mirrors the coverage-meter precedent (medium + low share the
  caution tint, the lit count carries the distinction)
- the confidence-badge low tier dropped dead *-dracula-comment/*
  classes (no generated utility) for border-border + bg-muted/50

Close two rule blind spots the sweep surfaced — directional borders
(border-l-dracula-red) and the parenthesised CSS-var shorthand
(bg-(--dracula-green)) — and flip healthlog/no-dracula-utility from
warn to error so the class cannot regrow. var(--dracula-*) references
in chart code stay: they are token references the light theme
overrides, and the chart palette is pinned visually.
2026-07-05 21:28:09 +02:00

141 lines
4.6 KiB
TypeScript

"use client";
/**
* `<SystemStatusSummary>` — compact at-a-glance system snapshot for the
* `/admin` overview landing page.
*
* The full breakdown lives in `<SystemStatusSection>` at
* `/admin/system-status`; this component only surfaces the handful of
* facts an admin wants on the overview screen: app version, database
* up/down, worker running, image build SHA + timestamp, and the
* server-process start time. Same `useSystemStatus()` data source so we
* don't re-fetch.
*/
import {
Clock,
Cog,
Database,
GitCommit,
Globe,
Loader2,
Server,
Tag,
} from "lucide-react";
import { formatDateTime } from "@/lib/format";
import { SettingsCard } from "@/components/settings/settings-card";
import { SettingsCardHeader } from "@/components/settings/_card-header";
import { useTranslations } from "@/lib/i18n/context";
import { StatusItem, usePublicVersion, useSystemStatus } from "./_shared";
export function SystemStatusSummary() {
const { t } = useTranslations();
const { data: status, isError } = useSystemStatus();
const { data: version } = usePublicVersion();
return (
<SettingsCard
as="section"
aria-labelledby="admin-overview-snapshot-heading"
>
<SettingsCardHeader
icon={Server}
titleId="admin-overview-snapshot-heading"
title={t("admin.overview.snapshotTitle")}
/>
{status ? (
<div className="mt-4 grid gap-3 pl-7 sm:grid-cols-2 lg:grid-cols-3">
<StatusItem
icon={Tag}
label={t("admin.overview.snapshotVersion")}
value={version?.version ?? status.version}
/>
<StatusItem
icon={Database}
label={t("admin.overview.snapshotDatabase")}
value={
status.database === "connected"
? t("admin.databaseConnected")
: t("admin.databaseError")
}
className={
status.database === "connected"
? "text-success"
: "text-destructive"
}
/>
<StatusItem
icon={Cog}
label={t("admin.overview.snapshotWorker")}
value={
status.worker.running
? t("admin.workerRunning")
: t("admin.workerStopped")
}
className={
status.worker.running ? "text-success" : "text-destructive"
}
/>
<StatusItem
icon={Clock}
label={t("admin.overview.snapshotStarted")}
value={formatDateTime(status.startTime)}
/>
{(version?.buildSha ?? status.gitCommit) && (
<StatusItem
icon={GitCommit}
label={t("admin.overview.snapshotBuildSha")}
value={version?.buildSha ?? status.gitCommit}
/>
)}
{version?.builtAt && (
<StatusItem
icon={Clock}
label={t("admin.overview.snapshotBuiltAt")}
value={formatDateTime(version.builtAt)}
/>
)}
{/* v1.4.27 R5 — surface the offline-geo state so the maintainer
spots the missing MAXMIND_LICENSE_KEY without crawling logs.
The field is undefined on legacy responses; the row only
renders when /api/version answers the new shape. */}
{version?.offlineGeoEnabled !== undefined && (
<StatusItem
icon={Globe}
label={t("admin.overview.snapshotOfflineGeo")}
value={
version.offlineGeoEnabled
? t("admin.overview.snapshotOfflineGeoOn")
: t("admin.overview.snapshotOfflineGeoOff", {
host: version.geoProviderHost ?? "ipwho.is",
})
}
className={
version.offlineGeoEnabled ? "text-success" : "text-warning"
}
/>
)}
</div>
) : isError ? (
<div
role="alert"
className="text-destructive bg-destructive/10 border-destructive/30 mt-4 rounded-md border px-3 py-2 text-sm"
>
{t("admin.overview.snapshotLoadError")}
</div>
) : (
<div className="mt-4 flex items-center gap-2">
<Loader2
className="text-muted-foreground h-4 w-4 animate-spin motion-reduce:animate-none"
aria-hidden="true"
/>
<span className="text-muted-foreground text-sm">
{t("admin.overview.snapshotLoading")}
</span>
</div>
)}
</SettingsCard>
);
}