@atlashub/smartstack-cli 3.23.0 → 3.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +96 -24
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/templates/mcp-scaffolding/component.tsx.hbs +21 -1
- package/templates/skills/apex/references/smartstack-api.md +174 -5
- package/templates/skills/apex/references/smartstack-frontend.md +1101 -0
- package/templates/skills/apex/references/smartstack-layers.md +81 -5
- package/templates/skills/apex/steps/step-01-analyze.md +27 -3
- package/templates/skills/apex/steps/step-02-plan.md +5 -1
- package/templates/skills/apex/steps/step-03-execute.md +47 -5
- package/templates/skills/apex/steps/step-04-validate.md +300 -0
- package/templates/skills/apex/steps/step-05-examine.md +7 -0
- package/templates/skills/apex/steps/step-07-tests.md +19 -0
- package/templates/skills/business-analyse/_shared.md +6 -6
- package/templates/skills/business-analyse/patterns/suggestion-catalog.md +1 -1
- package/templates/skills/business-analyse/questionnaire/07-ui.md +3 -3
- package/templates/skills/business-analyse/references/cadrage-pre-analysis.md +1 -1
- package/templates/skills/business-analyse/references/entity-architecture-decision.md +3 -3
- package/templates/skills/business-analyse/references/handoff-file-templates.md +13 -5
- package/templates/skills/business-analyse/references/spec-auto-inference.md +14 -14
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +2 -2
- package/templates/skills/business-analyse/steps/step-02-decomposition.md +1 -1
- package/templates/skills/business-analyse/steps/step-03a1-setup.md +2 -2
- package/templates/skills/business-analyse/steps/step-03b-ui.md +2 -1
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +15 -4
- package/templates/skills/business-analyse/templates/tpl-frd.md +2 -2
- package/templates/skills/business-analyse/templates-frd.md +2 -2
- package/templates/skills/efcore/steps/migration/step-02-create.md +14 -1
- package/templates/skills/ralph-loop/references/category-rules.md +71 -9
- package/templates/skills/ralph-loop/references/compact-loop.md +3 -3
- package/templates/skills/ralph-loop/references/core-seed-data.md +10 -0
- package/templates/skills/ralph-loop/steps/step-02-execute.md +190 -1
- package/templates/skills/validate-feature/steps/step-01-compile.md +4 -1
- package/templates/skills/validate-feature/steps/step-05-db-validation.md +86 -1
package/dist/mcp-entry.mjs
CHANGED
|
@@ -34500,6 +34500,8 @@ public interface I{{name}}Service
|
|
|
34500
34500
|
const implementationTemplate = `using System.Threading;
|
|
34501
34501
|
using System.Threading.Tasks;
|
|
34502
34502
|
using System.Collections.Generic;
|
|
34503
|
+
using System.Linq;
|
|
34504
|
+
using Microsoft.EntityFrameworkCore;
|
|
34503
34505
|
using Microsoft.Extensions.Logging;
|
|
34504
34506
|
using SmartStack.Application.Common.Interfaces.Identity;
|
|
34505
34507
|
using SmartStack.Application.Common.Interfaces.Persistence;
|
|
@@ -34509,6 +34511,7 @@ namespace {{implNamespace}};
|
|
|
34509
34511
|
/// <summary>
|
|
34510
34512
|
/// Service implementation for {{name}} operations.
|
|
34511
34513
|
/// IMPORTANT: All queries MUST filter by _currentUser.TenantId for multi-tenant isolation.
|
|
34514
|
+
/// IMPORTANT: GetAllAsync MUST support search parameter for frontend EntityLookup component.
|
|
34512
34515
|
/// </summary>
|
|
34513
34516
|
public class {{name}}Service : I{{name}}Service
|
|
34514
34517
|
{
|
|
@@ -34532,6 +34535,10 @@ public class {{name}}Service : I{{name}}Service
|
|
|
34532
34535
|
{
|
|
34533
34536
|
_logger.LogInformation("Executing {{this}} for tenant {TenantId}", _currentUser.TenantId);
|
|
34534
34537
|
// TODO: Implement {{this}} \u2014 ALL queries must filter by _currentUser.TenantId
|
|
34538
|
+
// IMPORTANT: GetAllAsync MUST accept (string? search, int page, int pageSize) parameters
|
|
34539
|
+
// to enable EntityLookup search on the frontend. Example:
|
|
34540
|
+
// if (!string.IsNullOrWhiteSpace(search))
|
|
34541
|
+
// query = query.Where(x => x.Name.Contains(search) || x.Code.Contains(search));
|
|
34535
34542
|
await Task.CompletedTask;
|
|
34536
34543
|
throw new NotImplementedException();
|
|
34537
34544
|
}
|
|
@@ -35344,6 +35351,27 @@ export function use{{name}}(options: Use{{name}}Options = {}) {
|
|
|
35344
35351
|
const componentImportPath = hierarchy.context && hierarchy.module ? `@/components/${hierarchy.context.toLowerCase()}/${hierarchy.module.toLowerCase()}/${name}` : `./components/${name}`;
|
|
35345
35352
|
result.instructions.push(`import { ${name} } from '${componentImportPath}';`);
|
|
35346
35353
|
result.instructions.push(`import { use${name} } from '@/hooks/use${name}';`);
|
|
35354
|
+
result.instructions.push("");
|
|
35355
|
+
result.instructions.push("### IMPORTANT: Foreign Key Fields in Forms");
|
|
35356
|
+
result.instructions.push("If this entity has FK fields (e.g., EmployeeId, DepartmentId):");
|
|
35357
|
+
result.instructions.push("- NEVER render FK Guid fields as plain text inputs");
|
|
35358
|
+
result.instructions.push("- ALWAYS use EntityLookup component from @/components/ui/EntityLookup");
|
|
35359
|
+
result.instructions.push("- EntityLookup provides searchable dropdown with API-backed search");
|
|
35360
|
+
result.instructions.push("- See smartstack-frontend.md section 6 for the full EntityLookup pattern");
|
|
35361
|
+
result.instructions.push("");
|
|
35362
|
+
result.instructions.push("Example:");
|
|
35363
|
+
result.instructions.push("```tsx");
|
|
35364
|
+
result.instructions.push('import { EntityLookup } from "@/components/ui/EntityLookup";');
|
|
35365
|
+
result.instructions.push("");
|
|
35366
|
+
result.instructions.push("<EntityLookup");
|
|
35367
|
+
result.instructions.push(' apiEndpoint="/api/{context}/{app}/{related-entity}"');
|
|
35368
|
+
result.instructions.push(" value={formData.relatedEntityId}");
|
|
35369
|
+
result.instructions.push(' onChange={(id) => handleChange("relatedEntityId", id)}');
|
|
35370
|
+
result.instructions.push(' label="Related Entity"');
|
|
35371
|
+
result.instructions.push(" mapOption={(item) => ({ id: item.id, label: item.name, sublabel: item.code })}");
|
|
35372
|
+
result.instructions.push(" required");
|
|
35373
|
+
result.instructions.push("/>");
|
|
35374
|
+
result.instructions.push("```");
|
|
35347
35375
|
}
|
|
35348
35376
|
async function scaffoldTest(name, options, structure, config2, result, dryRun = false) {
|
|
35349
35377
|
const isSystemEntity = options?.isSystemEntity || false;
|
|
@@ -56982,16 +57010,46 @@ async function scaffoldRoutes(input, config2) {
|
|
|
56982
57010
|
result.instructions.push("");
|
|
56983
57011
|
result.instructions.push("Add routes to `contextRoutes.{context}[]` with **RELATIVE** paths (no leading `/`):");
|
|
56984
57012
|
result.instructions.push("");
|
|
57013
|
+
result.instructions.push("**IMPORTANT:** Pages are lazy-loaded. Use `<Suspense fallback={<PageLoader />}>` wrapper.");
|
|
57014
|
+
result.instructions.push("");
|
|
56985
57015
|
result.instructions.push("```tsx");
|
|
57016
|
+
result.instructions.push("import { lazy, Suspense } from 'react';");
|
|
57017
|
+
result.instructions.push("import { PageLoader } from '@/components/ui/PageLoader';");
|
|
57018
|
+
result.instructions.push("");
|
|
57019
|
+
const importedComponents = /* @__PURE__ */ new Set();
|
|
57020
|
+
for (const [context, applications] of Object.entries(routeTree)) {
|
|
57021
|
+
for (const [, modules] of Object.entries(applications)) {
|
|
57022
|
+
for (const route of modules) {
|
|
57023
|
+
const pageEntry = pageFiles.get(route.navRoute);
|
|
57024
|
+
if (pageEntry) {
|
|
57025
|
+
for (const entry of pageEntry) {
|
|
57026
|
+
if (!importedComponents.has(entry.componentName)) {
|
|
57027
|
+
importedComponents.add(entry.componentName);
|
|
57028
|
+
result.instructions.push(`const ${entry.componentName} = lazy(() =>`);
|
|
57029
|
+
result.instructions.push(` import('${entry.importPath}').then(m => ({ default: m.${entry.componentName} }))`);
|
|
57030
|
+
result.instructions.push(");");
|
|
57031
|
+
}
|
|
57032
|
+
}
|
|
57033
|
+
} else {
|
|
57034
|
+
const component = route.navRoute.split(".").map(capitalize).join("") + "Page";
|
|
57035
|
+
if (!importedComponents.has(component)) {
|
|
57036
|
+
importedComponents.add(component);
|
|
57037
|
+
result.instructions.push(`// TODO: const ${component} = lazy(() => import('@/pages/...'));`);
|
|
57038
|
+
}
|
|
57039
|
+
}
|
|
57040
|
+
}
|
|
57041
|
+
}
|
|
57042
|
+
}
|
|
57043
|
+
result.instructions.push("");
|
|
56986
57044
|
result.instructions.push("const contextRoutes: ContextRouteExtensions = {");
|
|
56987
57045
|
for (const [context, applications] of Object.entries(routeTree)) {
|
|
56988
57046
|
result.instructions.push(` ${context}: [`);
|
|
56989
57047
|
for (const [, modules] of Object.entries(applications)) {
|
|
56990
57048
|
for (const route of modules) {
|
|
56991
|
-
const modulePath = route.navRoute.split(".").slice(1).join("/");
|
|
57049
|
+
const modulePath = route.navRoute.split(".").slice(1).map(toKebabCase).join("/");
|
|
56992
57050
|
const pageEntry = pageFiles.get(route.navRoute);
|
|
56993
57051
|
const component = pageEntry?.[0]?.componentName || `${route.navRoute.split(".").map(capitalize).join("")}Page`;
|
|
56994
|
-
result.instructions.push(` { path: '${modulePath}', element:
|
|
57052
|
+
result.instructions.push(` { path: '${modulePath}', element: <Suspense fallback={<PageLoader />}><${component} /></Suspense> },`);
|
|
56995
57053
|
}
|
|
56996
57054
|
}
|
|
56997
57055
|
result.instructions.push(" ],");
|
|
@@ -57004,7 +57062,8 @@ async function scaffoldRoutes(input, config2) {
|
|
|
57004
57062
|
result.instructions.push("");
|
|
57005
57063
|
result.instructions.push('### Pattern B: JSX Routes (if App.tsx uses `<Route path="/{context}" element={<{Layout} />}>`)');
|
|
57006
57064
|
result.instructions.push("");
|
|
57007
|
-
result.instructions.push("Insert `<Route>` children INSIDE the appropriate Layout wrapper
|
|
57065
|
+
result.instructions.push("Insert `<Route>` children INSIDE the appropriate Layout wrapper.");
|
|
57066
|
+
result.instructions.push("**IMPORTANT:** Use `<Suspense fallback={<PageLoader />}>` for lazy-loaded pages.");
|
|
57008
57067
|
result.instructions.push("");
|
|
57009
57068
|
for (const [context, applications] of Object.entries(routeTree)) {
|
|
57010
57069
|
const layoutName = getLayoutName(context);
|
|
@@ -57013,10 +57072,10 @@ async function scaffoldRoutes(input, config2) {
|
|
|
57013
57072
|
result.instructions.push("```tsx");
|
|
57014
57073
|
for (const [, modules] of Object.entries(applications)) {
|
|
57015
57074
|
for (const route of modules) {
|
|
57016
|
-
const modulePath = route.navRoute.split(".").slice(1).join("/");
|
|
57075
|
+
const modulePath = route.navRoute.split(".").slice(1).map(toKebabCase).join("/");
|
|
57017
57076
|
const pageEntry = pageFiles.get(route.navRoute);
|
|
57018
57077
|
const component = pageEntry?.[0]?.componentName || `${route.navRoute.split(".").map(capitalize).join("")}Page`;
|
|
57019
|
-
result.instructions.push(`<Route path="${modulePath}" element={
|
|
57078
|
+
result.instructions.push(`<Route path="${modulePath}" element={<Suspense fallback={<PageLoader />}><${component} /></Suspense>} />`);
|
|
57020
57079
|
}
|
|
57021
57080
|
}
|
|
57022
57081
|
result.instructions.push("```");
|
|
@@ -57116,11 +57175,11 @@ async function discoverNavRoutes(structure, scope, warnings) {
|
|
|
57116
57175
|
permissions.push(match2[1]);
|
|
57117
57176
|
}
|
|
57118
57177
|
const fullNavRoute = suffix ? `${navRoute}.${suffix}` : navRoute;
|
|
57119
|
-
const expectedRoute = `api/${navRoute
|
|
57178
|
+
const expectedRoute = `api/${navRouteToUrlPath(navRoute)}${suffix ? `/${toKebabCase(suffix)}` : ""}`;
|
|
57120
57179
|
routes.push({
|
|
57121
57180
|
navRoute: fullNavRoute,
|
|
57122
|
-
apiPath: `/api/${navRoute
|
|
57123
|
-
webPath: `/${navRoute
|
|
57181
|
+
apiPath: `/api/${navRouteToUrlPath(navRoute)}${suffix ? `/${toKebabCase(suffix)}` : ""}`,
|
|
57182
|
+
webPath: `/${navRouteToUrlPath(navRoute)}${suffix ? `/${toKebabCase(suffix)}` : ""}`,
|
|
57124
57183
|
permissions,
|
|
57125
57184
|
controller: controllerName,
|
|
57126
57185
|
methods
|
|
@@ -57255,34 +57314,37 @@ function generateRouterConfig(routes, includeGuards) {
|
|
|
57255
57314
|
" */",
|
|
57256
57315
|
"",
|
|
57257
57316
|
"import { createBrowserRouter, RouteObject } from 'react-router-dom';",
|
|
57258
|
-
"import {
|
|
57317
|
+
"import { lazy, Suspense } from 'react';",
|
|
57318
|
+
"import { ROUTES } from './navRoutes.generated';",
|
|
57319
|
+
"import { PageLoader } from '@/components/ui/PageLoader';"
|
|
57259
57320
|
];
|
|
57260
57321
|
if (includeGuards) {
|
|
57261
57322
|
lines.push("import { ProtectedRoute, PermissionGuard } from './guards';");
|
|
57262
57323
|
}
|
|
57263
57324
|
const contexts = Object.keys(routeTree);
|
|
57264
57325
|
for (const context of contexts) {
|
|
57265
|
-
|
|
57326
|
+
const name = `${capitalize(context)}Layout`;
|
|
57327
|
+
lines.push(`const ${name} = lazy(() => import('../layouts/${name}').then(m => ({ default: m.${name} })));`);
|
|
57266
57328
|
}
|
|
57267
57329
|
lines.push("");
|
|
57268
|
-
lines.push("// Page imports - customize
|
|
57330
|
+
lines.push("// Page imports - lazy loaded (customize paths)");
|
|
57269
57331
|
for (const route of routes) {
|
|
57270
57332
|
const pageName = route.navRoute.split(".").map(capitalize).join("");
|
|
57271
|
-
lines.push(`//
|
|
57333
|
+
lines.push(`// const ${pageName}Page = lazy(() => import('../pages/${pageName}Page').then(m => ({ default: m.${pageName}Page })));`);
|
|
57272
57334
|
}
|
|
57273
57335
|
lines.push("");
|
|
57274
57336
|
lines.push("const routes: RouteObject[] = [");
|
|
57275
57337
|
for (const [context, applications] of Object.entries(routeTree)) {
|
|
57276
57338
|
lines.push(" {");
|
|
57277
57339
|
lines.push(` path: '${context}',`);
|
|
57278
|
-
lines.push(` element:
|
|
57340
|
+
lines.push(` element: <Suspense fallback={<PageLoader />}><${capitalize(context)}Layout /></Suspense>,`);
|
|
57279
57341
|
lines.push(" children: [");
|
|
57280
57342
|
for (const [app, modules] of Object.entries(applications)) {
|
|
57281
57343
|
lines.push(" {");
|
|
57282
57344
|
lines.push(` path: '${app}',`);
|
|
57283
57345
|
lines.push(" children: [");
|
|
57284
57346
|
for (const route of modules) {
|
|
57285
|
-
const modulePath = route.navRoute.split(".").slice(2).join("/");
|
|
57347
|
+
const modulePath = route.navRoute.split(".").slice(2).map(toKebabCase).join("/");
|
|
57286
57348
|
const pageName = route.navRoute.split(".").map(capitalize).join("");
|
|
57287
57349
|
if (includeGuards && route.permissions.length > 0) {
|
|
57288
57350
|
lines.push(" {");
|
|
@@ -57503,7 +57565,9 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
|
|
|
57503
57565
|
"",
|
|
57504
57566
|
"import type { RouteObject } from 'react-router-dom';",
|
|
57505
57567
|
"import type { ContextRouteExtensions } from '@atlashub/smartstack';",
|
|
57506
|
-
"import {
|
|
57568
|
+
"import { lazy, Suspense } from 'react';",
|
|
57569
|
+
"import { Navigate } from 'react-router-dom';",
|
|
57570
|
+
"import { PageLoader } from '@/components/ui/PageLoader';"
|
|
57507
57571
|
];
|
|
57508
57572
|
if (includeGuards) {
|
|
57509
57573
|
lines.push("import { ROUTES } from './navRoutes.generated';");
|
|
@@ -57516,7 +57580,9 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
|
|
|
57516
57580
|
if (pageEntries) {
|
|
57517
57581
|
for (const entry of pageEntries) {
|
|
57518
57582
|
if (!importedComponents.has(entry.componentName)) {
|
|
57519
|
-
lines.push(`
|
|
57583
|
+
lines.push(`const ${entry.componentName} = lazy(() =>`);
|
|
57584
|
+
lines.push(` import('${entry.importPath}').then(m => ({ default: m.${entry.componentName} }))`);
|
|
57585
|
+
lines.push(");");
|
|
57520
57586
|
importedComponents.add(entry.componentName);
|
|
57521
57587
|
}
|
|
57522
57588
|
}
|
|
@@ -57525,7 +57591,7 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
|
|
|
57525
57591
|
for (const route of routes) {
|
|
57526
57592
|
if (!pageFiles.has(route.navRoute)) {
|
|
57527
57593
|
const pageName = route.navRoute.split(".").map(capitalize).join("") + "Page";
|
|
57528
|
-
lines.push(`// TODO:
|
|
57594
|
+
lines.push(`// TODO: const ${pageName} = lazy(() => import('@/pages/...').then(m => ({ default: m.${pageName} })));`);
|
|
57529
57595
|
}
|
|
57530
57596
|
}
|
|
57531
57597
|
lines.push("");
|
|
@@ -57542,10 +57608,10 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
|
|
|
57542
57608
|
lines.push(" {");
|
|
57543
57609
|
lines.push(` path: '${app}',`);
|
|
57544
57610
|
lines.push(" children: [");
|
|
57545
|
-
const firstModulePath = modules[0].navRoute.split(".").slice(2).join("/");
|
|
57611
|
+
const firstModulePath = modules[0].navRoute.split(".").slice(2).map(toKebabCase).join("/");
|
|
57546
57612
|
lines.push(` { index: true, element: <Navigate to="${firstModulePath}" replace /> },`);
|
|
57547
57613
|
for (const route of modules) {
|
|
57548
|
-
const modulePath = route.navRoute.split(".").slice(2).join("/");
|
|
57614
|
+
const modulePath = route.navRoute.split(".").slice(2).map(toKebabCase).join("/");
|
|
57549
57615
|
const pageName = route.navRoute.split(".").map(capitalize).join("") + "Page";
|
|
57550
57616
|
const pageEntry = pageFiles.get(route.navRoute);
|
|
57551
57617
|
const component = pageEntry?.[0]?.componentName || pageName;
|
|
@@ -57554,7 +57620,7 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
|
|
|
57554
57620
|
lines.push(` {`);
|
|
57555
57621
|
lines.push(` path: '${modulePath}',`);
|
|
57556
57622
|
if (hasRealPage) {
|
|
57557
|
-
lines.push(` element: <PermissionGuard permissions={ROUTES['${route.navRoute}'].permissions}><${component} /></PermissionGuard>,`);
|
|
57623
|
+
lines.push(` element: <Suspense fallback={<PageLoader />}><PermissionGuard permissions={ROUTES['${route.navRoute}'].permissions}><${component} /></PermissionGuard></Suspense>,`);
|
|
57558
57624
|
} else {
|
|
57559
57625
|
lines.push(` element: <PermissionGuard permissions={ROUTES['${route.navRoute}'].permissions}><div>TODO: ${component}</div></PermissionGuard>,`);
|
|
57560
57626
|
}
|
|
@@ -57562,7 +57628,7 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
|
|
|
57562
57628
|
} else {
|
|
57563
57629
|
lines.push(` {`);
|
|
57564
57630
|
lines.push(` path: '${modulePath}',`);
|
|
57565
|
-
lines.push(` element: ${hasRealPage ?
|
|
57631
|
+
lines.push(` element: ${hasRealPage ? `<Suspense fallback={<PageLoader />}><${component} /></Suspense>` : `<div>TODO: ${component}</div>`},`);
|
|
57566
57632
|
lines.push(` },`);
|
|
57567
57633
|
}
|
|
57568
57634
|
}
|
|
@@ -57570,7 +57636,7 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
|
|
|
57570
57636
|
lines.push(" },");
|
|
57571
57637
|
} else {
|
|
57572
57638
|
for (const route of modules) {
|
|
57573
|
-
const fullPath = route.navRoute.split(".").slice(1).join("/");
|
|
57639
|
+
const fullPath = route.navRoute.split(".").slice(1).map(toKebabCase).join("/");
|
|
57574
57640
|
const pageName = route.navRoute.split(".").map(capitalize).join("") + "Page";
|
|
57575
57641
|
const pageEntry = pageFiles.get(route.navRoute);
|
|
57576
57642
|
const component = pageEntry?.[0]?.componentName || pageName;
|
|
@@ -57579,7 +57645,7 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
|
|
|
57579
57645
|
lines.push(` {`);
|
|
57580
57646
|
lines.push(` path: '${fullPath}',`);
|
|
57581
57647
|
if (hasRealPage) {
|
|
57582
|
-
lines.push(` element: <PermissionGuard permissions={ROUTES['${route.navRoute}'].permissions}><${component} /></PermissionGuard>,`);
|
|
57648
|
+
lines.push(` element: <Suspense fallback={<PageLoader />}><PermissionGuard permissions={ROUTES['${route.navRoute}'].permissions}><${component} /></PermissionGuard></Suspense>,`);
|
|
57583
57649
|
} else {
|
|
57584
57650
|
lines.push(` element: <PermissionGuard permissions={ROUTES['${route.navRoute}'].permissions}><div>TODO: ${component}</div></PermissionGuard>,`);
|
|
57585
57651
|
}
|
|
@@ -57587,7 +57653,7 @@ function generateClientRoutesConfig(routes, pageFiles, includeGuards) {
|
|
|
57587
57653
|
} else {
|
|
57588
57654
|
lines.push(` {`);
|
|
57589
57655
|
lines.push(` path: '${fullPath}',`);
|
|
57590
|
-
lines.push(` element: ${hasRealPage ?
|
|
57656
|
+
lines.push(` element: ${hasRealPage ? `<Suspense fallback={<PageLoader />}><${component} /></Suspense>` : `<div>TODO: ${component}</div>`},`);
|
|
57591
57657
|
lines.push(` },`);
|
|
57592
57658
|
}
|
|
57593
57659
|
}
|
|
@@ -57632,6 +57698,12 @@ function getLayoutName(context) {
|
|
|
57632
57698
|
};
|
|
57633
57699
|
return layoutMap[context] || `${capitalize(context)}Layout`;
|
|
57634
57700
|
}
|
|
57701
|
+
function toKebabCase(segment) {
|
|
57702
|
+
return segment.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
57703
|
+
}
|
|
57704
|
+
function navRouteToUrlPath(navRoute) {
|
|
57705
|
+
return navRoute.split(".").map(toKebabCase).join("/");
|
|
57706
|
+
}
|
|
57635
57707
|
function capitalize(str) {
|
|
57636
57708
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
57637
57709
|
}
|