Files
accounted/app/(dashboard)/loading.tsx
T
Jakob WennbergandClaude Fable 5 bb551d1d59 polish(ui): founder feedback batch - inset sidebar hairline, calmer dialogs, Discord mark, chat skeleton (#1158)
- Sidebar: the hairline above the user block is inset to the content
  edges instead of running edge-to-edge (concept language).
- User menu: the Discord community link renders the actual Discord mark
  (inlined simple-icons path, CC0) instead of lucide MessagesSquare.
- Dialogs: open/close animation toned down, zoom 98 instead of 95 and
  150ms instead of 200ms.
- Settings modal: switching tabs no longer remounts the intercepted
  route (and replayed the whole open animation on every click). The rail
  now swaps sections via history.replaceState, which Next syncs into
  usePathname(), so the dialog stays mounted and tab switches are
  instant. Verified via Playwright: dialog DOM node survives three tab
  switches, URL tracks the section, Esc still closes back.
- /chat loading: the shared dashboard skeleton stretched edge-to-edge in
  chat's full-bleed wrapper (chat's own layout is what suspends, so a
  chat/loading.tsx cannot catch it). The shared fallback is now route-
  aware and renders a two-pane chat silhouette for /chat.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 19:36:45 +02:00

85 lines
3.2 KiB
TypeScript

'use client'
import { usePathname } from 'next/navigation'
import { Skeleton } from '@/components/ui/skeleton'
/**
* Shared loading fallback for the dashboard segment. It is the Suspense
* fallback for Hem (app/(dashboard)/page.tsx) and for every child route that
* has no loading.tsx of its own, so it deliberately mirrors Hem's silhouette:
* a greeting hero (H1 + date line) followed by a single "Att göra"-style pane
* of list rows (see DashboardContent / AttGoraSection).
*
* A greeting + hairline-separated rows reads honestly on Hem and stays neutral
* on the plain list pages that fall back here (a page title + rows), which is
* why it is not shaped like any one page's specific grid.
*
* /chat is the exception: MainContainer renders it full-bleed (no max-width,
* no padding), so the column silhouette would stretch edge-to-edge and read
* broken. The chat branch mirrors the two-pane chat shell instead:
* conversation sidebar + empty conversation pane (see ChatLayout/ChatSidebar).
*/
export default function DashboardLoading() {
const pathname = usePathname()
if (pathname.startsWith('/chat')) {
return (
<div className="flex h-full">
<aside className="flex w-full flex-col border-r border-border bg-card/40 md:w-80 shrink-0">
<div className="space-y-3 border-b border-border px-5 py-4">
<div className="flex items-center gap-2">
<Skeleton className="h-8 w-8 shrink-0 rounded-full" />
<div className="flex-1 space-y-1.5">
<Skeleton className="h-4 w-28" />
<Skeleton className="h-2.5 w-20" />
</div>
</div>
<Skeleton className="h-8 w-full rounded-md" />
</div>
<div className="space-y-1 p-3">
{['w-40', 'w-48', 'w-36', 'w-44'].map((w, i) => (
<div key={i} className="space-y-1.5 px-2 py-2.5">
<Skeleton className={`h-3.5 ${w}`} />
<Skeleton className="h-2.5 w-24" />
</div>
))}
</div>
</aside>
<div className="hidden min-w-0 flex-1 bg-background md:block" />
</div>
)
}
return (
<div className="space-y-8">
{/* Greeting hero (title + date line) */}
<section>
<Skeleton className="h-7 w-52" />
<Skeleton className="mt-2 h-3.5 w-64" />
</section>
{/* Single pane: header over a hairline, then list rows */}
<section>
<div className="flex items-baseline justify-between border-b border-border px-1 pb-2.5">
<Skeleton className="h-4 w-24" />
<Skeleton className="h-3 w-16" />
</div>
<Skeleton className="ml-1 mt-5 mb-1 h-2.5 w-20" />
<div>
{['w-44', 'w-52', 'w-40', 'w-48', 'w-44'].map((w, i) => (
<div
key={i}
className="flex items-start gap-3 border-b border-border px-1 py-3.5"
>
<Skeleton className="mt-px h-[15px] w-[15px] shrink-0 rounded" />
<Skeleton className={`h-3.5 ${w}`} />
<Skeleton className="ml-auto h-5 w-7 shrink-0 rounded-full" />
</div>
))}
</div>
</section>
</div>
)
}