@atlashub/smartstack-cli 3.37.0 → 3.38.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 +16 -24
- package/dist/index.js.map +1 -1
- package/dist/mcp-entry.mjs +201 -256
- package/dist/mcp-entry.mjs.map +1 -1
- package/package.json +1 -1
- package/scripts/extract-api-endpoints.ts +5 -5
- package/scripts/generate-doc-with-mock-ui.ts +10 -17
- package/templates/agents/ba-reader.md +9 -9
- package/templates/agents/ba-writer.md +12 -15
- package/templates/agents/code-reviewer.md +1 -1
- package/templates/agents/docs-context-reader.md +1 -1
- package/templates/agents/gitflow/merge.md +0 -4
- package/templates/agents/gitflow/pr.md +0 -4
- package/templates/agents/gitflow/start.md +30 -5
- package/templates/mcp-scaffolding/frontend/nav-routes.ts.hbs +20 -20
- package/templates/mcp-scaffolding/frontend/routes.tsx.hbs +16 -24
- package/templates/mcp-scaffolding/migrations/seed-roles.cs.hbs +2 -2
- package/templates/skills/_resources/mcp-validate-documentation-spec.md +3 -3
- package/templates/skills/_shared.md +15 -17
- package/templates/skills/ai-prompt/SKILL.md +1 -1
- package/templates/skills/ai-prompt/steps/step-00-init.md +47 -0
- package/templates/skills/apex/SKILL.md +3 -4
- package/templates/skills/apex/_shared.md +10 -20
- package/templates/skills/apex/references/analysis-methods.md +141 -0
- package/templates/skills/apex/references/challenge-questions.md +1 -21
- package/templates/skills/apex/references/core-seed-data.md +35 -57
- package/templates/skills/apex/references/examine-build-validation.md +87 -0
- package/templates/skills/apex/references/execution-frontend-gates.md +177 -0
- package/templates/skills/apex/references/execution-frontend-patterns.md +105 -0
- package/templates/skills/apex/references/execution-layer1-rules.md +96 -0
- package/templates/skills/apex/references/initialization-challenge-flow.md +110 -0
- package/templates/skills/apex/references/planning-layer-mapping.md +151 -0
- package/templates/skills/apex/references/post-checks.md +145 -40
- package/templates/skills/apex/references/smartstack-api.md +35 -51
- package/templates/skills/apex/references/smartstack-frontend.md +17 -17
- package/templates/skills/apex/references/smartstack-layers.md +38 -62
- package/templates/skills/apex/steps/step-00-init.md +14 -26
- package/templates/skills/apex/steps/step-01-analyze.md +10 -143
- package/templates/skills/apex/steps/step-02-plan.md +10 -92
- package/templates/skills/apex/steps/step-03-execute.md +47 -249
- package/templates/skills/apex/steps/step-04-examine.md +14 -78
- package/templates/skills/apex/steps/step-05-deep-review.md +2 -2
- package/templates/skills/apex/steps/step-08-run-tests.md +2 -0
- package/templates/skills/application/SKILL.md +6 -7
- package/templates/skills/application/references/backend-controller-hierarchy.md +16 -16
- package/templates/skills/application/references/backend-seeding-and-dto-output.md +83 -0
- package/templates/skills/application/references/backend-table-prefix-mapping.md +79 -0
- package/templates/skills/application/references/backend-verification.md +1 -1
- package/templates/skills/application/references/frontend-i18n-and-output.md +67 -0
- package/templates/skills/application/references/frontend-route-naming.md +117 -0
- package/templates/skills/application/references/frontend-route-wiring-app-tsx.md +107 -0
- package/templates/skills/application/references/frontend-verification.md +12 -12
- package/templates/skills/application/references/init-parameter-detection.md +120 -0
- package/templates/skills/application/references/migration-checklist-troubleshooting.md +100 -0
- package/templates/skills/application/references/nav-fallback-procedure.md +5 -6
- package/templates/skills/application/references/provider-template.md +2 -6
- package/templates/skills/application/references/roles-client-project-handling.md +55 -0
- package/templates/skills/application/references/roles-fallback-procedure.md +149 -0
- package/templates/skills/application/references/test-coverage-requirements.md +213 -0
- package/templates/skills/application/references/test-frontend.md +3 -3
- package/templates/skills/application/steps/step-00-init.md +11 -141
- package/templates/skills/application/steps/step-01-navigation.md +3 -3
- package/templates/skills/application/steps/step-02-permissions.md +4 -4
- package/templates/skills/application/steps/step-03-roles.md +18 -175
- package/templates/skills/application/steps/step-03b-provider.md +1 -2
- package/templates/skills/application/steps/step-04-backend.md +19 -110
- package/templates/skills/application/steps/step-05-frontend.md +17 -143
- package/templates/skills/application/steps/step-06-migration.md +12 -60
- package/templates/skills/application/steps/step-07-tests.md +9 -76
- package/templates/skills/application/templates-backend.md +29 -27
- package/templates/skills/application/templates-frontend.md +48 -48
- package/templates/skills/application/templates-seed.md +57 -131
- package/templates/skills/business-analyse/SKILL.md +27 -30
- package/templates/skills/business-analyse/_architecture.md +6 -6
- package/templates/skills/business-analyse/_shared.md +60 -88
- package/templates/skills/business-analyse/questionnaire/04-data.md +3 -3
- package/templates/skills/business-analyse/questionnaire/06-security.md +1 -1
- package/templates/skills/business-analyse/questionnaire/13-cross-module.md +1 -1
- package/templates/skills/business-analyse/react/application-viewer.md +12 -12
- package/templates/skills/business-analyse/react/components.md +8 -12
- package/templates/skills/business-analyse/react/schema.md +11 -11
- package/templates/skills/business-analyse/references/agent-module-prompt.md +2 -3
- package/templates/skills/business-analyse/references/analysis-semantic-checks.md +190 -0
- package/templates/skills/business-analyse/references/cache-warming-strategy.md +2 -2
- package/templates/skills/business-analyse/references/cadrage-challenge-patterns.md +41 -0
- package/templates/skills/business-analyse/references/cadrage-coverage-matrix.md +74 -0
- package/templates/skills/business-analyse/references/cadrage-shared-modules.md +69 -0
- package/templates/skills/business-analyse/references/cadrage-structure-cards.md +1 -1
- package/templates/skills/business-analyse/references/compilation-structure-cards.md +297 -0
- package/templates/skills/business-analyse/references/consolidation-structural-checks.md +2 -2
- package/templates/skills/business-analyse/references/deploy-modes.md +5 -5
- package/templates/skills/business-analyse/references/detection-strategies.md +7 -7
- package/templates/skills/business-analyse/references/handoff-file-templates.md +14 -22
- package/templates/skills/business-analyse/references/handoff-mappings.md +4 -4
- package/templates/skills/business-analyse/references/handoff-seeddata-generation.md +312 -0
- package/templates/skills/business-analyse/references/init-schema-deployment.md +3 -3
- package/templates/skills/business-analyse/references/naming-conventions.md +22 -24
- package/templates/skills/business-analyse/references/prd-generation.md +2 -2
- package/templates/skills/business-analyse/references/review-data-mapping.md +2 -2
- package/templates/skills/business-analyse/references/robustness-checks.md +1 -1
- package/templates/skills/business-analyse/references/spec-auto-inference.md +3 -3
- package/templates/skills/business-analyse/references/team-orchestration.md +49 -6
- package/templates/skills/business-analyse/references/ui-dashboard-spec.md +1 -1
- package/templates/skills/business-analyse/references/ui-resource-cards.md +18 -18
- package/templates/skills/business-analyse/references/validate-incremental-html.md +2 -2
- package/templates/skills/business-analyse/references/validation-checklist.md +2 -2
- package/templates/skills/business-analyse/schemas/application-schema.json +4 -5
- package/templates/skills/business-analyse/schemas/project-schema.json +1 -6
- package/templates/skills/business-analyse/schemas/sections/metadata-schema.json +2 -3
- package/templates/skills/business-analyse/schemas/sections/specification-schema.json +4 -4
- package/templates/skills/business-analyse/steps/step-00-init.md +8 -17
- package/templates/skills/business-analyse/steps/step-01-cadrage.md +35 -198
- package/templates/skills/business-analyse/steps/step-01b-applications.md +16 -20
- package/templates/skills/business-analyse/steps/step-02-decomposition.md +1 -1
- package/templates/skills/business-analyse/steps/step-03a1-setup.md +4 -4
- package/templates/skills/business-analyse/steps/step-03a2-analysis.md +1 -1
- package/templates/skills/business-analyse/steps/step-03b-ui.md +4 -4
- package/templates/skills/business-analyse/steps/step-03c-compile.md +66 -140
- package/templates/skills/business-analyse/steps/step-03d-validate.md +2 -2
- package/templates/skills/business-analyse/steps/step-04a-collect.md +2 -2
- package/templates/skills/business-analyse/steps/step-04b-analyze.md +42 -160
- package/templates/skills/business-analyse/steps/step-04c-decide.md +1 -1
- package/templates/skills/business-analyse/steps/step-05a-handoff.md +74 -104
- package/templates/skills/business-analyse/steps/step-05b-deploy.md +13 -11
- package/templates/skills/business-analyse/steps/step-06-review.md +3 -3
- package/templates/skills/business-analyse/templates/tpl-frd.md +13 -13
- package/templates/skills/business-analyse/templates/tpl-handoff.md +12 -12
- package/templates/skills/business-analyse/templates-frd.md +25 -25
- package/templates/skills/business-analyse/templates-react.md +15 -21
- package/templates/skills/controller/SKILL.md +1 -1
- package/templates/skills/controller/postman-templates.md +1 -1
- package/templates/skills/controller/references/controller-code-templates.md +2 -2
- package/templates/skills/controller/references/mcp-scaffold-workflow.md +209 -0
- package/templates/skills/controller/references/permission-sync-templates.md +13 -16
- package/templates/skills/controller/steps/step-00-init.md +11 -11
- package/templates/skills/controller/steps/step-03-generate.md +64 -103
- package/templates/skills/controller/templates.md +67 -71
- package/templates/skills/debug/SKILL.md +13 -218
- package/templates/skills/debug/steps/step-00-init.md +57 -0
- package/templates/skills/debug/steps/step-01-analyze.md +219 -0
- package/templates/skills/debug/steps/step-02-resolve.md +85 -0
- package/templates/skills/documentation/SKILL.md +49 -345
- package/templates/skills/documentation/data-schema.md +11 -8
- package/templates/skills/documentation/steps/step-00-init.md +70 -0
- package/templates/skills/documentation/steps/step-01-scan.md +113 -0
- package/templates/skills/documentation/steps/step-02-generate.md +231 -0
- package/templates/skills/documentation/steps/step-03-validate.md +238 -0
- package/templates/skills/documentation/templates.md +480 -322
- package/templates/skills/efcore/references/both-contexts.md +32 -0
- package/templates/skills/efcore/references/database-operations.md +67 -0
- package/templates/skills/efcore/references/destructive-operations.md +38 -0
- package/templates/skills/efcore/references/reset-operations.md +81 -0
- package/templates/skills/efcore/references/seed-methods.md +86 -0
- package/templates/skills/efcore/references/shared-init-functions.md +250 -0
- package/templates/skills/efcore/references/sql-objects-injection.md +61 -0
- package/templates/skills/efcore/references/troubleshooting.md +81 -0
- package/templates/skills/efcore/steps/db/step-deploy.md +1 -32
- package/templates/skills/efcore/steps/db/step-reset.md +7 -103
- package/templates/skills/efcore/steps/db/step-seed.md +10 -132
- package/templates/skills/efcore/steps/db/step-status.md +5 -44
- package/templates/skills/efcore/steps/migration/step-03-validate.md +8 -62
- package/templates/skills/efcore/steps/rebase-snapshot/step-03-create.md +1 -57
- package/templates/skills/efcore/steps/shared/step-00-init.md +11 -254
- package/templates/skills/efcore/steps/squash/step-03-create.md +1 -58
- package/templates/skills/feature-full/SKILL.md +1 -1
- package/templates/skills/feature-full/steps/step-00-init.md +57 -0
- package/templates/skills/feature-full/steps/step-01-implementation.md +1 -1
- package/templates/skills/gitflow/SKILL.md +1 -1
- package/templates/skills/gitflow/_shared.md +23 -0
- package/templates/skills/gitflow/references/commit-message-generation.md +58 -0
- package/templates/skills/gitflow/references/commit-migration-validation.md +49 -0
- package/templates/skills/gitflow/references/finish-cleanup.md +51 -0
- package/templates/skills/gitflow/references/finish-version-bumping.md +45 -0
- package/templates/skills/gitflow/references/init-environment-detection.md +41 -0
- package/templates/skills/gitflow/references/init-questions.md +185 -0
- package/templates/skills/gitflow/references/init-structure-creation.md +71 -0
- package/templates/skills/gitflow/references/init-version-detection.md +21 -0
- package/templates/skills/gitflow/references/init-workspace-detection.md +43 -0
- package/templates/skills/gitflow/references/merge-ci-status.md +36 -0
- package/templates/skills/gitflow/references/merge-execution.md +62 -0
- package/templates/skills/gitflow/references/merge-pr-context.md +76 -0
- package/templates/skills/gitflow/references/pr-build-checks.md +60 -0
- package/templates/skills/gitflow/references/pr-generation.md +58 -0
- package/templates/skills/gitflow/references/start-branch-normalization.md +28 -0
- package/templates/skills/gitflow/references/start-worktree-creation.md +50 -0
- package/templates/skills/gitflow/references/sync-push-verify.md +44 -0
- package/templates/skills/gitflow/references/sync-rebase-conflicts.md +38 -0
- package/templates/skills/gitflow/steps/step-commit.md +12 -91
- package/templates/skills/gitflow/steps/step-finish.md +15 -159
- package/templates/skills/gitflow/steps/step-init.md +24 -326
- package/templates/skills/gitflow/steps/step-merge.md +17 -176
- package/templates/skills/gitflow/steps/step-pr.md +10 -116
- package/templates/skills/gitflow/steps/step-start.md +16 -109
- package/templates/skills/gitflow/steps/step-sync.md +6 -69
- package/templates/skills/ralph-loop/SKILL.md +6 -0
- package/templates/skills/ralph-loop/references/category-completeness.md +185 -0
- package/templates/skills/ralph-loop/references/compact-loop.md +1 -1
- package/templates/skills/ralph-loop/references/init-resume-recovery.md +127 -0
- package/templates/skills/ralph-loop/references/module-transition.md +151 -0
- package/templates/skills/ralph-loop/references/multi-module-queue.md +171 -0
- package/templates/skills/ralph-loop/references/parallel-execution.md +246 -0
- package/templates/skills/ralph-loop/references/task-transform-legacy.md +6 -9
- package/templates/skills/ralph-loop/references/team-orchestration.md +45 -3
- package/templates/skills/ralph-loop/steps/step-00-init.md +36 -109
- package/templates/skills/ralph-loop/steps/step-01-task.md +15 -163
- package/templates/skills/ralph-loop/steps/step-02-execute.md +8 -154
- package/templates/skills/ralph-loop/steps/step-04-check.md +21 -73
- package/templates/skills/review-code/references/owasp-api-top10.md +5 -5
- package/templates/skills/review-code/references/smartstack-conventions.md +11 -11
- package/templates/skills/validate-feature/references/api-smoke-tests.md +140 -0
- package/templates/skills/validate-feature/references/db-validation-checks.md +180 -0
- package/templates/skills/validate-feature/steps/step-01-compile.md +5 -2
- package/templates/skills/validate-feature/steps/step-04-api-smoke.md +34 -145
- package/templates/skills/validate-feature/steps/step-05-db-validation.md +74 -260
- package/templates/skills/workflow/SKILL.md +1 -1
- package/templates/skills/workflow/steps/step-00-init.md +57 -0
|
@@ -14,7 +14,7 @@ if [ -n "$SEED_FILES" ]; then
|
|
|
14
14
|
if [ -n "$BAD_ROUTES" ]; then
|
|
15
15
|
echo "BLOCKING: Navigation routes must be full paths starting with /"
|
|
16
16
|
echo "$BAD_ROUTES"
|
|
17
|
-
echo "Expected: \"/
|
|
17
|
+
echo "Expected: \"/human-resources\" NOT \"humanresources\""
|
|
18
18
|
exit 1
|
|
19
19
|
fi
|
|
20
20
|
fi
|
|
@@ -512,35 +512,7 @@ if [ -n "$CREATE_VALIDATORS" ]; then
|
|
|
512
512
|
fi
|
|
513
513
|
```
|
|
514
514
|
|
|
515
|
-
### POST-CHECK 19:
|
|
516
|
-
|
|
517
|
-
```bash
|
|
518
|
-
# NavigationContext IDs (business, platform, personal) are pre-seeded by SmartStack core
|
|
519
|
-
# with hardcoded GUIDs. Client code MUST look them up by code at runtime, NEVER generate them.
|
|
520
|
-
SEED_CONST_FILES=$(find src/ -path "*/Seeding/*" -name "SeedConstants.cs" 2>/dev/null)
|
|
521
|
-
SEED_ALL_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*.cs" 2>/dev/null)
|
|
522
|
-
if [ -n "$SEED_CONST_FILES" ]; then
|
|
523
|
-
BAD_CONTEXT_ID=$(grep -Pn 'ContextId\s*=' $SEED_CONST_FILES 2>/dev/null)
|
|
524
|
-
if [ -n "$BAD_CONTEXT_ID" ]; then
|
|
525
|
-
echo "BLOCKING: SeedConstants must NOT contain a ContextId constant"
|
|
526
|
-
echo "NavigationContext IDs are pre-seeded by SmartStack core with hardcoded GUIDs"
|
|
527
|
-
echo "Fix: Remove ContextId from SeedConstants. In SeedDataProvider, query:"
|
|
528
|
-
echo " var ctx = await db.NavigationContexts.FirstOrDefaultAsync(c => c.Code == \"business\", ct);"
|
|
529
|
-
echo "$BAD_CONTEXT_ID"
|
|
530
|
-
exit 1
|
|
531
|
-
fi
|
|
532
|
-
fi
|
|
533
|
-
if [ -n "$SEED_ALL_FILES" ]; then
|
|
534
|
-
BAD_CTX_GUID=$(grep -Pn 'DeterministicGuid\("nav:(business|platform|personal)"\)' $SEED_ALL_FILES 2>/dev/null)
|
|
535
|
-
if [ -n "$BAD_CTX_GUID" ]; then
|
|
536
|
-
echo "BLOCKING: Deterministic GUID for NavigationContext detected"
|
|
537
|
-
echo "Context IDs (business, platform, personal) are pre-seeded by SmartStack core"
|
|
538
|
-
echo "Fix: Look up context by code at runtime in SeedDataProvider.SeedNavigationAsync()"
|
|
539
|
-
echo "$BAD_CTX_GUID"
|
|
540
|
-
exit 1
|
|
541
|
-
fi
|
|
542
|
-
fi
|
|
543
|
-
```
|
|
515
|
+
### POST-CHECK 19: (REMOVED — Context level no longer exists in SmartStack navigation hierarchy)
|
|
544
516
|
|
|
545
517
|
### POST-CHECK 20: RolePermission seed data must NOT use deterministic role GUIDs
|
|
546
518
|
|
|
@@ -764,12 +736,12 @@ if [ -n "$PERM_FILES" ]; then
|
|
|
764
736
|
PATH_VAL=$(echo "$line" | grep -oP '"[^"]*\.[^"]*"' | tr -d '"')
|
|
765
737
|
if [ -n "$PATH_VAL" ]; then
|
|
766
738
|
DOTS=$(echo "$PATH_VAL" | tr -cd '.' | wc -c)
|
|
767
|
-
# Module permissions:
|
|
768
|
-
# Section permissions:
|
|
739
|
+
# Module permissions: 2 dots (app.module.action = 3 segments = 2+1)
|
|
740
|
+
# Section permissions: 3 dots (app.module.section.action = 4 segments = 3+1)
|
|
769
741
|
# Wildcard: ends with .* (valid at any level)
|
|
770
742
|
if echo "$PATH_VAL" | grep -qP '\.\*$'; then
|
|
771
743
|
continue # Wildcards are valid
|
|
772
|
-
elif [ "$DOTS" -lt
|
|
744
|
+
elif [ "$DOTS" -lt 2 ] || [ "$DOTS" -gt 4 ]; then
|
|
773
745
|
echo "WARNING: Permission path has unexpected segment count ($((DOTS+1)) segments): $PATH_VAL"
|
|
774
746
|
fi
|
|
775
747
|
fi
|
|
@@ -1072,7 +1044,7 @@ fi
|
|
|
1072
1044
|
```bash
|
|
1073
1045
|
# NavRoute segments are navigation entity Codes joined by dots.
|
|
1074
1046
|
# Multi-word codes MUST use kebab-case (e.g., "human-resources", NOT "humanresources").
|
|
1075
|
-
# Verified from SmartStack.app: "
|
|
1047
|
+
# Verified from SmartStack.app: "support-client.my-tickets", "administration.access-requests"
|
|
1076
1048
|
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
1077
1049
|
if [ -n "$CTRL_FILES" ]; then
|
|
1078
1050
|
for f in $CTRL_FILES; do
|
|
@@ -1084,7 +1056,7 @@ if [ -n "$CTRL_FILES" ]; then
|
|
|
1084
1056
|
echo "BLOCKING: NavRoute segment '$SEG' in $f appears to be concatenated multi-word without hyphens"
|
|
1085
1057
|
echo " Full NavRoute: $NAVROUTE_VAL"
|
|
1086
1058
|
echo " Fix: Use kebab-case: e.g., 'humanresources' → 'human-resources'"
|
|
1087
|
-
echo " SmartStack convention (from SmartStack.app): '
|
|
1059
|
+
echo " SmartStack convention (from SmartStack.app): 'support-client.my-tickets'"
|
|
1088
1060
|
exit 1
|
|
1089
1061
|
fi
|
|
1090
1062
|
done
|
|
@@ -1110,8 +1082,8 @@ fi
|
|
|
1110
1082
|
|
|
1111
1083
|
```bash
|
|
1112
1084
|
# Permission codes in [RequirePermission] and Permissions.cs MUST use kebab-case for multi-word segments.
|
|
1113
|
-
# SmartStack.app convention: "
|
|
1114
|
-
# FORBIDDEN: "
|
|
1085
|
+
# SmartStack.app convention: "support-client.my-tickets.read" (kebab-case everywhere)
|
|
1086
|
+
# FORBIDDEN: "humanresources.employees.read" — must be "human-resources.employees.read"
|
|
1115
1087
|
|
|
1116
1088
|
# Check [RequirePermission] attributes in controllers
|
|
1117
1089
|
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
@@ -1126,7 +1098,7 @@ if [ -n "$CTRL_FILES" ]; then
|
|
|
1126
1098
|
echo "BLOCKING: Permission code segment '$SEG' in $f appears concatenated without hyphens"
|
|
1127
1099
|
echo " Full permission: $PERM"
|
|
1128
1100
|
echo " Fix: Use kebab-case: e.g., 'humanresources' → 'human-resources'"
|
|
1129
|
-
echo " SmartStack convention: '
|
|
1101
|
+
echo " SmartStack convention: 'support-client.my-tickets.read'"
|
|
1130
1102
|
exit 1
|
|
1131
1103
|
fi
|
|
1132
1104
|
done
|
|
@@ -1378,8 +1350,8 @@ fi
|
|
|
1378
1350
|
### POST-CHECK 48: NavRoute attribute values must use kebab-case (BLOCKING)
|
|
1379
1351
|
|
|
1380
1352
|
```bash
|
|
1381
|
-
# Root cause (test-apex-007): Controllers had [NavRoute("
|
|
1382
|
-
# instead of [NavRoute("
|
|
1353
|
+
# Root cause (test-apex-007): Controllers had [NavRoute("humanresources.employees")]
|
|
1354
|
+
# instead of [NavRoute("human-resources.employees")]. This causes route mismatch with
|
|
1383
1355
|
# seed data and permission codes, resulting in 404s at runtime.
|
|
1384
1356
|
CTRL_FILES=$(find src/ -path "*/Controllers/*" -name "*Controller.cs" 2>/dev/null)
|
|
1385
1357
|
if [ -n "$CTRL_FILES" ]; then
|
|
@@ -1476,4 +1448,137 @@ if [ -n "$CTRL_FILES" ]; then
|
|
|
1476
1448
|
fi
|
|
1477
1449
|
```
|
|
1478
1450
|
|
|
1451
|
+
### POST-CHECK 51: RolesSeedData must map standard role-permission matrix (BLOCKING)
|
|
1452
|
+
|
|
1453
|
+
```bash
|
|
1454
|
+
# SmartStack standard role-permission matrix:
|
|
1455
|
+
# Admin = wildcard (*) — full access
|
|
1456
|
+
# Manager = CRU (read + create + update) — no delete
|
|
1457
|
+
# Contributor = CR (read + create) — no update, no delete
|
|
1458
|
+
# Viewer = R (read only)
|
|
1459
|
+
# If RolesSeedData deviates from this matrix, the RBAC model is broken.
|
|
1460
|
+
ROLE_SEED_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*RolesSeedData.cs" ! -name "ApplicationRolesSeedData.cs" 2>/dev/null)
|
|
1461
|
+
if [ -n "$ROLE_SEED_FILES" ]; then
|
|
1462
|
+
FAIL=false
|
|
1463
|
+
for f in $ROLE_SEED_FILES; do
|
|
1464
|
+
# Skip ApplicationRolesSeedData (defines roles, not mappings)
|
|
1465
|
+
BASENAME=$(basename "$f")
|
|
1466
|
+
if [ "$BASENAME" = "ApplicationRolesSeedData.cs" ]; then continue; fi
|
|
1467
|
+
|
|
1468
|
+
# Check Admin has wildcard
|
|
1469
|
+
HAS_ADMIN_WILDCARD=$(grep -Pc '(admin|Admin).*\*' "$f" 2>/dev/null)
|
|
1470
|
+
if [ "$HAS_ADMIN_WILDCARD" -eq 0 ]; then
|
|
1471
|
+
# Also accept .Access or wildcard pattern
|
|
1472
|
+
HAS_ADMIN_ACCESS=$(grep -Pc '(admin|Admin).*(Access|Wildcard|IsWildcard)' "$f" 2>/dev/null)
|
|
1473
|
+
if [ "$HAS_ADMIN_ACCESS" -eq 0 ]; then
|
|
1474
|
+
echo "BLOCKING: Admin role missing wildcard (*) permission in $f"
|
|
1475
|
+
echo "Fix: Admin must map to wildcard permission (navRoute.*) or use IsWildcard=true"
|
|
1476
|
+
FAIL=true
|
|
1477
|
+
fi
|
|
1478
|
+
fi
|
|
1479
|
+
|
|
1480
|
+
# Check Viewer has NO delete/create/update
|
|
1481
|
+
VIEWER_WRITE=$(grep -Pc '(viewer|Viewer).*(\.delete|\.create|\.update|Delete|Create|Update)' "$f" 2>/dev/null)
|
|
1482
|
+
if [ "$VIEWER_WRITE" -gt 0 ]; then
|
|
1483
|
+
echo "BLOCKING: Viewer role has write permissions (create/update/delete) in $f"
|
|
1484
|
+
echo "Fix: Viewer must only have read permission. Remove create/update/delete mappings."
|
|
1485
|
+
FAIL=true
|
|
1486
|
+
fi
|
|
1487
|
+
|
|
1488
|
+
# Check Manager has NO delete
|
|
1489
|
+
MANAGER_DELETE=$(grep -Pc '(manager|Manager).*(\.delete|Delete)' "$f" 2>/dev/null)
|
|
1490
|
+
if [ "$MANAGER_DELETE" -gt 0 ]; then
|
|
1491
|
+
echo "WARNING: Manager role has delete permission in $f"
|
|
1492
|
+
echo "SmartStack standard: Manager = CRU (no delete). Verify this is intentional."
|
|
1493
|
+
fi
|
|
1494
|
+
done
|
|
1495
|
+
if [ "$FAIL" = true ]; then
|
|
1496
|
+
exit 1
|
|
1497
|
+
fi
|
|
1498
|
+
fi
|
|
1499
|
+
```
|
|
1500
|
+
|
|
1501
|
+
### POST-CHECK 52: PermissionAction enum must use valid typed values only (BLOCKING)
|
|
1502
|
+
|
|
1503
|
+
```bash
|
|
1504
|
+
# Valid PermissionAction enum values: Access(0), Read(1), Create(2), Update(3), Delete(4),
|
|
1505
|
+
# Export(5), Import(6), Approve(7), Reject(8), Assign(9), Execute(10)
|
|
1506
|
+
# FORBIDDEN: Enum.Parse<PermissionAction>("...") — runtime crash if value doesn't exist
|
|
1507
|
+
# FORBIDDEN: (PermissionAction)99 or any cast beyond 0-10
|
|
1508
|
+
SEED_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*.cs" 2>/dev/null)
|
|
1509
|
+
if [ -n "$SEED_FILES" ]; then
|
|
1510
|
+
FAIL=false
|
|
1511
|
+
for f in $SEED_FILES; do
|
|
1512
|
+
# Check for Enum.Parse<PermissionAction> usage
|
|
1513
|
+
ENUM_PARSE=$(grep -Pn 'Enum\.Parse<PermissionAction>' "$f" 2>/dev/null)
|
|
1514
|
+
if [ -n "$ENUM_PARSE" ]; then
|
|
1515
|
+
echo "BLOCKING: Enum.Parse<PermissionAction> detected — runtime crash risk: $f"
|
|
1516
|
+
echo "$ENUM_PARSE"
|
|
1517
|
+
echo "Fix: Use typed enum directly: PermissionAction.Read (NOT Enum.Parse<PermissionAction>(\"Read\"))"
|
|
1518
|
+
FAIL=true
|
|
1519
|
+
fi
|
|
1520
|
+
|
|
1521
|
+
# Check for invalid cast values (PermissionAction)N where N > 10
|
|
1522
|
+
INVALID_CAST=$(grep -Pn '\(PermissionAction\)\s*([1-9]\d{1,}|[2-9]\d)' "$f" 2>/dev/null)
|
|
1523
|
+
if [ -n "$INVALID_CAST" ]; then
|
|
1524
|
+
echo "BLOCKING: Invalid PermissionAction cast detected (value > 10): $f"
|
|
1525
|
+
echo "$INVALID_CAST"
|
|
1526
|
+
echo "Valid values: Access(0), Read(1), Create(2), Update(3), Delete(4), Export(5), Import(6), Approve(7), Reject(8), Assign(9), Execute(10)"
|
|
1527
|
+
FAIL=true
|
|
1528
|
+
fi
|
|
1529
|
+
done
|
|
1530
|
+
if [ "$FAIL" = true ]; then
|
|
1531
|
+
exit 1
|
|
1532
|
+
fi
|
|
1533
|
+
fi
|
|
1534
|
+
```
|
|
1535
|
+
|
|
1536
|
+
### POST-CHECK 53: Navigation translation completeness — 4 languages per level (BLOCKING)
|
|
1537
|
+
|
|
1538
|
+
```bash
|
|
1539
|
+
# Every navigation seed data file must provide translations for ALL 4 languages (fr, en, it, de).
|
|
1540
|
+
# If sections exist (GetSectionEntries), GetSectionTranslationEntries MUST also exist.
|
|
1541
|
+
# If resources exist (GetResourceEntries), resource translation entries MUST also exist.
|
|
1542
|
+
NAV_SEED_FILES=$(find src/ -path "*/Seeding/Data/*" -name "*NavigationSeedData.cs" ! -name "*Application*" 2>/dev/null)
|
|
1543
|
+
if [ -n "$NAV_SEED_FILES" ]; then
|
|
1544
|
+
FAIL=false
|
|
1545
|
+
for f in $NAV_SEED_FILES; do
|
|
1546
|
+
# Check module translations have all 4 languages
|
|
1547
|
+
LANG_COUNT=$(grep -c 'LanguageCode\s*=' "$f" 2>/dev/null)
|
|
1548
|
+
HAS_FR=$(grep -c '"fr"' "$f" 2>/dev/null)
|
|
1549
|
+
HAS_EN=$(grep -c '"en"' "$f" 2>/dev/null)
|
|
1550
|
+
HAS_IT=$(grep -c '"it"' "$f" 2>/dev/null)
|
|
1551
|
+
HAS_DE=$(grep -c '"de"' "$f" 2>/dev/null)
|
|
1552
|
+
|
|
1553
|
+
if [ "$HAS_FR" -eq 0 ] || [ "$HAS_EN" -eq 0 ] || [ "$HAS_IT" -eq 0 ] || [ "$HAS_DE" -eq 0 ]; then
|
|
1554
|
+
echo "BLOCKING: Missing language(s) in navigation translations: $f"
|
|
1555
|
+
echo " fr=$HAS_FR, en=$HAS_EN, it=$HAS_IT, de=$HAS_DE (all must be > 0)"
|
|
1556
|
+
echo "Fix: Add NavigationTranslationSeedEntry for all 4 languages (fr, en, it, de)"
|
|
1557
|
+
FAIL=true
|
|
1558
|
+
fi
|
|
1559
|
+
|
|
1560
|
+
# If sections exist, section translations MUST exist
|
|
1561
|
+
HAS_SECTION_ENTRIES=$(grep -c 'GetSectionEntries' "$f" 2>/dev/null)
|
|
1562
|
+
HAS_SECTION_TRANSLATIONS=$(grep -c 'GetSectionTranslationEntries' "$f" 2>/dev/null)
|
|
1563
|
+
if [ "$HAS_SECTION_ENTRIES" -gt 0 ] && [ "$HAS_SECTION_TRANSLATIONS" -eq 0 ]; then
|
|
1564
|
+
echo "BLOCKING: Sections defined but GetSectionTranslationEntries() missing: $f"
|
|
1565
|
+
echo "Fix: Add GetSectionTranslationEntries() with 4 languages per section (ref core-seed-data.md §2b)"
|
|
1566
|
+
FAIL=true
|
|
1567
|
+
fi
|
|
1568
|
+
|
|
1569
|
+
# If resources exist, resource translations MUST exist
|
|
1570
|
+
HAS_RESOURCE_ENTRIES=$(grep -c 'GetResourceEntries' "$f" 2>/dev/null)
|
|
1571
|
+
HAS_RESOURCE_TRANSLATIONS=$(grep -Pc 'ResourceTranslation|GetResourceTranslation|NavigationEntityType\.Resource.*LanguageCode' "$f" 2>/dev/null)
|
|
1572
|
+
if [ "$HAS_RESOURCE_ENTRIES" -gt 0 ] && [ "$HAS_RESOURCE_TRANSLATIONS" -eq 0 ]; then
|
|
1573
|
+
echo "BLOCKING: Resources defined but resource translations missing: $f"
|
|
1574
|
+
echo "Fix: Add resource translation entries with 4 languages per resource (ref core-seed-data.md §2b)"
|
|
1575
|
+
FAIL=true
|
|
1576
|
+
fi
|
|
1577
|
+
done
|
|
1578
|
+
if [ "$FAIL" = true ]; then
|
|
1579
|
+
exit 1
|
|
1580
|
+
fi
|
|
1581
|
+
fi
|
|
1582
|
+
```
|
|
1583
|
+
|
|
1479
1584
|
**If ANY POST-CHECK fails → fix in step-03, re-validate.**
|
|
@@ -79,7 +79,7 @@ public enum EntityScope
|
|
|
79
79
|
```csharp
|
|
80
80
|
using SmartStack.Domain.Common;
|
|
81
81
|
|
|
82
|
-
namespace {ProjectName}.Domain.Entities.{
|
|
82
|
+
namespace {ProjectName}.Domain.Entities.{App}.{Module};
|
|
83
83
|
|
|
84
84
|
public class {Name} : BaseEntity, ITenantEntity, IAuditableEntity
|
|
85
85
|
{
|
|
@@ -299,7 +299,7 @@ using SmartStack.Application.Common.Interfaces.Identity;
|
|
|
299
299
|
using SmartStack.Application.Common.Interfaces.Tenants;
|
|
300
300
|
using SmartStack.Application.Common.Interfaces.Persistence;
|
|
301
301
|
|
|
302
|
-
namespace {ProjectName}.Infrastructure.Services.{
|
|
302
|
+
namespace {ProjectName}.Infrastructure.Services.{App}.{Module};
|
|
303
303
|
|
|
304
304
|
public class {Name}Service : I{Name}Service
|
|
305
305
|
{
|
|
@@ -432,10 +432,10 @@ using Microsoft.AspNetCore.Mvc;
|
|
|
432
432
|
using SmartStack.Api.Routing;
|
|
433
433
|
using SmartStack.Api.Authorization;
|
|
434
434
|
|
|
435
|
-
namespace {ProjectName}.Api.Controllers.{
|
|
435
|
+
namespace {ProjectName}.Api.Controllers.{App};
|
|
436
436
|
|
|
437
437
|
[ApiController]
|
|
438
|
-
[NavRoute("{
|
|
438
|
+
[NavRoute("{app}.{module}")]
|
|
439
439
|
[Authorize]
|
|
440
440
|
public class {Name}Controller : ControllerBase
|
|
441
441
|
{
|
|
@@ -500,10 +500,10 @@ public class {Name}Controller : ControllerBase
|
|
|
500
500
|
**CRITICAL:** Use `[RequirePermission(Permissions.{Module}.{Action})]` on EVERY endpoint — NEVER `[Authorize]` alone (no RBAC enforcement).
|
|
501
501
|
|
|
502
502
|
**CRITICAL — Permission paths use IDENTICAL segments to NavRoute codes (kebab-case):**
|
|
503
|
-
- NavRoute: `
|
|
504
|
-
- NavRoute: `
|
|
505
|
-
- FORBIDDEN: `
|
|
506
|
-
- SmartStack.app convention: `
|
|
503
|
+
- NavRoute: `human-resources.employees` → Permission: `human-resources.employees.read`
|
|
504
|
+
- NavRoute: `human-resources.employees.leaves` → Permission: `human-resources.employees.leaves.read`
|
|
505
|
+
- FORBIDDEN: `humanresources.employees.read` (no kebab-case — mismatches NavRoute)
|
|
506
|
+
- SmartStack.app convention: `support-client.my-tickets.read` (always kebab-case)
|
|
507
507
|
|
|
508
508
|
### Section-Level Controller (NavRoute with 4 segments)
|
|
509
509
|
|
|
@@ -512,11 +512,11 @@ When a module has sections, each section gets its own controller with a 4-segmen
|
|
|
512
512
|
```csharp
|
|
513
513
|
// Section-level controller: navRoute has 4 segments
|
|
514
514
|
[ApiController]
|
|
515
|
-
[NavRoute("{
|
|
515
|
+
[NavRoute("{app}.{module}.{section}")]
|
|
516
516
|
[Authorize]
|
|
517
517
|
public class {Section}Controller : ControllerBase
|
|
518
518
|
{
|
|
519
|
-
// Example:
|
|
519
|
+
// Example: human-resources.employees.departments
|
|
520
520
|
[HttpGet]
|
|
521
521
|
[RequirePermission(Permissions.{Section}.Read)]
|
|
522
522
|
public async Task<ActionResult<PaginatedResult<{Section}ResponseDto>>> GetAll(
|
|
@@ -531,12 +531,12 @@ public class {Section}Controller : ControllerBase
|
|
|
531
531
|
**NavRoute segment rules:**
|
|
532
532
|
| Level | NavRoute format | Example |
|
|
533
533
|
|-------|----------------|---------|
|
|
534
|
-
| Module | `{
|
|
535
|
-
| Section | `{
|
|
534
|
+
| Module | `{app}.{module}` (2 segments) | `human-resources.employees` |
|
|
535
|
+
| Section | `{app}.{module}.{section}` (3 segments) | `human-resources.employees.departments` |
|
|
536
536
|
|
|
537
537
|
**Namespace:** `SmartStack.Api.Routing` (NOT `SmartStack.Api.Core.Routing`)
|
|
538
538
|
|
|
539
|
-
**NavRoute resolves at startup from DB:** `
|
|
539
|
+
**NavRoute resolves at startup from DB:** `administration.users` → `api/administration/users`
|
|
540
540
|
|
|
541
541
|
### Sub-Resource Pattern (NavRoute Suffix)
|
|
542
542
|
|
|
@@ -545,7 +545,7 @@ When an entity is a child of another entity (e.g., LeaveTypes under Leaves), use
|
|
|
545
545
|
```csharp
|
|
546
546
|
// Sub-resource controller: types are nested under leaves
|
|
547
547
|
[ApiController]
|
|
548
|
-
[NavRoute("
|
|
548
|
+
[NavRoute("human-resources.employees.leaves", Suffix = "types")]
|
|
549
549
|
[Authorize]
|
|
550
550
|
public class LeaveTypesController : ControllerBase
|
|
551
551
|
{
|
|
@@ -582,22 +582,22 @@ public async Task<ActionResult<PaginatedResult<LeaveTypeResponseDto>>> GetAllLea
|
|
|
582
582
|
|
|
583
583
|
| Level | Route Format | Example |
|
|
584
584
|
|-------|-------------|---------|
|
|
585
|
-
| Application | `/{
|
|
586
|
-
| Module | `/{
|
|
587
|
-
| Section | `/{
|
|
588
|
-
| Resource | `/{
|
|
585
|
+
| Application | `/{app-kebab}` | `/human-resources` |
|
|
586
|
+
| Module | `/{app-kebab}/{module-kebab}` | `/human-resources/employees` |
|
|
587
|
+
| Section | `/{app-kebab}/{module-kebab}/{section-kebab}` | `/human-resources/employees/departments` |
|
|
588
|
+
| Resource | `/{app-kebab}/{module-kebab}/{section-kebab}/{resource-kebab}` | `/human-resources/employees/departments/export` |
|
|
589
589
|
|
|
590
590
|
**ROUTE SPECIAL CASES (list and detail sections):**
|
|
591
591
|
> The `list` and `detail` sections are NOT functional sub-areas — they are view modes of the module itself.
|
|
592
592
|
> Their navigation routes MUST NOT add extra segments:
|
|
593
|
-
> - `list` section route = module route (e.g., `/
|
|
594
|
-
> - `detail` section route = module route + `/:id` (e.g., `/
|
|
593
|
+
> - `list` section route = module route (e.g., `/human-resources/employees`)
|
|
594
|
+
> - `detail` section route = module route + `/:id` (e.g., `/human-resources/employees/:id`)
|
|
595
595
|
> - FORBIDDEN: `/employees/list`, `/employees/detail/:id`
|
|
596
596
|
> - Other sections (dashboard, approve, import, etc.) = module route + `/{section-kebab}` (normal behavior)
|
|
597
597
|
|
|
598
598
|
**Rules:**
|
|
599
599
|
- Routes ALWAYS start with `/`
|
|
600
|
-
- Routes ALWAYS include the full hierarchy from
|
|
600
|
+
- Routes ALWAYS include the full hierarchy from application to current level
|
|
601
601
|
- Routes ALWAYS use kebab-case (NOT PascalCase, NOT camelCase)
|
|
602
602
|
- Code identifiers stay PascalCase in C# (`HumanResources`) but routes are kebab-case (`human-resources`)
|
|
603
603
|
|
|
@@ -612,24 +612,14 @@ private static string ToKebabCase(string value)
|
|
|
612
612
|
|
|
613
613
|
### SeedConstants Pattern
|
|
614
614
|
|
|
615
|
-
> **CRITICAL — NavigationContext IDs are NEVER generated.**
|
|
616
|
-
> Contexts (`business`, `platform`, `personal`) are pre-seeded by SmartStack core with hardcoded GUIDs.
|
|
617
|
-
> You MUST query the context by code at runtime in the SeedDataProvider:
|
|
618
|
-
> `var ctx = await db.NavigationContexts.FirstOrDefaultAsync(c => c.Code == "business", ct);`
|
|
619
|
-
> **FORBIDDEN:** `DeterministicGuid("nav:business")` or any ContextId constant in SeedConstants.
|
|
620
|
-
|
|
621
615
|
```csharp
|
|
622
616
|
public static class SeedConstants
|
|
623
617
|
{
|
|
624
618
|
// Deterministic GUIDs (SHA256-based, reproducible across environments)
|
|
625
619
|
// NOTE: Application/Module/Section/Resource IDs are deterministic.
|
|
626
|
-
|
|
627
|
-
public static readonly Guid
|
|
628
|
-
public static readonly Guid
|
|
629
|
-
public static readonly Guid SectionId = DeterministicGuid("nav:business.human-resources.employees.departments");
|
|
630
|
-
|
|
631
|
-
// FORBIDDEN — Context IDs are NOT deterministic, they come from SmartStack core:
|
|
632
|
-
// public static readonly Guid BusinessContextId = DeterministicGuid("nav:business"); // WRONG!
|
|
620
|
+
public static readonly Guid ApplicationId = DeterministicGuid("nav:human-resources");
|
|
621
|
+
public static readonly Guid ModuleId = DeterministicGuid("nav:human-resources.employees");
|
|
622
|
+
public static readonly Guid SectionId = DeterministicGuid("nav:human-resources.employees.departments");
|
|
633
623
|
|
|
634
624
|
private static Guid DeterministicGuid(string input)
|
|
635
625
|
{
|
|
@@ -647,30 +637,25 @@ public static class SeedConstants
|
|
|
647
637
|
### Navigation Seed Data Example
|
|
648
638
|
|
|
649
639
|
```csharp
|
|
650
|
-
//
|
|
651
|
-
var businessCtx = await db.NavigationContexts
|
|
652
|
-
.FirstOrDefaultAsync(c => c.Code == "business", ct);
|
|
653
|
-
if (businessCtx == null) return; // Context not yet seeded by SmartStack core
|
|
654
|
-
|
|
655
|
-
// Application: /business/human-resources
|
|
640
|
+
// Application: /human-resources
|
|
656
641
|
var app = NavigationApplication.Create(
|
|
657
|
-
|
|
642
|
+
"human-resources", "Human Resources", "HR Management",
|
|
658
643
|
"Users", IconType.Lucide,
|
|
659
|
-
"/
|
|
644
|
+
"/human-resources", // FULL PATH — starts with /, kebab-case
|
|
660
645
|
10);
|
|
661
646
|
|
|
662
|
-
// Module: /
|
|
647
|
+
// Module: /human-resources/employees
|
|
663
648
|
var module = NavigationModule.Create(
|
|
664
649
|
app.Id, "employees", "Employees", "Employee management",
|
|
665
650
|
"UserCheck", IconType.Lucide,
|
|
666
|
-
"/
|
|
651
|
+
"/human-resources/employees", // FULL PATH — includes parent
|
|
667
652
|
10);
|
|
668
653
|
|
|
669
|
-
// Section: /
|
|
654
|
+
// Section: /human-resources/employees/departments
|
|
670
655
|
var section = NavigationSection.Create(
|
|
671
656
|
module.Id, "departments", "Departments", "Manage departments",
|
|
672
657
|
"Building2", IconType.Lucide,
|
|
673
|
-
"/
|
|
658
|
+
"/human-resources/employees/departments", // FULL PATH
|
|
674
659
|
10);
|
|
675
660
|
```
|
|
676
661
|
|
|
@@ -678,10 +663,9 @@ var section = NavigationSection.Create(
|
|
|
678
663
|
|
|
679
664
|
| Mistake | Reality |
|
|
680
665
|
|---------|---------|
|
|
681
|
-
| `"humanresources"` as route | Must be `"/
|
|
682
|
-
| `"employees"` as route | Must be `"/
|
|
666
|
+
| `"humanresources"` as route | Must be `"/human-resources"` (full path, kebab-case) |
|
|
667
|
+
| `"employees"` as route | Must be `"/human-resources/employees"` (includes parent) |
|
|
683
668
|
| `Guid.NewGuid()` in seed data | Must use deterministic GUIDs (SHA256) |
|
|
684
|
-
| `DeterministicGuid("nav:business")` for ContextId | Context IDs are pre-seeded by SmartStack core — look up by code |
|
|
685
669
|
| Missing translations | Must have 4 languages: fr, en, it, de |
|
|
686
670
|
| Missing NavigationApplicationSeedData | Menu invisible without Application level |
|
|
687
671
|
|
|
@@ -747,9 +731,9 @@ services.AddValidatorsFromAssemblyContaining<Create{Name}DtoValidator>();
|
|
|
747
731
|
| `ICurrentUser` in service code | Does NOT exist — use `ICurrentUserService` + `ICurrentTenantService` |
|
|
748
732
|
| `_currentTenant.TenantId!.Value` | Crashes with 500 — use `?? throw new TenantContextRequiredException()` |
|
|
749
733
|
| `UnauthorizedAccessException("Tenant context is required")` | Returns 401 → clears frontend token. Use `TenantContextRequiredException()` (400) |
|
|
750
|
-
| Route `"humanresources"` in seed data | Must be full path `"/
|
|
734
|
+
| Route `"humanresources"` in seed data | Must be full path `"/human-resources"` |
|
|
751
735
|
| Route without leading `/` | All routes must start with `/` |
|
|
752
|
-
| `
|
|
736
|
+
| `humanresources.employees.read` in permissions | Permission segments MUST match NavRoute kebab-case: `human-resources.employees.read` |
|
|
753
737
|
| `Permission.Create()` | Does NOT exist — use `CreateForModule()`, `CreateForSection()`, etc. |
|
|
754
738
|
| `GetAllAsync()` without search param | ALL GetAll endpoints MUST support `?search=` for EntityLookup |
|
|
755
739
|
| `string Date` in DTO | Date-only fields MUST use `DateOnly`, NEVER `string` |
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
```tsx
|
|
16
16
|
// Named exports — use .then() to wrap
|
|
17
17
|
const EmployeesPage = lazy(() =>
|
|
18
|
-
import('@/pages/
|
|
18
|
+
import('@/pages/HumanResources/Employees/EmployeesPage')
|
|
19
19
|
.then(m => ({ default: m.EmployeesPage }))
|
|
20
20
|
);
|
|
21
21
|
|
|
@@ -32,7 +32,7 @@ import { PageLoader } from '@/components/ui/PageLoader';
|
|
|
32
32
|
// Route element wrapping
|
|
33
33
|
element: (
|
|
34
34
|
<Suspense fallback={<PageLoader />}>
|
|
35
|
-
<PermissionGuard permissions={ROUTES['
|
|
35
|
+
<PermissionGuard permissions={ROUTES['hr.employees'].permissions}>
|
|
36
36
|
<EmployeesPage />
|
|
37
37
|
</PermissionGuard>
|
|
38
38
|
</Suspense>
|
|
@@ -49,7 +49,7 @@ element: (
|
|
|
49
49
|
**FORBIDDEN:**
|
|
50
50
|
```tsx
|
|
51
51
|
// WRONG: static import in route file
|
|
52
|
-
import { EmployeesPage } from '@/pages/
|
|
52
|
+
import { EmployeesPage } from '@/pages/HumanResources/Employees/EmployeesPage';
|
|
53
53
|
|
|
54
54
|
// WRONG: no Suspense wrapper
|
|
55
55
|
element: <EmployeesPage />
|
|
@@ -65,7 +65,7 @@ element: <EmployeesPage />
|
|
|
65
65
|
**CORRECT — Lazy imports in client App.tsx:**
|
|
66
66
|
```tsx
|
|
67
67
|
const ClientsListPage = lazy(() =>
|
|
68
|
-
import('@/pages/
|
|
68
|
+
import('@/pages/HumanResources/Clients/ClientsListPage')
|
|
69
69
|
.then(m => ({ default: m.ClientsListPage }))
|
|
70
70
|
);
|
|
71
71
|
```
|
|
@@ -73,7 +73,7 @@ const ClientsListPage = lazy(() =>
|
|
|
73
73
|
**FORBIDDEN — Static imports in client App.tsx:**
|
|
74
74
|
```tsx
|
|
75
75
|
// WRONG: Static import kills code splitting
|
|
76
|
-
import { ClientsListPage } from '@/pages/
|
|
76
|
+
import { ClientsListPage } from '@/pages/HumanResources/Clients/ClientsListPage';
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
> **Note:** The `smartstackRoutes.tsx` from the npm package may use static imports internally — this is acceptable for the package. But client `App.tsx` code MUST always use lazy imports for business pages.
|
|
@@ -425,8 +425,8 @@ const handleTabClick = (tab: TabKey) => {
|
|
|
425
425
|
|
|
426
426
|
| Action | Route pattern | Page component | File location |
|
|
427
427
|
|--------|--------------|----------------|---------------|
|
|
428
|
-
| Create | `/{module}/create` | `EntityCreatePage` | `src/pages/{
|
|
429
|
-
| Edit | `/{module}/:id/edit` | `EntityEditPage` | `src/pages/{
|
|
428
|
+
| Create | `/{module}/create` | `EntityCreatePage` | `src/pages/{App}/{Module}/EntityCreatePage.tsx` |
|
|
429
|
+
| Edit | `/{module}/:id/edit` | `EntityEditPage` | `src/pages/{App}/{Module}/EntityEditPage.tsx` |
|
|
430
430
|
|
|
431
431
|
### Create Page Template
|
|
432
432
|
|
|
@@ -562,11 +562,11 @@ export function EntityEditPage() {
|
|
|
562
562
|
```tsx
|
|
563
563
|
// In route files — form pages are also lazy-loaded
|
|
564
564
|
const EntityCreatePage = lazy(() =>
|
|
565
|
-
import('@/pages/
|
|
565
|
+
import('@/pages/HumanResources/Employees/EntityCreatePage')
|
|
566
566
|
.then(m => ({ default: m.EntityCreatePage }))
|
|
567
567
|
);
|
|
568
568
|
const EntityEditPage = lazy(() =>
|
|
569
|
-
import('@/pages/
|
|
569
|
+
import('@/pages/HumanResources/Employees/EntityEditPage')
|
|
570
570
|
.then(m => ({ default: m.EntityEditPage }))
|
|
571
571
|
);
|
|
572
572
|
|
|
@@ -610,7 +610,7 @@ const EntityEditPage = lazy(() =>
|
|
|
610
610
|
// PermissionGuard for section-level routes
|
|
611
611
|
element: (
|
|
612
612
|
<Suspense fallback={<PageLoader />}>
|
|
613
|
-
<PermissionGuard permissions={ROUTES['
|
|
613
|
+
<PermissionGuard permissions={ROUTES['app.module.section'].permissions}>
|
|
614
614
|
<SectionPage />
|
|
615
615
|
</PermissionGuard>
|
|
616
616
|
</Suspense>
|
|
@@ -742,7 +742,7 @@ interface EntityLookupOption {
|
|
|
742
742
|
}
|
|
743
743
|
|
|
744
744
|
interface EntityLookupProps {
|
|
745
|
-
/** API endpoint to search entities (e.g., '/api/
|
|
745
|
+
/** API endpoint to search entities (e.g., '/api/human-resources/employees') */
|
|
746
746
|
apiEndpoint: string;
|
|
747
747
|
/** Currently selected entity ID */
|
|
748
748
|
value: string | null;
|
|
@@ -948,7 +948,7 @@ import { EntityLookup } from '@/components/ui/EntityLookup';
|
|
|
948
948
|
|
|
949
949
|
// Inside the form:
|
|
950
950
|
<EntityLookup
|
|
951
|
-
apiEndpoint="/api/
|
|
951
|
+
apiEndpoint="/api/human-resources/employees"
|
|
952
952
|
value={formData.employeeId}
|
|
953
953
|
onChange={(id) => handleChange('employeeId', id)}
|
|
954
954
|
label={t('module:form.employee', 'Employee')}
|
|
@@ -964,7 +964,7 @@ import { EntityLookup } from '@/components/ui/EntityLookup';
|
|
|
964
964
|
|
|
965
965
|
// For DepartmentId FK:
|
|
966
966
|
<EntityLookup
|
|
967
|
-
apiEndpoint="/api/
|
|
967
|
+
apiEndpoint="/api/human-resources/departments"
|
|
968
968
|
value={formData.departmentId}
|
|
969
969
|
onChange={(id) => handleChange('departmentId', id)}
|
|
970
970
|
label={t('module:form.department', 'Department')}
|
|
@@ -1077,7 +1077,7 @@ public async Task<PaginatedResult<EntityResponseDto>> GetAllAsync(
|
|
|
1077
1077
|
**CORRECT — ONLY this pattern:**
|
|
1078
1078
|
```tsx
|
|
1079
1079
|
<EntityLookup
|
|
1080
|
-
apiEndpoint="/api/
|
|
1080
|
+
apiEndpoint="/api/human-resources/departments"
|
|
1081
1081
|
value={formData.departmentId}
|
|
1082
1082
|
onChange={(id) => handleChange('departmentId', id)}
|
|
1083
1083
|
label={t('module:form.department', 'Department')}
|
|
@@ -1156,7 +1156,7 @@ The `DocRenderer` shared component renders all 8 documentation sections (overvie
|
|
|
1156
1156
|
If the automatic route mapping doesn't work for your module, pass a custom URL:
|
|
1157
1157
|
|
|
1158
1158
|
```tsx
|
|
1159
|
-
<DocToggleButton customDocUrl="/docs/
|
|
1159
|
+
<DocToggleButton customDocUrl="/docs/human-resources/employees" />
|
|
1160
1160
|
```
|
|
1161
1161
|
|
|
1162
1162
|
### Rules
|
|
@@ -1181,7 +1181,7 @@ Before marking frontend tasks as complete, verify:
|
|
|
1181
1181
|
- [ ] No hardcoded strings in JSX — all text goes through `t()`
|
|
1182
1182
|
- [ ] CSS uses variables only — no hardcoded Tailwind colors (BLOCKING POST-CHECK 13)
|
|
1183
1183
|
- [ ] Pages follow loading → error → content pattern
|
|
1184
|
-
- [ ] Pages use `src/pages/{
|
|
1184
|
+
- [ ] Pages use `src/pages/{App}/{Module}/` hierarchy
|
|
1185
1185
|
- [ ] API calls use generated hooks or `apiClient` (never raw axios)
|
|
1186
1186
|
- [ ] Components use SmartTable/SmartFilter/EntityCard (never raw HTML tables)
|
|
1187
1187
|
- [ ] **FK fields use `EntityLookup` — ZERO plain text inputs for Guid FK fields**
|
|
@@ -1470,7 +1470,7 @@ And in the module-specific translation files (e.g., `employees.json`):
|
|
|
1470
1470
|
### Test File Convention
|
|
1471
1471
|
|
|
1472
1472
|
```
|
|
1473
|
-
src/pages/{
|
|
1473
|
+
src/pages/{App}/{Module}/
|
|
1474
1474
|
├── EntityCreatePage.tsx
|
|
1475
1475
|
├── EntityCreatePage.test.tsx ← MANDATORY
|
|
1476
1476
|
├── EntityEditPage.tsx
|