diff --git a/app/(dashboard)/e/[sector]/[slug]/loading.tsx b/app/(dashboard)/e/[sector]/[slug]/loading.tsx
index 929bde24..957208c6 100644
--- a/app/(dashboard)/e/[sector]/[slug]/loading.tsx
+++ b/app/(dashboard)/e/[sector]/[slug]/loading.tsx
@@ -1,25 +1,29 @@
-import { headers } from 'next/headers'
+'use client'
+
+import { usePathname } from 'next/navigation'
import { Skeleton } from '@/components/ui/skeleton'
import { Card, CardHeader, CardContent } from '@/components/ui/card'
import { PageHeader } from '@/components/ui/page-header'
import { getExtensionDefinition } from '@/lib/extensions/sectors'
+import InvoiceInboxSkeleton from '@/components/extensions/general/InvoiceInboxSkeleton'
// Mirror of FULLSCREEN_WORKSPACES in ExtensionWorkspaceLoader. loading.tsx
-// can't read route params, so we inspect the forwarded x-pathname header to
-// branch the skeleton shape: the parent dashboard loading.tsx renders a
-// metrics dashboard shape that has nothing to do with extension workspaces.
+// can't read route params, so we branch on the client pathname: the parent
+// dashboard loading.tsx renders a metrics dashboard shape that has nothing
+// to do with extension workspaces. (This must stay a client component: the
+// x-pathname header middleware sets goes on the response, so headers() in a
+// server loading file never sees it and the fullscreen branch went dead.)
const FULLSCREEN_WORKSPACES = new Set(['general/invoice-inbox'])
-export default async function ExtensionWorkspaceLoading() {
- const h = await headers()
- const pathname = h.get('x-pathname') ?? ''
+export default function ExtensionWorkspaceLoading() {
+ const pathname = usePathname()
const match = pathname.match(/^\/e\/([^/]+)\/([^/]+)/)
const sector = match?.[1] ?? ''
const slug = match?.[2] ?? ''
const key = `${sector}/${slug}`
if (FULLSCREEN_WORKSPACES.has(key)) {
- return
+ return
}
const definition = sector && slug ? getExtensionDefinition(sector, slug) : undefined
@@ -101,49 +105,3 @@ function TicSkeleton() {
)
}
-
-function FullScreenWorkspaceSkeleton() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/components/extensions/general/InvoiceInboxSkeleton.tsx b/components/extensions/general/InvoiceInboxSkeleton.tsx
new file mode 100644
index 00000000..3ce9a5a3
--- /dev/null
+++ b/components/extensions/general/InvoiceInboxSkeleton.tsx
@@ -0,0 +1,48 @@
+import { Skeleton } from '@/components/ui/skeleton'
+
+// Single source of truth for the Dokumentinkorg loading silhouette, rendered
+// by both the route-level loading.tsx and the workspace's own client-fetch
+// state so the route fallback, the fetch shell, and the loaded UI are one
+// shape with no reflow. Mirrors the live layout: full-bleed top bar +
+// 3-pane grid, list pane with search + filter-dropdown trigger (PR #1524
+// replaced the old filter-pill row with a single full-width dropdown).
+export default function InvoiceInboxSkeleton() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/components/extensions/general/InvoiceInboxWorkspace.tsx b/components/extensions/general/InvoiceInboxWorkspace.tsx
index 5e084901..9feed7d8 100644
--- a/components/extensions/general/InvoiceInboxWorkspace.tsx
+++ b/components/extensions/general/InvoiceInboxWorkspace.tsx
@@ -52,6 +52,7 @@ import { cn, formatCurrency, formatDate, formatDateLong } from '@/lib/utils'
import { QUIET_LINK_CLASS } from '@/components/ui/dry-table'
import { GoogleMark, MicrosoftMark } from '@/components/ui/provider-marks'
import EditKonteringDialog from '@/components/extensions/general/EditKonteringDialog'
+import InvoiceInboxSkeleton from '@/components/extensions/general/InvoiceInboxSkeleton'
import { WhatsAppMark } from '@/components/extensions/general/WhatsAppMark'
import { useReceiptHunt } from '@/components/extensions/general/use-receipt-hunt'
import { createClient } from '@/lib/supabase/client'
@@ -302,55 +303,10 @@ function deriveInboxStatus(item: InboxItem): InboxStatus {
}
// ── Skeleton ─────────────────────────────────────────────────
-// Mirrors the live layout (top bar + 3-pane card) so the transition from
-// the route-level loading.tsx to data-loaded content has no visible reflow.
-// Keep in sync with app/(dashboard)/e/[sector]/[slug]/loading.tsx.
+// Shared with app/(dashboard)/e/[sector]/[slug]/loading.tsx so the route
+// fallback and this client-fetch shell are one silhouette with no reflow.
-function WorkspaceSkeleton() {
- return (
-