mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
feat(charts): show 'still computing' caption on long chart loads
This commit is contained in:
1 parent
c0a2c8784e
commit
b2e2f78ac2
8 files changed
+60
No files matched your search
@@ -793,6 +793,7 @@
|
||||
},
|
||||
"charts": {
|
||||
"loadingLabel": "Diagramm wird geladen…",
|
||||
"loadingSlowHint": "Auswertungen werden berechnet — das kann einen Moment dauern.",
|
||||
"weight": "Gewicht",
|
||||
"bloodPressure": "Blutdruck",
|
||||
"pulse": "Puls",
|
||||
|
||||
@@ -793,6 +793,7 @@
|
||||
},
|
||||
"charts": {
|
||||
"loadingLabel": "Loading chart…",
|
||||
"loadingSlowHint": "Computing analytics — this can take a moment.",
|
||||
"weight": "Weight",
|
||||
"bloodPressure": "Blood Pressure",
|
||||
"pulse": "Pulse",
|
||||
|
||||
@@ -793,6 +793,7 @@
|
||||
},
|
||||
"charts": {
|
||||
"loadingLabel": "Cargando la gráfica…",
|
||||
"loadingSlowHint": "Calculando los análisis — puede tardar un momento.",
|
||||
"weight": "Peso",
|
||||
"bloodPressure": "Tensión arterial",
|
||||
"pulse": "Pulso",
|
||||
|
||||
@@ -793,6 +793,7 @@
|
||||
},
|
||||
"charts": {
|
||||
"loadingLabel": "Chargement du graphique…",
|
||||
"loadingSlowHint": "Calcul des analyses — cela peut prendre un instant.",
|
||||
"weight": "Poids",
|
||||
"bloodPressure": "Tension artérielle",
|
||||
"pulse": "Pouls",
|
||||
|
||||
@@ -793,6 +793,7 @@
|
||||
},
|
||||
"charts": {
|
||||
"loadingLabel": "Caricamento del grafico…",
|
||||
"loadingSlowHint": "Calcolo delle analisi — può richiedere un momento.",
|
||||
"weight": "Peso",
|
||||
"bloodPressure": "Pressione arteriosa",
|
||||
"pulse": "Pulsazioni",
|
||||
|
||||
@@ -793,6 +793,7 @@
|
||||
},
|
||||
"charts": {
|
||||
"loadingLabel": "Ładowanie wykresu…",
|
||||
"loadingSlowHint": "Obliczanie analiz — to może chwilę potrwać.",
|
||||
"weight": "Waga",
|
||||
"bloodPressure": "Ciśnienie tętnicze",
|
||||
"pulse": "Tętno",
|
||||
|
||||
@@ -50,4 +50,21 @@ describe("<ChartSkeleton>", () => {
|
||||
const html = render(<ChartSkeleton mini />);
|
||||
expect(html).toMatch(/h-\[140px\]/);
|
||||
});
|
||||
|
||||
// v1.4.43 QoL (L4) — slow-hint caption appears after a 3 s grace.
|
||||
// SSR markup never paints the caption because `useEffect` does not
|
||||
// run server-side; this also keeps hydration mismatch-free.
|
||||
it("does NOT paint the slow-hint caption in the initial SSR markup", () => {
|
||||
const html = render(<ChartSkeleton />);
|
||||
expect(html).not.toContain('data-slot="chart-skeleton-slow-hint"');
|
||||
expect(html).not.toContain("Computing analytics");
|
||||
});
|
||||
|
||||
it("mini variant never sets up the slow-hint timer", () => {
|
||||
// Read the component source: the `useEffect` early-returns when
|
||||
// `mini` is true, so a `<ChartSkeleton mini />` row in trends
|
||||
// never paints the multi-line caption that would overflow it.
|
||||
const html = render(<ChartSkeleton mini />);
|
||||
expect(html).not.toContain('data-slot="chart-skeleton-slow-hint"');
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
@@ -31,10 +33,21 @@ import { useTranslations } from "@/lib/i18n/context";
|
||||
* `rounded-md p-2` and pins the inner band at 140 px so the loading
|
||||
* shell occupies the same visible box as the painted chart.
|
||||
*
|
||||
* v1.4.43 QoL (L4) — when the skeleton is still painted three seconds
|
||||
* in, surface a small caption ("Computing analytics — this can take
|
||||
* a moment") so the user understands the chart is being computed
|
||||
* rather than broken. Today the `/api/analytics` correlations branch
|
||||
* can sit on the skeleton for ~9 s; the caption sets expectation.
|
||||
* Only fires on the full variant — the mini-skeleton sits in a
|
||||
* dashboard row strip where a multi-line caption would overflow.
|
||||
*
|
||||
* `prefers-reduced-motion` is honoured automatically — the `<Skeleton>`
|
||||
* primitive's `animate-pulse` is suppressed via Tailwind's
|
||||
* `motion-reduce:animate-none` modifier.
|
||||
*/
|
||||
|
||||
const SLOW_HINT_DELAY_MS = 3_000;
|
||||
|
||||
export function ChartSkeleton({
|
||||
className,
|
||||
mini = false,
|
||||
@@ -48,6 +61,21 @@ export function ChartSkeleton({
|
||||
mini?: boolean;
|
||||
}) {
|
||||
const { t } = useTranslations();
|
||||
// v1.4.43 QoL (L4) — `showSlowHint` flips true 3 s into the load so
|
||||
// the caption only appears for genuinely slow renders. Using a
|
||||
// `useEffect` keeps the SSR markup identical (caption absent) so
|
||||
// hydration never mismatches; the timer kicks in client-side once
|
||||
// the skeleton actually paints.
|
||||
const [showSlowHint, setShowSlowHint] = useState(false);
|
||||
useEffect(() => {
|
||||
if (mini) return undefined;
|
||||
const handle = setTimeout(
|
||||
() => setShowSlowHint(true),
|
||||
SLOW_HINT_DELAY_MS,
|
||||
);
|
||||
return () => clearTimeout(handle);
|
||||
}, [mini]);
|
||||
|
||||
if (mini) {
|
||||
return (
|
||||
<div
|
||||
@@ -93,6 +121,15 @@ export function ChartSkeleton({
|
||||
{/* Chart band — mirrors the same `h-[var(--chart-height,…)]`
|
||||
contract `health-chart.tsx` paints at line 1087-1089. */}
|
||||
<Skeleton className="h-[var(--chart-height,240px)] w-full md:h-[var(--chart-height-md,280px)]" />
|
||||
|
||||
{showSlowHint && (
|
||||
<p
|
||||
data-slot="chart-skeleton-slow-hint"
|
||||
className="text-muted-foreground mt-3 text-xs"
|
||||
>
|
||||
{t("charts.loadingSlowHint")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user