diff --git a/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx b/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx index 8a6054b5..a9fb94c8 100644 --- a/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx @@ -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 (
- {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 && (
- {scenarioData ? "Updating figures…" : "Loading scenario…"} + Updating figures…
)} @@ -509,7 +517,11 @@ function MetricsBody({ aria-busy={scenarioBusy} > {/* KPI band */} - + {scenarioLoadingFirst ? ( + + ) : ( + + )} {/* Charts + ledger */}
@@ -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 ( +
+ {Array.from({ length: 5 }).map((_, i) => ( +
0 ? "border-l border-gray-100" : ""}`} + > +
+
+
+ ))} +
+ ); +} + function MetricsSkeleton() { return (
- {/* KPI band */} -
- {Array.from({ length: 5 }).map((_, i) => ( -
0 ? "border-l border-gray-100" : ""}`} - > -
-
-
- ))} -
+ {/* Charts + side panel */}