diff --git a/src/components/admin/__tests__/recovery-readiness-card.test.tsx b/src/components/admin/__tests__/recovery-readiness-card.test.tsx index 3d130ed3b..ac09f58fa 100644 --- a/src/components/admin/__tests__/recovery-readiness-card.test.tsx +++ b/src/components/admin/__tests__/recovery-readiness-card.test.tsx @@ -24,7 +24,10 @@ import type { ReadinessSignal, RecoveryReadiness, } from "@/components/admin/use-recovery-readiness"; -import { READINESS_FIX_PATHS } from "@/lib/recovery/readiness-model"; +import { + READINESS_FIX_PATHS, + READINESS_SIGNALS, +} from "@/lib/recovery/readiness-model"; import { primeLocales } from "@/__tests__/helpers/prime-locales"; primeLocales("de"); @@ -97,6 +100,23 @@ function row(html: string, id: string): string { } describe("RecoveryReadinessCard", () => { + it("holds one row per signal while it loads, so the cards below stay put", () => { + // A read that has not answered: no data, no error. + const client = new QueryClient(); + const html = renderToStaticMarkup( + + + + + , + ); + const start = html.indexOf('data-testid="recovery-readiness-loading"'); + expect(start).toBeGreaterThan(-1); + expect(html.slice(start).match(/
  • { pathname.current = "/admin"; const html = render(READINESS); diff --git a/src/components/admin/recovery-readiness-card.tsx b/src/components/admin/recovery-readiness-card.tsx index d77ae64d1..34189912b 100644 --- a/src/components/admin/recovery-readiness-card.tsx +++ b/src/components/admin/recovery-readiness-card.tsx @@ -24,7 +24,6 @@ import { CheckCircle2, Clock, LifeBuoy, - Loader2, MinusCircle, XCircle, } from "lucide-react"; @@ -34,6 +33,7 @@ import { SettingsCard } from "@/components/settings/settings-card"; import { Badge } from "@/components/ui/badge"; import { InlineAction } from "@/components/ui/inline-action"; import { QueryErrorRow } from "@/components/ui/query-error-row"; +import { Skeleton } from "@/components/ui/skeleton"; import { useFormatters, useTranslations } from "@/lib/i18n/context"; import { cn } from "@/lib/utils"; @@ -44,6 +44,17 @@ import { type RecoveryReadiness, } from "./use-recovery-readiness"; +/** One placeholder row per readiness signal the card always lists. */ +const READINESS_SIGNAL_SLOTS = [ + "database_dump", + "weekly_copy", + "offhost_copies", + "restore_drill", + "offhost_setup", + "dump_retention", + "recovery_kit", +] as const satisfies readonly ReadinessSignal["id"][]; + const STATE_ICON: Record< ReadinessState, React.ComponentType<{ className?: string; "aria-hidden"?: boolean }> @@ -194,14 +205,27 @@ export function RecoveryReadinessCard() { onRetry={() => void refetch()} /> ) : ( -
    -