mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
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.
137 lines
4.6 KiB
TypeScript
137 lines
4.6 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* `<VersionTileSection>` — admin-overview version card.
|
|
*
|
|
* v1.15.12 H1 — a dedicated tile that surfaces the running build's
|
|
* version, short build SHA, and build timestamp, plus a best-effort
|
|
* "update available" hint computed against the latest GitHub release
|
|
* tag. The release comparison reuses the existing
|
|
* `GET /api/version/check-updates` proxy (server-side `safeFetch` past
|
|
* the CSP), so no new route or OpenAPI schema is introduced. The check
|
|
* is day-stale (`staleTime`) and never blocks the render: on any
|
|
* failure the tile simply omits the update hint.
|
|
*
|
|
* Cookie-admin only by construction — it lives under the admin layout
|
|
* (`/admin`), which gates on `requireAdmin()`.
|
|
*/
|
|
|
|
import {
|
|
ArrowUpCircle,
|
|
CheckCircle2,
|
|
Clock,
|
|
GitCommit,
|
|
Tag,
|
|
} from "lucide-react";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
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 { queryKeys } from "@/lib/query-keys";
|
|
import { ApiError, apiGet } from "@/lib/api/api-fetch";
|
|
import { StatusItem, usePublicVersion } from "./_shared";
|
|
|
|
type CheckUpdatesResult =
|
|
| {
|
|
status: "up_to_date";
|
|
current: string;
|
|
latest_tag: string;
|
|
checked_at: string;
|
|
}
|
|
| {
|
|
status: "newer_available";
|
|
current: string;
|
|
latest_tag: string;
|
|
html_url: string | null;
|
|
published_at: string | null;
|
|
checked_at: string;
|
|
}
|
|
| { status: "unknown"; current: string; reason: string };
|
|
|
|
async function fetchUpdateCheck(): Promise<CheckUpdatesResult> {
|
|
try {
|
|
return await apiGet<CheckUpdatesResult>("/api/version/check-updates");
|
|
} catch (err) {
|
|
const reason =
|
|
err instanceof ApiError ? `http_${err.status}` : "network_error";
|
|
return { status: "unknown", current: "", reason };
|
|
}
|
|
}
|
|
|
|
function shortSha(sha: string | null): string | null {
|
|
if (!sha) return null;
|
|
return sha.length > 7 ? sha.slice(0, 7) : sha;
|
|
}
|
|
|
|
export function VersionTileSection() {
|
|
const { t } = useTranslations();
|
|
const { data: version } = usePublicVersion();
|
|
|
|
// Best-effort, day-stale, never throws into the render. The route is
|
|
// auth-gated and proxies the GitHub Releases API through the server so
|
|
// the production CSP doesn't block the call.
|
|
const { data: update } = useQuery({
|
|
queryKey: queryKeys.versionUpdateCheck(),
|
|
queryFn: fetchUpdateCheck,
|
|
staleTime: 24 * 60 * 60_000,
|
|
retry: false,
|
|
});
|
|
|
|
const newer = update?.status === "newer_available";
|
|
const upToDate = update?.status === "up_to_date";
|
|
const sha = shortSha(version?.buildSha ?? null);
|
|
|
|
return (
|
|
<SettingsCard as="section" aria-labelledby="admin-overview-version-heading">
|
|
<SettingsCardHeader
|
|
icon={Tag}
|
|
titleId="admin-overview-version-heading"
|
|
title={t("admin.overview.versionTileTitle")}
|
|
status={
|
|
newer ? (
|
|
<a
|
|
href={
|
|
(update.status === "newer_available" && update.html_url) ||
|
|
"https://github.com/MBombeck/HealthLog/releases/latest"
|
|
}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="bg-warning/15 text-warning border-warning/30 inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium transition-opacity hover:opacity-80"
|
|
>
|
|
<ArrowUpCircle className="h-3.5 w-3.5" aria-hidden="true" />
|
|
{t("admin.overview.versionTileUpdateAvailable", {
|
|
tag:
|
|
update.status === "newer_available" ? update.latest_tag : "",
|
|
})}
|
|
</a>
|
|
) : upToDate ? (
|
|
<span className="text-success inline-flex items-center gap-1.5 text-xs font-medium">
|
|
<CheckCircle2 className="h-3.5 w-3.5" aria-hidden="true" />
|
|
{t("admin.overview.versionTileUpToDate")}
|
|
</span>
|
|
) : null
|
|
}
|
|
/>
|
|
|
|
<div className="mt-4 grid gap-3 pl-7 sm:grid-cols-2 lg:grid-cols-3">
|
|
<StatusItem
|
|
icon={Tag}
|
|
label={t("admin.overview.versionTileVersion")}
|
|
value={version?.version ?? "—"}
|
|
/>
|
|
<StatusItem
|
|
icon={GitCommit}
|
|
label={t("admin.overview.versionTileBuildSha")}
|
|
value={sha ?? "—"}
|
|
/>
|
|
<StatusItem
|
|
icon={Clock}
|
|
label={t("admin.overview.versionTileBuiltAt")}
|
|
value={version?.builtAt ? formatDateTime(version.builtAt) : "—"}
|
|
/>
|
|
</div>
|
|
</SettingsCard>
|
|
);
|
|
}
|