import { eq } from 'drizzle-orm'; import { db } from '@/lib/db'; import { ports as portsTable } from '@/lib/db/schema/ports'; import { isExpensesModuleEnabled } from '@/lib/services/expenses-module.service'; import { ModuleDisabledPage } from '@/components/shared/module-disabled-page'; interface ExpensesLayoutProps { children: React.ReactNode; params: Promise<{ portSlug: string }>; } /** * Layout-level gate for the entire /expenses subtree (list, scan, * detail). When the port has expenses_module_enabled = false, every * route under /expenses renders the ModuleDisabledPage instead of the * real content. This is the route-level half of the "hybrid hide+block" * model (the sidebar entries are independently hidden via * expensesModuleByPort on the SSR-resolved sidebar prop). * * Using a layout rather than per-page guards means: (a) one place to * change the gate logic, (b) nested routes (scan, [id]) are covered * automatically, (c) the children subtree never mounts when disabled, * so its data-fetching effects don't fire. */ export default async function ExpensesLayout({ children, params }: ExpensesLayoutProps) { const { portSlug } = await params; const port = await db.query.ports.findFirst({ where: eq(portsTable.slug, portSlug), columns: { id: true }, }); if (!port) return children; const enabled = await isExpensesModuleEnabled(port.id); if (enabled) return children; return ( ); }