#!/usr/bin/env node /** * Generates CRM app routes (thin page.tsx re-exports) and stub feature files if missing. */ import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT = path.join(__dirname, ".."); const APP = path.join(ROOT, "app", "crm"); const FEATURES = path.join(ROOT, "modules", "crm", "features"); const ROUTES = [ ["hub", "pages/hub", "CrmHub", "pages/hub"], ["sales/dashboard/overview", "sales/dashboards", "OverviewDashboard", "sales/dashboards"], ["sales/dashboard/sales", "sales/dashboards", "SalesDashboard", "sales/dashboards"], ["sales/dashboard/pipeline", "sales/dashboards", "PipelineDashboard", "sales/dashboards"], ["sales/dashboard/leads", "sales/dashboards", "LeadDashboard", "sales/dashboards"], ["sales/dashboard/activities", "sales/dashboards", "ActivityDashboard", "sales/dashboards"], ["sales/dashboard/calendar", "sales/dashboards", "CalendarDashboard", "sales/dashboards"], ["sales/customers", "sales/customers", "default", "sales/customers"], ["sales/companies", "sales/companies", "default", "sales/companies"], ["sales/contacts", "sales/contacts", "default", "sales/contacts"], ["sales/leads", "sales/leads", "default", "sales/leads"], ["sales/deals", "sales/deals", "default", "sales/deals"], ["sales/kanban", "sales/kanban", "default", "sales/kanban"], ["sales/pipelines", "sales/pipelines", "default", "sales/pipelines"], ["sales/quotes", "sales/quotes", "default", "sales/quotes"], ["sales/activities", "sales/activities", "default", "sales/activities"], ["sales/tasks", "sales/tasks", "default", "sales/tasks"], ["sales/meetings", "sales/meetings", "default", "sales/meetings"], ["sales/calls", "sales/calls", "default", "sales/calls"], ["sales/emails", "sales/emails", "default", "sales/emails"], ["sales/notes", "sales/notes", "default", "sales/notes"], ["sales/timeline", "sales/timeline", "default", "sales/timeline"], ["sales/documents", "sales/documents", "default", "sales/documents"], ["sales/campaigns", "sales/scoped", "CampaignsPage", "sales/scoped"], ["sales/segments", "sales/scoped", "SegmentsPage", "sales/scoped"], ["sales/tags", "sales/tags", "default", "sales/tags"], ["sales/custom-fields", "sales/custom-fields", "default", "sales/custom-fields"], ["sales/import", "sales/scoped", "ImportPage", "sales/scoped"], ["sales/export", "sales/export", "default", "sales/export"], ["sales/reports", "sales/reports", "default", "sales/reports"], ["sales/analytics", "sales/analytics", "default", "sales/analytics"], ["sales/automation", "sales/scoped", "AutomationPage", "sales/scoped"], ["sales/notifications", "sales/admin", "NotificationsPage", "sales/admin"], ["sales/roles", "sales/admin", "RolesPage", "sales/admin"], ["sales/permissions", "sales/admin", "PermissionsPage", "sales/admin"], ["sales/integrations", "sales/scoped", "IntegrationsPage", "sales/scoped"], ["sales/api-keys", "sales/scoped", "ApiKeysPage", "sales/scoped"], ["sales/audit-logs", "sales/admin", "AuditLogsPage", "sales/admin"], ["sales/settings", "sales/admin", "SettingsPage", "sales/admin"], ["sales/health", "sales/admin", "HealthPage", "sales/admin"], ["sales/capabilities", "sales/admin", "CapabilitiesPage", "sales/admin"], ]; function ensureDir(dir) { fs.mkdirSync(dir, { recursive: true }); } function writePage(routePath, modulePath, exportName) { const dir = path.join(APP, routePath); ensureDir(dir); const pageContent = exportName === "default" ? `"use client";\n\nexport { default } from "@/modules/crm/features/${modulePath}";\n` : `"use client";\n\nexport { ${exportName} as default } from "@/modules/crm/features/${modulePath}";\n`; fs.writeFileSync(path.join(dir, "page.tsx"), pageContent); for (const name of ["loading.tsx", "error.tsx"]) { const file = path.join(dir, name); if (!fs.existsSync(file)) { if (name === "loading.tsx") { fs.writeFileSync( file, `import { LoadingState } from "@/components/ds";\n\nexport default function Loading() {\n return ;\n}\n` ); } else { fs.writeFileSync( file, `"use client";\n\nimport { ErrorState } from "@/components/ds";\n\nexport default function Error({ error, reset }: { error: Error; reset: () => void }) {\n return ;\n}\n` ); } } } } // Root layouts ensureDir(path.join(APP)); if (!fs.existsSync(path.join(APP, "layout.tsx"))) { fs.writeFileSync( path.join(APP, "layout.tsx"), `/** CRM root — sub-routes supply portal shells. */\nexport default function CrmRootLayout({ children }: { children: React.ReactNode }) {\n return children;\n}\n` ); } if (!fs.existsSync(path.join(APP, "not-found.tsx"))) { fs.writeFileSync( path.join(APP, "not-found.tsx"), `import Link from "next/link";\nimport { Button, EmptyState } from "@/components/ds";\n\nexport default function CrmNotFound() {\n return (\n
\n }\n />\n
\n );\n}\n` ); } ensureDir(path.join(APP, "sales")); if (!fs.existsSync(path.join(APP, "sales", "layout.tsx"))) { fs.writeFileSync( path.join(APP, "sales", "layout.tsx"), `"use client";\n\nimport { createPortalLayout } from "@/modules/crm/components/createPortalLayout";\n\nexport default createPortalLayout("sales");\n` ); } for (const [route, mod, exp] of ROUTES) { writePage(route, mod, exp); } console.log(`Generated ${ROUTES.length} CRM routes under app/crm/`);