mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-07-27 22:45:03 +00:00
feat(reporting): skeleton the KPI band on first scenario entry
Switching from Current stock into a scenario has no previous overlay to keep, so the scenario KPIs were momentarily empty — an empty-bar flash before the figures loaded. That's the "no data yet" case, so it now shows the same KpiBandSkeleton the initial page load uses (extracted + shared), and drops the now-redundant "Loading scenario…" line there. The dim + "Updating figures…" path is unchanged and stays for the cases where data is already on screen (scenario→scenario switch, filter changes), where the previous figures are deliberately kept via keepPreviousData. Net: skeleton = no data yet; dim = refreshing existing data — one consistent convention. Verified in a headless browser on #796: first entry shows the KPI skeleton + aria-busy and no empty flash; a filter toggle still dims the figures with "Updating figures…". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a23719aa34
commit
43667ec1fd
1 changed files with 40 additions and 15 deletions
|
|
@ -485,17 +485,25 @@ function MetricsBody({
|
|||
: null;
|
||||
|
||||
const scenarioBusy = isScenario && isFetching;
|
||||
// First entry into a scenario from Current stock: there's no previous overlay
|
||||
// to keep, so the scenario KPIs are momentarily empty. Skeleton that band
|
||||
// (the "no data yet" case → same pattern as first page load) rather than
|
||||
// flashing an empty bar. A scenario→scenario switch keeps the old figures, so
|
||||
// it stays on the dim+spinner path below instead.
|
||||
const scenarioLoadingFirst = scenarioBusy && !scenarioData;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{scenarioBusy && (
|
||||
{/* Refetch-with-data only: the figures stay on screen and dim, so a label
|
||||
clarifies the dim means "updating". First load speaks via its skeleton. */}
|
||||
{scenarioBusy && scenarioData && (
|
||||
<div
|
||||
className="flex items-center gap-1.5 text-[0.8rem] text-gray-600"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
<ArrowPathIcon className="h-3.5 w-3.5 animate-spin text-gray-400" />
|
||||
{scenarioData ? "Updating figures…" : "Loading scenario…"}
|
||||
Updating figures…
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -509,7 +517,11 @@ function MetricsBody({
|
|||
aria-busy={scenarioBusy}
|
||||
>
|
||||
{/* KPI band */}
|
||||
<KpiBand kpis={isScenario ? scenarioKpis : currentStockKpis} />
|
||||
{scenarioLoadingFirst ? (
|
||||
<KpiBandSkeleton />
|
||||
) : (
|
||||
<KpiBand kpis={isScenario ? scenarioKpis : currentStockKpis} />
|
||||
)}
|
||||
|
||||
{/* Charts + ledger */}
|
||||
<div className="grid grid-cols-1 items-stretch gap-4 lg:grid-cols-[1.55fr_1fr]">
|
||||
|
|
@ -610,21 +622,34 @@ function ScenarioRowSkeleton() {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The five-cell KPI band in its loading shape. Shared by the initial page-load
|
||||
* skeleton and the first Current-stock → scenario switch, so both "no data yet"
|
||||
* moments read identically.
|
||||
*/
|
||||
function KpiBandSkeleton() {
|
||||
return (
|
||||
<div
|
||||
className="grid grid-cols-2 rounded-lg border border-gray-200 bg-white sm:grid-cols-3 lg:grid-cols-5"
|
||||
aria-hidden
|
||||
>
|
||||
{Array.from({ length: 5 }).map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`space-y-2 px-5 py-4 ${i > 0 ? "border-l border-gray-100" : ""}`}
|
||||
>
|
||||
<div className="h-2.5 w-16 animate-pulse rounded bg-gray-100" />
|
||||
<div className="h-6 w-20 animate-pulse rounded bg-gray-100" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MetricsSkeleton() {
|
||||
return (
|
||||
<div className="space-y-4" aria-hidden>
|
||||
{/* KPI band */}
|
||||
<div className="grid grid-cols-2 rounded-lg border border-gray-200 bg-white sm:grid-cols-3 lg:grid-cols-5">
|
||||
{Array.from({ length: 5 }).map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`space-y-2 px-5 py-4 ${i > 0 ? "border-l border-gray-100" : ""}`}
|
||||
>
|
||||
<div className="h-2.5 w-16 animate-pulse rounded bg-gray-100" />
|
||||
<div className="h-6 w-20 animate-pulse rounded bg-gray-100" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<KpiBandSkeleton />
|
||||
{/* Charts + side panel */}
|
||||
<div className="grid grid-cols-1 items-stretch gap-4 lg:grid-cols-[1.55fr_1fr]">
|
||||
<div className="h-64 animate-pulse rounded-lg border border-gray-200 bg-white" />
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue