From 74ed45ddfcff2c09e58792ce2b01a828ceda50ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Sat, 11 Apr 2026 23:22:18 +0200 Subject: [PATCH] fix(web): add missing loading and error states to MfaPromptBanner, Step1Identity, MobileSummaryClient - MfaPromptBanner: silently hide on query error (non-critical advisory banner) - Step1Identity: show skeleton placeholders while blueprint list loads - MobileSummaryClient: add error state with retry button for dashboard queries Co-Authored-By: Claude Opus 4.6 --- .../components/mobile/MobileSummaryClient.tsx | 41 +++++++++++++++---- .../projects/project-wizard/Step1Identity.tsx | 10 ++++- .../components/security/MfaPromptBanner.tsx | 10 ++--- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/apps/web/src/components/mobile/MobileSummaryClient.tsx b/apps/web/src/components/mobile/MobileSummaryClient.tsx index 475cc85..6ac0381 100644 --- a/apps/web/src/components/mobile/MobileSummaryClient.tsx +++ b/apps/web/src/components/mobile/MobileSummaryClient.tsx @@ -8,7 +8,12 @@ import { MobileProjectCard } from "./MobileProjectCard.js"; import { EmptyState } from "~/components/ui/EmptyState.js"; export function MobileSummaryClient() { - const { data: overview, isLoading: overviewLoading } = trpc.dashboard.getOverview.useQuery(undefined, { + const { + data: overview, + isLoading: overviewLoading, + isError: overviewError, + refetch: refetchOverview, + } = trpc.dashboard.getOverview.useQuery(undefined, { staleTime: 60_000, }); @@ -16,18 +21,23 @@ export function MobileSummaryClient() { const { data: projectsData, isLoading: projectsLoading } = (trpc.project.list.useQuery as any)( { limit: 5, status: "ACTIVE" }, { staleTime: 60_000 }, - ) as { data: { projects: Array<{ id: string; shortCode: string; name: string; status: string }> } | undefined; isLoading: boolean }; + ) as { + data: + | { projects: Array<{ id: string; shortCode: string; name: string; status: string }> } + | undefined; + isLoading: boolean; + }; // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { data: demandData } = (trpc.dashboard.getDemand.useQuery as any)( - undefined, - { staleTime: 60_000 }, - ) as { data: { openDemandCount?: number; openDemands?: unknown[] } | undefined }; + const { data: demandData } = (trpc.dashboard.getDemand.useQuery as any)(undefined, { + staleTime: 60_000, + }) as { data: { openDemandCount?: number; openDemands?: unknown[] } | undefined }; const projects = projectsData?.projects ?? []; const openDemandCount = demandData?.openDemandCount ?? demandData?.openDemands?.length ?? 0; const isLoading = overviewLoading || projectsLoading; + const isError = overviewError; return (
@@ -40,7 +50,20 @@ export function MobileSummaryClient() {
- {isLoading ? ( + {isError ? ( +
+

+ Failed to load dashboard data +

+ +
+ ) : isLoading ? (
{Array.from({ length: 3 }).map((_, i) => (
@@ -64,7 +87,9 @@ export function MobileSummaryClient() { className="flex items-center gap-3 rounded-xl border border-amber-300 dark:border-amber-700 bg-amber-50 dark:bg-amber-950/30 px-4 py-3" >
- {openDemandCount} + + {openDemandCount} +
diff --git a/apps/web/src/components/projects/project-wizard/Step1Identity.tsx b/apps/web/src/components/projects/project-wizard/Step1Identity.tsx index f10d035..e872454 100644 --- a/apps/web/src/components/projects/project-wizard/Step1Identity.tsx +++ b/apps/web/src/components/projects/project-wizard/Step1Identity.tsx @@ -12,10 +12,11 @@ interface Step1Props { } export function Step1Identity({ state, onChange }: Step1Props) { - const { data: blueprints } = trpc.blueprint.list.useQuery( + const { data: blueprints, isLoading: blueprintsLoading } = trpc.blueprint.list.useQuery( { target: BlueprintTarget.PROJECT, isActive: true }, { staleTime: 30_000 }, ) as { + isLoading: boolean; data: | Array<{ id: string; @@ -88,6 +89,13 @@ export function Step1Identity({ state, onChange }: Step1Props) {
No Blueprint
Start blank
+ {blueprintsLoading && + Array.from({ length: 3 }).map((_, i) => ( +
+ ))} {(blueprints ?? []).map((bp) => (