fix(dashboard): show skeleton instead of default layout until hydration completes
Root cause: useDashboardLayout initialised React state with createDefaultDashboardLayout() (1 widget), so the wrong default rendered during the ~100–500ms window while React Query fetched the user session and DB layout after login. On reload within staleTime the cache hit resolved instantly, masking the bug. Fix: add isHydrated boolean state that becomes true only once localStorage OR DB hydration has settled; DashboardClient renders a GridLayoutSkeleton until then. Also adds router.refresh() in the sign-in handler to bust the Next.js Router Cache so the post-login navigation always lands on a fresh server component tree. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import type { DashboardWidgetConfig, DashboardWidgetType } from "@capakraken/shared/types";
|
||||
import { noCompactor } from "react-grid-layout";
|
||||
import dynamic from "next/dynamic";
|
||||
import { Suspense, useState, useRef, useEffect, useMemo } from "react";
|
||||
import { useDashboardLayout } from "~/hooks/useDashboardLayout.js";
|
||||
@@ -145,7 +146,7 @@ function DeferredWidgetBody({
|
||||
|
||||
export function DashboardClient() {
|
||||
const [addModalOpen, setAddModalOpen] = useState(false);
|
||||
const { config, addWidget, removeWidget, updateWidgetConfig, onLayoutChange, resetLayout } =
|
||||
const { config, isHydrated, addWidget, removeWidget, updateWidgetConfig, onLayoutChange, resetLayout } =
|
||||
useDashboardLayout();
|
||||
|
||||
// Measure grid container width so Responsive knows the column size.
|
||||
@@ -226,6 +227,26 @@ export function DashboardClient() {
|
||||
[config.widgets, removeWidget, updateWidgetConfig],
|
||||
);
|
||||
|
||||
// Show a skeleton while hydration is in-flight (avoids flashing the 1-widget default
|
||||
// layout before the user's real layout is loaded from localStorage or the DB).
|
||||
if (!isHydrated) {
|
||||
return (
|
||||
<div className="app-page space-y-6">
|
||||
<div className="app-page-header gap-4">
|
||||
<div>
|
||||
<h1 className="app-page-title">Dashboard</h1>
|
||||
<p className="app-page-subtitle mt-1">
|
||||
Drag widgets to rearrange them and resize from the corners.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="app-surface overflow-hidden p-3">
|
||||
<GridLayoutSkeleton />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="app-page space-y-6">
|
||||
<div className="app-page-header gap-4">
|
||||
@@ -291,8 +312,7 @@ export function DashboardClient() {
|
||||
breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
|
||||
cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
|
||||
rowHeight={80}
|
||||
compactType={null}
|
||||
preventCollision={false}
|
||||
compactor={noCompactor}
|
||||
onLayoutChange={(
|
||||
_: unknown,
|
||||
allLayouts: Record<
|
||||
|
||||
Reference in New Issue
Block a user