@yss123/cage-cli 0.2.0 → 0.3.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/README.md +47 -3
- package/bin/cage.js +5 -0
- package/bin/create-cage-app.js +5 -0
- package/dist/analyze.d.ts +16 -0
- package/dist/analyze.js +18 -2
- package/dist/analyze.js.map +1 -1
- package/dist/baseline.d.ts +8 -0
- package/dist/baseline.js +13 -0
- package/dist/baseline.js.map +1 -1
- package/dist/bootstrap.d.ts +20 -0
- package/dist/bootstrap.js +75 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/budget.d.ts +7 -0
- package/dist/budget.js +49 -5
- package/dist/budget.js.map +1 -1
- package/dist/capabilities.d.ts +38 -0
- package/dist/capabilities.js +133 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/config.d.ts +31 -2
- package/dist/config.js +22 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +14 -0
- package/dist/context.js +35 -2
- package/dist/context.js.map +1 -1
- package/dist/create-prompt.d.ts +7 -0
- package/dist/create-prompt.js +12 -0
- package/dist/create-prompt.js.map +1 -1
- package/dist/create.d.ts +21 -1
- package/dist/create.js +23 -1
- package/dist/create.js.map +1 -1
- package/dist/derive.d.ts +23 -0
- package/dist/derive.js +40 -0
- package/dist/derive.js.map +1 -1
- package/dist/dev.d.ts +6 -0
- package/dist/dev.js +11 -0
- package/dist/dev.js.map +1 -1
- package/dist/doctor.d.ts +98 -0
- package/dist/doctor.js +93 -0
- package/dist/doctor.js.map +1 -0
- package/dist/env.d.ts +6 -0
- package/dist/env.js +11 -0
- package/dist/env.js.map +1 -1
- package/dist/findings.d.ts +27 -0
- package/dist/findings.js +20 -0
- package/dist/findings.js.map +1 -1
- package/dist/gen/contracts.d.ts +28 -0
- package/dist/gen/contracts.js +52 -12
- package/dist/gen/contracts.js.map +1 -1
- package/dist/gen/index.d.ts +26 -0
- package/dist/gen/index.js +69 -1
- package/dist/gen/index.js.map +1 -1
- package/dist/gen/zod-code.d.ts +42 -0
- package/dist/gen/zod-code.js +178 -16
- package/dist/gen/zod-code.js.map +1 -1
- package/dist/graph-approvals.d.ts +60 -0
- package/dist/graph-approvals.js +80 -0
- package/dist/graph-approvals.js.map +1 -0
- package/dist/graph-business.d.ts +135 -0
- package/dist/graph-business.js +404 -0
- package/dist/graph-business.js.map +1 -0
- package/dist/graph-command.d.ts +17 -0
- package/dist/graph-command.js +21 -0
- package/dist/graph-command.js.map +1 -1
- package/dist/graph-model.d.ts +44 -0
- package/dist/graph-model.js +36 -3
- package/dist/graph-model.js.map +1 -1
- package/dist/graph-presentation.d.ts +16 -0
- package/dist/graph-presentation.js +10 -1
- package/dist/graph-presentation.js.map +1 -1
- package/dist/graph-query.d.ts +95 -0
- package/dist/graph-query.js +62 -0
- package/dist/graph-query.js.map +1 -1
- package/dist/graph-relations.d.ts +20 -0
- package/dist/graph-relations.js +34 -4
- package/dist/graph-relations.js.map +1 -1
- package/dist/graph-search.d.ts +35 -0
- package/dist/graph-search.js +36 -0
- package/dist/graph-search.js.map +1 -1
- package/dist/graph-snapshot.d.ts +26 -0
- package/dist/graph-snapshot.js +30 -1
- package/dist/graph-snapshot.js.map +1 -1
- package/dist/graph.d.ts +13 -2
- package/dist/graph.js +59 -4
- package/dist/graph.js.map +1 -1
- package/dist/impact.d.ts +50 -0
- package/dist/impact.js +62 -2
- package/dist/impact.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +12 -0
- package/dist/init.js +15 -0
- package/dist/init.js.map +1 -1
- package/dist/load.d.ts +10 -0
- package/dist/load.js +15 -0
- package/dist/load.js.map +1 -1
- package/dist/main.d.ts +6 -0
- package/dist/main.js +93 -5
- package/dist/main.js.map +1 -1
- package/dist/migrate.d.ts +12 -0
- package/dist/migrate.js +17 -0
- package/dist/migrate.js.map +1 -1
- package/dist/ontology.d.ts +67 -2
- package/dist/ontology.js +89 -2
- package/dist/ontology.js.map +1 -1
- package/dist/process-rules.d.ts +21 -0
- package/dist/process-rules.js +100 -0
- package/dist/process-rules.js.map +1 -0
- package/dist/process.d.ts +32 -0
- package/dist/process.js +32 -0
- package/dist/process.js.map +1 -1
- package/dist/project-structure.d.ts +5 -0
- package/dist/project-structure.js +10 -0
- package/dist/project-structure.js.map +1 -1
- package/dist/registration.d.ts +16 -0
- package/dist/registration.js +56 -0
- package/dist/registration.js.map +1 -0
- package/dist/rules.d.ts +52 -0
- package/dist/rules.js +58 -0
- package/dist/rules.js.map +1 -1
- package/dist/runtime-diagnostics.d.ts +27 -0
- package/dist/runtime-diagnostics.js +60 -0
- package/dist/runtime-diagnostics.js.map +1 -0
- package/dist/snapshots.d.ts +40 -0
- package/dist/snapshots.js +62 -0
- package/dist/snapshots.js.map +1 -0
- package/dist/static-check.d.ts +33 -0
- package/dist/static-check.js +37 -1
- package/dist/static-check.js.map +1 -1
- package/dist/sync.d.ts +11 -0
- package/dist/sync.js +65 -8
- package/dist/sync.js.map +1 -1
- package/dist/upgrade.d.ts +16 -0
- package/dist/upgrade.js +21 -0
- package/dist/upgrade.js.map +1 -1
- package/dist/verify.d.ts +14 -0
- package/dist/verify.js +19 -0
- package/dist/verify.js.map +1 -1
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -1
- package/graph-ui/app.js +321 -27
- package/graph-ui/approval-flow.js +165 -0
- package/graph-ui/business.css +985 -0
- package/graph-ui/business.js +549 -0
- package/graph-ui/flowchart-interaction.js +177 -0
- package/graph-ui/flowchart.js +1059 -0
- package/graph-ui/graph-approvals.js +79 -0
- package/graph-ui/graph-business.js +403 -0
- package/graph-ui/graph-model.js +36 -3
- package/graph-ui/graph-relations.js +34 -4
- package/graph-ui/graph-search.js +36 -0
- package/graph-ui/index.html +26 -12
- package/graph-ui/model.js +28 -0
- package/graph-ui/relations.js +4 -0
- package/graph-ui/structure.js +44 -0
- package/graph-ui/style.css +12 -0
- package/package.json +6 -4
- package/templates/app/.dockerignore +2 -0
- package/templates/app/.env.example +10 -0
- package/templates/app/AGENTS.md +49 -9
- package/templates/app/README.md +4 -1
- package/templates/app/_gitignore +1 -0
- package/templates/app/apps/api/package.json +2 -2
- package/templates/app/apps/api/src/main.ts +5 -1
- package/templates/app/apps/api/src/worker.ts +3 -0
- package/templates/app/apps/web/index.html +1 -0
- package/templates/app/apps/web/package.json +7 -3
- package/templates/app/apps/web/src/app/app.tsx +122 -12
- package/templates/app/apps/web/src/app/styles/global.css +88 -8
- package/templates/app/apps/web/src/app/use-session.ts +43 -0
- package/templates/app/apps/web/src/app/workspace-layout.tsx +58 -19
- package/templates/app/apps/web/src/main.tsx +5 -0
- package/templates/app/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +11 -0
- package/templates/app/apps/web/src/modules/approval/index.ts +4 -0
- package/templates/app/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +20 -1
- package/templates/app/apps/web/src/modules/auth/index.ts +4 -0
- package/templates/app/apps/web/src/modules/auth/pages/login-page.tsx +12 -0
- package/templates/app/apps/web/src/modules/inventory/index.ts +4 -0
- package/templates/app/apps/web/src/modules/inventory/pages/inventory-page.tsx +29 -1
- package/templates/app/apps/web/src/modules/purchase/components/approval-history.tsx +32 -0
- package/templates/app/apps/web/src/modules/purchase/components/order-attachments.tsx +29 -0
- package/templates/app/apps/web/src/modules/purchase/components/purchase-assistant-context.tsx +17 -0
- package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.css +6 -0
- package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.tsx +25 -3
- package/templates/app/apps/web/src/modules/purchase/index.ts +5 -0
- package/templates/app/apps/web/src/modules/purchase/lib/presentation.ts +20 -0
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-intelligence.css +15 -0
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +154 -0
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +52 -22
- package/templates/app/apps/web/src/modules/workspace/index.ts +5 -0
- package/templates/app/apps/web/src/modules/workspace/pages/home-page.tsx +44 -0
- package/templates/app/apps/web/src/shared/hooks/use-command.ts +13 -0
- package/templates/app/apps/web/src/shared/lib/error-message.ts +9 -0
- package/templates/app/apps/web/src/shared/lib/navigation.ts +21 -0
- package/templates/app/apps/web/src/shared/ui/assistant-markdown.tsx +19 -0
- package/templates/app/apps/web/src/shared/ui/assistant-panel.css +134 -0
- package/templates/app/apps/web/src/shared/ui/assistant-panel.tsx +200 -0
- package/templates/app/apps/web/src/shared/ui/assistant-scope.tsx +72 -0
- package/templates/app/apps/web/src/shared/ui/attachment-upload.css +28 -0
- package/templates/app/apps/web/src/shared/ui/attachment-upload.tsx +163 -0
- package/templates/app/apps/web/src/shared/ui/notice.tsx +7 -0
- package/templates/app/apps/web/src/shared/ui/page-heading.tsx +7 -0
- package/templates/app/apps/web/src/shared/ui/page-state.css +47 -0
- package/templates/app/apps/web/src/shared/ui/page-state.tsx +76 -0
- package/templates/app/apps/web/src/shared/ui/require-capability.tsx +34 -0
- package/templates/app/apps/web/vite.config.ts +5 -0
- package/templates/app/cage.config.json +9 -0
- package/templates/app/db/migrations/0002_draft.sql +12 -0
- package/templates/app/db/migrations/0003_draft.sql +8 -0
- package/templates/app/deploy/nginx.conf +52 -0
- package/templates/app/docs/ai/README.md +21 -0
- package/templates/app/docs/ai/architecture.md +26 -0
- package/templates/app/docs/ai/commenting.md +203 -0
- package/templates/app/docs/ai/diagnostics.md +80 -0
- package/templates/app/docs/ai/files.md +93 -0
- package/templates/app/docs/ai/intelligence.md +348 -0
- package/templates/app/docs/ai/ontology-context.md +59 -4
- package/templates/app/docs/ai/primitives.md +33 -0
- package/templates/app/docs/ai/project-structure.md +8 -3
- package/templates/app/docs/ai/purchase-example.md +16 -2
- package/templates/app/docs/ai/recipes.md +9 -1
- package/templates/app/docs/ai/routing.md +86 -0
- package/templates/app/docs/ai/troubleshooting.md +5 -0
- package/templates/app/docs/ai/workflow.md +24 -11
- package/templates/app/docs/errors/CAGE-AI-001.md +61 -0
- package/templates/app/docs/errors/CAGE-APPR-001.md +7 -6
- package/templates/app/docs/errors/CAGE-APPR-002.md +2 -2
- package/templates/app/docs/errors/CAGE-APPR-003.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-004.md +4 -4
- package/templates/app/docs/errors/CAGE-APPR-005.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-006.md +12 -6
- package/templates/app/docs/errors/CAGE-APPR-007.md +3 -3
- package/templates/app/docs/errors/CAGE-APPR-010.md +5 -5
- package/templates/app/docs/errors/CAGE-AUDIT-001.md +53 -0
- package/templates/app/docs/errors/CAGE-AUTH-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BIZ-001.md +51 -0
- package/templates/app/docs/errors/CAGE-BIZ-002.md +49 -0
- package/templates/app/docs/errors/CAGE-BUDGET-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-002.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CMD-002.md +1 -1
- package/templates/app/docs/errors/CAGE-CMD-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-001.md +3 -3
- package/templates/app/docs/errors/CAGE-CONN-002.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-004.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-005.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-006.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-007.md +6 -6
- package/templates/app/docs/errors/CAGE-CONN-008.md +5 -5
- package/templates/app/docs/errors/CAGE-CONN-009.md +6 -6
- package/templates/app/docs/errors/CAGE-CONN-010.md +6 -6
- package/templates/app/docs/errors/CAGE-CONN-011.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-012.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-013.md +49 -0
- package/templates/app/docs/errors/CAGE-CONN-014.md +48 -0
- package/templates/app/docs/errors/CAGE-CONN-015.md +49 -0
- package/templates/app/docs/errors/CAGE-CREATE-001.md +10 -9
- package/templates/app/docs/errors/CAGE-DECL-001.md +2 -2
- package/templates/app/docs/errors/CAGE-DECL-002.md +7 -6
- package/templates/app/docs/errors/CAGE-DECL-003.md +1 -1
- package/templates/app/docs/errors/CAGE-DECL-004.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-001.md +2 -2
- package/templates/app/docs/errors/CAGE-DEP-002.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-003.md +2 -2
- package/templates/app/docs/errors/CAGE-DEP-004.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-005.md +1 -1
- package/templates/app/docs/errors/CAGE-DIAG-001.md +53 -0
- package/templates/app/docs/errors/CAGE-DLQ-001.md +2 -2
- package/templates/app/docs/errors/CAGE-ENV-001.md +4 -4
- package/templates/app/docs/errors/CAGE-ENV-002.md +51 -0
- package/templates/app/docs/errors/CAGE-ENV-003.md +48 -0
- package/templates/app/docs/errors/CAGE-EVENT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-EVENT-002.md +7 -7
- package/templates/app/docs/errors/CAGE-FIELD-001.md +2 -2
- package/templates/app/docs/errors/CAGE-FIELD-002.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-003.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-004.md +1 -1
- package/templates/app/docs/errors/CAGE-FILE-001.md +56 -0
- package/templates/app/docs/errors/CAGE-GEN-001.md +2 -2
- package/templates/app/docs/errors/CAGE-GEN-002.md +1 -1
- package/templates/app/docs/errors/CAGE-GEN-003.md +53 -0
- package/templates/app/docs/errors/CAGE-GRANT-001.md +3 -3
- package/templates/app/docs/errors/CAGE-GRANT-002.md +2 -2
- package/templates/app/docs/errors/CAGE-GRANT-003.md +7 -6
- package/templates/app/docs/errors/CAGE-IMPACT-001.md +2 -2
- package/templates/app/docs/errors/CAGE-IMPACT-002.md +52 -0
- package/templates/app/docs/errors/CAGE-INIT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-003.md +2 -2
- package/templates/app/docs/errors/CAGE-INIT-004.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-005.md +52 -0
- package/templates/app/docs/errors/CAGE-JOB-001.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-002.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-003.md +1 -1
- package/templates/app/docs/errors/CAGE-LINT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-001.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-002.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-005.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-001.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-002.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-003.md +3 -3
- package/templates/app/docs/errors/CAGE-NAME-004.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-005.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-006.md +1 -1
- package/templates/app/docs/errors/CAGE-ORG-001.md +7 -6
- package/templates/app/docs/errors/CAGE-PAIR-002.md +6 -5
- package/templates/app/docs/errors/CAGE-QUERY-001.md +1 -1
- package/templates/app/docs/errors/CAGE-QUERY-002.md +2 -2
- package/templates/app/docs/errors/CAGE-QUERY-003.md +7 -6
- package/templates/app/docs/errors/CAGE-REACT-001.md +3 -3
- package/templates/app/docs/errors/CAGE-REACT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-REACT-003.md +50 -0
- package/templates/app/docs/errors/CAGE-READS-001.md +9 -6
- package/templates/app/docs/errors/CAGE-REG-001.md +13 -12
- package/templates/app/docs/errors/CAGE-REG-002.md +2 -1
- package/templates/app/docs/errors/CAGE-REG-003.md +1 -1
- package/templates/app/docs/errors/CAGE-REL-001.md +14 -14
- package/templates/app/docs/errors/CAGE-ROUTE-001.md +7 -6
- package/templates/app/docs/errors/CAGE-SCOPE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SCOPE-002.md +2 -2
- package/templates/app/docs/errors/CAGE-SECRET-001.md +2 -2
- package/templates/app/docs/errors/CAGE-SECRET-002.md +1 -1
- package/templates/app/docs/errors/CAGE-SENS-001.md +5 -5
- package/templates/app/docs/errors/CAGE-SQL-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SQL-002.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-001.md +3 -3
- package/templates/app/docs/errors/CAGE-STATE-002.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-003.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-004.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-005.md +3 -2
- package/templates/app/docs/errors/CAGE-STATE-010.md +5 -5
- package/templates/app/docs/errors/CAGE-STATE-011.md +5 -5
- package/templates/app/docs/errors/CAGE-STRUCT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-STRUCT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-SYNC-001.md +3 -2
- package/templates/app/docs/errors/CAGE-TEST-001.md +1 -1
- package/templates/app/docs/errors/CAGE-TEST-002.md +50 -0
- package/templates/app/docs/errors/CAGE-TEST-003.md +52 -0
- package/templates/app/docs/errors/CAGE-UNSAFE-001.md +3 -2
- package/templates/app/docs/errors/CAGE-UPGRADE-001.md +2 -2
- package/templates/app/docs/errors/CAGE-WORKER-001.md +48 -0
- package/templates/app/docs/errors/CAGE-WORKER-002.md +50 -0
- package/templates/app/docs/errors/CAGE-WORKER-003.md +48 -0
- package/templates/app/docs/errors/README.md +50 -0
- package/templates/app/package.json +3 -3
- package/templates/app/packages/contracts/package.json +1 -1
- package/templates/app/packages/contracts/src/aggregates/dept/index.ts +10 -10
- package/templates/app/packages/contracts/src/aggregates/inventory/index.ts +5 -5
- package/templates/app/packages/contracts/src/aggregates/inventory-lock/index.ts +4 -4
- package/templates/app/packages/contracts/src/aggregates/purchase-order/index.ts +26 -12
- package/templates/app/packages/contracts/src/aggregates/role/index.ts +15 -15
- package/templates/app/packages/contracts/src/aggregates/user/index.ts +16 -16
- package/templates/app/packages/contracts/src/index.ts +7 -1
- package/templates/app/packages/contracts/src/queries/index.ts +2 -2
- package/templates/app/packages/domain/package.json +5 -4
- package/templates/app/packages/domain/src/index.ts +4 -0
- package/templates/app/packages/domain/src/integrations/erp.ts +15 -0
- package/templates/app/packages/domain/src/integrations/intelligence.ts +20 -0
- package/templates/app/packages/domain/src/modules/inventory/index.ts +1 -0
- package/templates/app/packages/domain/src/modules/inventory/inventory.ts +12 -0
- package/templates/app/packages/domain/src/modules/purchase/approvals.ts +22 -1
- package/templates/app/packages/domain/src/modules/purchase/index.ts +1 -0
- package/templates/app/packages/domain/src/modules/purchase/intelligence.test.ts +62 -0
- package/templates/app/packages/domain/src/modules/purchase/jobs.ts +5 -0
- package/templates/app/packages/domain/src/modules/purchase/purchase-order.test.ts +9 -0
- package/templates/app/packages/domain/src/modules/purchase/purchase-order.ts +70 -0
- package/templates/app/packages/domain/src/modules/purchase/queries.ts +5 -0
- package/templates/app/packages/domain/src/workflows/purchase-inventory.ts +6 -0
- package/templates/app/packages/domain/src/workflows/purchase-process.ts +42 -0
- package/templates/blank/.dockerignore +2 -0
- package/templates/blank/.env.example +10 -0
- package/templates/blank/AGENTS.md +49 -9
- package/templates/blank/README.md +4 -1
- package/templates/blank/_gitignore +1 -0
- package/templates/blank/apps/api/package.json +1 -1
- package/templates/blank/apps/api/src/main.ts +4 -1
- package/templates/blank/apps/api/src/worker.ts +2 -0
- package/templates/blank/apps/web/index.html +1 -0
- package/templates/blank/apps/web/package.json +7 -3
- package/templates/blank/apps/web/src/app/app.tsx +80 -18
- package/templates/blank/apps/web/src/app/styles/global.css +90 -0
- package/templates/blank/apps/web/src/app/use-session.ts +43 -0
- package/templates/blank/apps/web/src/app/workspace-layout.tsx +30 -5
- package/templates/blank/apps/web/src/main.tsx +5 -0
- package/templates/blank/apps/web/src/modules/auth/index.ts +4 -0
- package/templates/blank/apps/web/src/modules/auth/pages/login-page.tsx +18 -1
- package/templates/blank/apps/web/src/modules/directory/index.ts +7 -0
- package/templates/blank/apps/web/src/modules/directory/pages/user-detail-page.tsx +78 -0
- package/templates/blank/apps/web/src/modules/directory/pages/users-page.tsx +93 -0
- package/templates/blank/apps/web/src/modules/workspace/index.ts +4 -0
- package/templates/blank/apps/web/src/modules/workspace/pages/home-page.tsx +17 -0
- package/templates/blank/apps/web/src/shared/lib/error-message.ts +11 -0
- package/templates/blank/apps/web/src/shared/lib/navigation.ts +21 -0
- package/templates/blank/apps/web/src/shared/ui/brand.tsx +10 -2
- package/templates/blank/apps/web/src/shared/ui/page-state.css +47 -0
- package/templates/blank/apps/web/src/shared/ui/page-state.tsx +76 -0
- package/templates/blank/apps/web/src/shared/ui/require-capability.tsx +34 -0
- package/templates/blank/apps/web/vite.config.ts +5 -0
- package/templates/blank/deploy/nginx.conf +52 -0
- package/templates/blank/docs/ai/README.md +21 -0
- package/templates/blank/docs/ai/architecture.md +26 -0
- package/templates/blank/docs/ai/commenting.md +203 -0
- package/templates/blank/docs/ai/diagnostics.md +80 -0
- package/templates/blank/docs/ai/files.md +93 -0
- package/templates/blank/docs/ai/intelligence.md +348 -0
- package/templates/blank/docs/ai/ontology-context.md +59 -4
- package/templates/blank/docs/ai/primitives.md +33 -0
- package/templates/blank/docs/ai/project-structure.md +8 -3
- package/templates/blank/docs/ai/purchase-example.md +16 -2
- package/templates/blank/docs/ai/recipes.md +9 -1
- package/templates/blank/docs/ai/routing.md +86 -0
- package/templates/blank/docs/ai/troubleshooting.md +5 -0
- package/templates/blank/docs/ai/workflow.md +24 -11
- package/templates/blank/docs/errors/CAGE-AI-001.md +61 -0
- package/templates/blank/docs/errors/CAGE-APPR-001.md +7 -6
- package/templates/blank/docs/errors/CAGE-APPR-002.md +2 -2
- package/templates/blank/docs/errors/CAGE-APPR-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-004.md +4 -4
- package/templates/blank/docs/errors/CAGE-APPR-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-006.md +12 -6
- package/templates/blank/docs/errors/CAGE-APPR-007.md +3 -3
- package/templates/blank/docs/errors/CAGE-APPR-010.md +5 -5
- package/templates/blank/docs/errors/CAGE-AUDIT-001.md +53 -0
- package/templates/blank/docs/errors/CAGE-AUTH-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BIZ-001.md +51 -0
- package/templates/blank/docs/errors/CAGE-BIZ-002.md +49 -0
- package/templates/blank/docs/errors/CAGE-BUDGET-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CMD-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-CMD-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-001.md +3 -3
- package/templates/blank/docs/errors/CAGE-CONN-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-006.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-007.md +6 -6
- package/templates/blank/docs/errors/CAGE-CONN-008.md +5 -5
- package/templates/blank/docs/errors/CAGE-CONN-009.md +6 -6
- package/templates/blank/docs/errors/CAGE-CONN-010.md +6 -6
- package/templates/blank/docs/errors/CAGE-CONN-011.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-012.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-013.md +49 -0
- package/templates/blank/docs/errors/CAGE-CONN-014.md +48 -0
- package/templates/blank/docs/errors/CAGE-CONN-015.md +49 -0
- package/templates/blank/docs/errors/CAGE-CREATE-001.md +10 -9
- package/templates/blank/docs/errors/CAGE-DECL-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-DECL-002.md +7 -6
- package/templates/blank/docs/errors/CAGE-DECL-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-DECL-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-DEP-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-003.md +2 -2
- package/templates/blank/docs/errors/CAGE-DEP-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-DIAG-001.md +53 -0
- package/templates/blank/docs/errors/CAGE-DLQ-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-ENV-001.md +4 -4
- package/templates/blank/docs/errors/CAGE-ENV-002.md +51 -0
- package/templates/blank/docs/errors/CAGE-ENV-003.md +48 -0
- package/templates/blank/docs/errors/CAGE-EVENT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-EVENT-002.md +7 -7
- package/templates/blank/docs/errors/CAGE-FIELD-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-FIELD-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-FILE-001.md +56 -0
- package/templates/blank/docs/errors/CAGE-GEN-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-GEN-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-GEN-003.md +53 -0
- package/templates/blank/docs/errors/CAGE-GRANT-001.md +3 -3
- package/templates/blank/docs/errors/CAGE-GRANT-002.md +2 -2
- package/templates/blank/docs/errors/CAGE-GRANT-003.md +7 -6
- package/templates/blank/docs/errors/CAGE-IMPACT-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-IMPACT-002.md +52 -0
- package/templates/blank/docs/errors/CAGE-INIT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-003.md +2 -2
- package/templates/blank/docs/errors/CAGE-INIT-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-005.md +52 -0
- package/templates/blank/docs/errors/CAGE-JOB-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-LINT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-003.md +3 -3
- package/templates/blank/docs/errors/CAGE-NAME-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-006.md +1 -1
- package/templates/blank/docs/errors/CAGE-ORG-001.md +7 -6
- package/templates/blank/docs/errors/CAGE-PAIR-002.md +6 -5
- package/templates/blank/docs/errors/CAGE-QUERY-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-QUERY-002.md +2 -2
- package/templates/blank/docs/errors/CAGE-QUERY-003.md +7 -6
- package/templates/blank/docs/errors/CAGE-REACT-001.md +3 -3
- package/templates/blank/docs/errors/CAGE-REACT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-REACT-003.md +50 -0
- package/templates/blank/docs/errors/CAGE-READS-001.md +9 -6
- package/templates/blank/docs/errors/CAGE-REG-001.md +13 -12
- package/templates/blank/docs/errors/CAGE-REG-002.md +2 -1
- package/templates/blank/docs/errors/CAGE-REG-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-REL-001.md +14 -14
- package/templates/blank/docs/errors/CAGE-ROUTE-001.md +7 -6
- package/templates/blank/docs/errors/CAGE-SCOPE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SCOPE-002.md +2 -2
- package/templates/blank/docs/errors/CAGE-SECRET-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-SECRET-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-SENS-001.md +5 -5
- package/templates/blank/docs/errors/CAGE-SQL-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SQL-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-001.md +3 -3
- package/templates/blank/docs/errors/CAGE-STATE-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-005.md +3 -2
- package/templates/blank/docs/errors/CAGE-STATE-010.md +5 -5
- package/templates/blank/docs/errors/CAGE-STATE-011.md +5 -5
- package/templates/blank/docs/errors/CAGE-STRUCT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-STRUCT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-SYNC-001.md +3 -2
- package/templates/blank/docs/errors/CAGE-TEST-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-TEST-002.md +50 -0
- package/templates/blank/docs/errors/CAGE-TEST-003.md +52 -0
- package/templates/blank/docs/errors/CAGE-UNSAFE-001.md +3 -2
- package/templates/blank/docs/errors/CAGE-UPGRADE-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-WORKER-001.md +48 -0
- package/templates/blank/docs/errors/CAGE-WORKER-002.md +50 -0
- package/templates/blank/docs/errors/CAGE-WORKER-003.md +48 -0
- package/templates/blank/docs/errors/README.md +50 -0
- package/templates/blank/docs/examples/purchase-admin/apps/api/src/main.ts +5 -1
- package/templates/blank/docs/examples/purchase-admin/apps/api/src/worker.ts +3 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/app.tsx +122 -12
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/styles/global.css +88 -8
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/use-session.ts +43 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/workspace-layout.tsx +58 -19
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/main.tsx +5 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +11 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/index.ts +4 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +20 -1
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/index.ts +4 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/pages/login-page.tsx +12 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/index.ts +4 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/pages/inventory-page.tsx +29 -1
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/approval-history.tsx +32 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/order-attachments.tsx +29 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-assistant-context.tsx +17 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.css +6 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.tsx +25 -3
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/index.ts +5 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/lib/presentation.ts +20 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-intelligence.css +15 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +154 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +52 -22
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/workspace/index.ts +5 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/workspace/pages/home-page.tsx +44 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/hooks/use-command.ts +13 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts +9 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/navigation.ts +21 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-markdown.tsx +19 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-panel.css +134 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-panel.tsx +200 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-scope.tsx +72 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/attachment-upload.css +28 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/attachment-upload.tsx +163 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/notice.tsx +7 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-heading.tsx +7 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-state.css +47 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-state.tsx +76 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/require-capability.tsx +34 -0
- package/templates/blank/docs/examples/purchase-admin/cage.config.json +9 -0
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/dept/index.ts +10 -10
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory/index.ts +5 -5
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory-lock/index.ts +4 -4
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/purchase-order/index.ts +26 -12
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/role/index.ts +15 -15
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/user/index.ts +16 -16
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/index.ts +7 -1
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/queries/index.ts +2 -2
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/index.ts +4 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/integrations/erp.ts +15 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/integrations/intelligence.ts +20 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/index.ts +1 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/inventory.ts +12 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts +22 -1
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/index.ts +1 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/intelligence.test.ts +62 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/jobs.ts +5 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.test.ts +9 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.ts +70 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/queries.ts +5 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-inventory.ts +6 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-process.ts +42 -0
- package/templates/blank/package.json +3 -3
- package/templates/blank/packages/contracts/package.json +1 -1
- package/templates/blank/packages/contracts/src/aggregates/dept/index.ts +10 -10
- package/templates/blank/packages/contracts/src/aggregates/role/index.ts +15 -15
- package/templates/blank/packages/contracts/src/aggregates/user/index.ts +16 -16
- package/templates/blank/packages/contracts/src/index.ts +1 -1
- package/templates/blank/packages/contracts/src/queries/index.ts +1 -1
- package/templates/blank/packages/domain/package.json +5 -4
- package/templates/blank/packages/domain/src/index.ts +2 -1
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 空白应用的主题、品牌、登录布局、工作台外壳与成员目录样式,独立于采购模板维护。
|
|
3
|
+
* 颜色和间距供共享 UI 复用;成员详情与空状态只负责呈现真实接口结果或尚未添加模块的状态。
|
|
4
|
+
* 布局样式不授予管理能力,入口显示条件仍由组件与真实授权控制。
|
|
5
|
+
*/
|
|
1
6
|
/* 空白工作台以留白、细线和深绿建立层次;这里不包含演示统计或客户业务样式。 */
|
|
2
7
|
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap');
|
|
3
8
|
|
|
@@ -514,3 +519,88 @@ button {
|
|
|
514
519
|
.session-state .primary {
|
|
515
520
|
min-width: 200px;
|
|
516
521
|
}
|
|
522
|
+
|
|
523
|
+
/* 目录复用真实组织数据,独立列表与详情是业务模块的最小导航参考。 */
|
|
524
|
+
.directory-intro {
|
|
525
|
+
display: flex;
|
|
526
|
+
gap: 16px;
|
|
527
|
+
align-items: center;
|
|
528
|
+
margin: 0 0 24px;
|
|
529
|
+
font-size: 12px;
|
|
530
|
+
color: var(--muted);
|
|
531
|
+
}
|
|
532
|
+
.directory-panel {
|
|
533
|
+
border: 1px solid var(--line);
|
|
534
|
+
border-radius: 7px;
|
|
535
|
+
background: #fdfefb;
|
|
536
|
+
overflow: hidden;
|
|
537
|
+
}
|
|
538
|
+
.directory-panel table {
|
|
539
|
+
width: 100%;
|
|
540
|
+
border-collapse: collapse;
|
|
541
|
+
text-align: left;
|
|
542
|
+
}
|
|
543
|
+
.directory-panel th,
|
|
544
|
+
.directory-panel td {
|
|
545
|
+
padding: 18px 24px;
|
|
546
|
+
border-bottom: 1px solid var(--line);
|
|
547
|
+
font-size: 12px;
|
|
548
|
+
}
|
|
549
|
+
.directory-panel th {
|
|
550
|
+
background: #f1f5eb;
|
|
551
|
+
color: var(--muted);
|
|
552
|
+
font-size: 11px;
|
|
553
|
+
font-weight: 500;
|
|
554
|
+
}
|
|
555
|
+
.record-link {
|
|
556
|
+
color: var(--green);
|
|
557
|
+
text-decoration: underline;
|
|
558
|
+
text-decoration-color: #c6d5b5;
|
|
559
|
+
text-underline-offset: 4px;
|
|
560
|
+
}
|
|
561
|
+
.record-link:hover {
|
|
562
|
+
text-decoration-color: var(--green);
|
|
563
|
+
}
|
|
564
|
+
.directory-pagination {
|
|
565
|
+
display: flex;
|
|
566
|
+
justify-content: space-between;
|
|
567
|
+
align-items: center;
|
|
568
|
+
padding: 20px 24px;
|
|
569
|
+
color: var(--muted);
|
|
570
|
+
font-size: 11px;
|
|
571
|
+
}
|
|
572
|
+
.directory-pagination button {
|
|
573
|
+
padding: 8px 12px;
|
|
574
|
+
margin-left: 8px;
|
|
575
|
+
background: #f1f5eb;
|
|
576
|
+
color: var(--green);
|
|
577
|
+
border: 1px solid var(--line);
|
|
578
|
+
border-radius: 4px;
|
|
579
|
+
}
|
|
580
|
+
.directory-empty {
|
|
581
|
+
text-align: center;
|
|
582
|
+
padding: 48px;
|
|
583
|
+
color: var(--muted);
|
|
584
|
+
font-size: 13px;
|
|
585
|
+
}
|
|
586
|
+
.record-facts {
|
|
587
|
+
display: grid;
|
|
588
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
589
|
+
margin: 0;
|
|
590
|
+
padding: 8px 24px;
|
|
591
|
+
gap: 0 36px;
|
|
592
|
+
}
|
|
593
|
+
.record-facts > div {
|
|
594
|
+
padding: 24px 0;
|
|
595
|
+
border-bottom: 1px solid var(--line);
|
|
596
|
+
}
|
|
597
|
+
.record-facts dt {
|
|
598
|
+
color: var(--muted);
|
|
599
|
+
font-size: 11px;
|
|
600
|
+
margin-bottom: 12px;
|
|
601
|
+
}
|
|
602
|
+
.record-facts dd {
|
|
603
|
+
margin: 0;
|
|
604
|
+
font-size: 14px;
|
|
605
|
+
overflow-wrap: anywhere;
|
|
606
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 应用会话生命周期:首次加载 /me,并把 SDK 的账号退出或失效通知同步到路由状态。
|
|
3
|
+
* 与 URL 路由分离,网络故障不会被误判成未登录;查询缓存不能跨账号继续使用。
|
|
4
|
+
*/
|
|
5
|
+
// 应用会话与 URL 导航分别管理:401 才进入登录,网络故障保留原地址并允许重试。
|
|
6
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
7
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
8
|
+
import { client, loadMe } from '@app/contracts';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 在 QueryClientProvider 内使用。挂载时恢复用户,卸载时解除 SDK 订阅。
|
|
12
|
+
* 返回 session:loading 为恢复中,guest 为未认证,ready 为已加载身份,error 为其他恢复失败。
|
|
13
|
+
* restore 可重试恢复;onLogin/onLogout 由已完成认证动作的页面调用,只更新应用状态。
|
|
14
|
+
* 账号失效时发起取消查询并清空缓存;这里不负责服务端退出操作,也不保证取消已提交的业务命令。
|
|
15
|
+
*/
|
|
16
|
+
export function useSession() {
|
|
17
|
+
const [session, setSession] = useState<'loading' | 'guest' | 'ready' | 'error'>('loading');
|
|
18
|
+
const cache = useQueryClient();
|
|
19
|
+
/**
|
|
20
|
+
* 每次恢复先显示 loading,再按 loadMe 的明确结果分类;只有 UNAUTHENTICATED 进入 guest。
|
|
21
|
+
*/
|
|
22
|
+
const restore = useCallback(async () => {
|
|
23
|
+
setSession('loading');
|
|
24
|
+
const result = await loadMe();
|
|
25
|
+
setSession(result.ok ? 'ready' : result.code === 'UNAUTHENTICATED' ? 'guest' : 'error');
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const unsubscribe = client.subscribe((_snapshot, reason) => {
|
|
30
|
+
if (reason === 'loaded') return;
|
|
31
|
+
// 先取消在途查询,再清除上一账号的缓存;路由守卫保留 URL 并决定登录回跳。
|
|
32
|
+
void cache.cancelQueries();
|
|
33
|
+
cache.clear();
|
|
34
|
+
setSession('guest');
|
|
35
|
+
});
|
|
36
|
+
void restore();
|
|
37
|
+
return () => {
|
|
38
|
+
unsubscribe();
|
|
39
|
+
};
|
|
40
|
+
}, [cache, restore]);
|
|
41
|
+
|
|
42
|
+
return { session, restore, onLogin: () => setSession('ready'), onLogout: () => setSession('guest') };
|
|
43
|
+
}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 已登录空白应用的公共外壳,装配品牌、导航、账号信息、退出登录和子页面 Outlet。
|
|
3
|
+
* 成员目录和系统管理入口依据 User.read 能力显示;该显示判断不代表拥有全部管理操作。
|
|
4
|
+
*/
|
|
1
5
|
// 应用壳负责导航和账号状态;具体页面通过业务模块的公开入口组合。
|
|
2
6
|
import { useState } from 'react';
|
|
7
|
+
import { NavLink, Outlet, useLocation } from 'react-router-dom';
|
|
3
8
|
import { useQueryClient } from '@tanstack/react-query';
|
|
4
9
|
import { client, currentUser, useCan } from '@app/contracts';
|
|
5
10
|
import { errorMessages } from '../shared/lib/error-message';
|
|
6
11
|
import { Brand } from '../shared/ui/brand';
|
|
7
|
-
import { HomePage } from '../modules/workspace';
|
|
8
12
|
|
|
13
|
+
/**
|
|
14
|
+
* 从路由计算当前位置标题,读取当前账号与能力,承载各业务模块页面。
|
|
15
|
+
* onLogout 由 app 提供,退出请求成功并清空缓存后调用;失败在当前页面显示原因。
|
|
16
|
+
*/
|
|
9
17
|
export function WorkspaceLayout({ onLogout }: { onLogout: () => void }) {
|
|
10
18
|
// 能力只决定界面入口是否展示;系统管理接口还会再次校验授权。
|
|
11
19
|
const canManage = useCan('User.read');
|
|
@@ -13,7 +21,19 @@ export function WorkspaceLayout({ onLogout }: { onLogout: () => void }) {
|
|
|
13
21
|
const [error, setError] = useState('');
|
|
14
22
|
const user = currentUser();
|
|
15
23
|
const cache = useQueryClient();
|
|
24
|
+
const location = useLocation();
|
|
25
|
+
const page = location.pathname.startsWith('/directory/users/')
|
|
26
|
+
? '成员详情'
|
|
27
|
+
: location.pathname === '/directory/users'
|
|
28
|
+
? '成员目录'
|
|
29
|
+
: location.pathname === '/workspace'
|
|
30
|
+
? '工作台'
|
|
31
|
+
: '页面';
|
|
16
32
|
|
|
33
|
+
/**
|
|
34
|
+
* 等待 SDK 结束服务端会话,成功才清除查询缓存并通知 app。
|
|
35
|
+
* 失败不伪造已退出状态;按钮在结果返回前保持忙碌。
|
|
36
|
+
*/
|
|
17
37
|
async function logout() {
|
|
18
38
|
setBusy(true);
|
|
19
39
|
const result = await client.logout();
|
|
@@ -29,9 +49,14 @@ export function WorkspaceLayout({ onLogout }: { onLogout: () => void }) {
|
|
|
29
49
|
<aside className="sidebar">
|
|
30
50
|
<Brand />
|
|
31
51
|
<span className="sidebar-label">工作空间</span>
|
|
32
|
-
<
|
|
52
|
+
<NavLink className={({ isActive }) => `nav-item${isActive ? ' active' : ''}`} to="/workspace">
|
|
33
53
|
<span aria-hidden="true">◫</span>工作台<span className="nav-arrow">↗</span>
|
|
34
|
-
</
|
|
54
|
+
</NavLink>
|
|
55
|
+
{canManage && (
|
|
56
|
+
<NavLink className={({ isActive }) => `nav-item${isActive ? ' active' : ''}`} to="/directory/users">
|
|
57
|
+
<span aria-hidden="true">♧</span>成员目录<span className="nav-arrow">↗</span>
|
|
58
|
+
</NavLink>
|
|
59
|
+
)}
|
|
35
60
|
{canManage && (
|
|
36
61
|
<a className="nav-item" href="/admin/">
|
|
37
62
|
<span aria-hidden="true">⚙</span>系统管理<span className="nav-arrow">↗</span>
|
|
@@ -46,7 +71,7 @@ export function WorkspaceLayout({ onLogout }: { onLogout: () => void }) {
|
|
|
46
71
|
<header className="topbar">
|
|
47
72
|
<span>
|
|
48
73
|
工作空间<span className="breadcrumb-divider">/</span>
|
|
49
|
-
<strong
|
|
74
|
+
<strong>{page}</strong>
|
|
50
75
|
</span>
|
|
51
76
|
<div className="account">
|
|
52
77
|
<span className="avatar">{user?.name.slice(0, 1)}</span>
|
|
@@ -62,7 +87,7 @@ export function WorkspaceLayout({ onLogout }: { onLogout: () => void }) {
|
|
|
62
87
|
{error}
|
|
63
88
|
</p>
|
|
64
89
|
)}
|
|
65
|
-
<
|
|
90
|
+
<Outlet />
|
|
66
91
|
</main>
|
|
67
92
|
</div>
|
|
68
93
|
</div>
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 空白业务应用的浏览器启动入口,创建共享 QueryClient 并挂载 app 路由。
|
|
3
|
+
* 新增业务页面使用同一 Provider 内的生成查询 hooks;此处只装配前端,不注册领域或连接数据库。
|
|
4
|
+
* 全局样式由入口加载,会话失效与账号切换时的查询清理由 useSession 负责。
|
|
5
|
+
*/
|
|
1
6
|
// 浏览器入口只负责挂载和全局 Provider。QueryClient 让新增模块可直接使用生成查询 hooks。
|
|
2
7
|
import { createRoot } from 'react-dom/client';
|
|
3
8
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
@@ -1,13 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 空白应用的登录表单,使用客户端完成认证并由 app 决定成功后的导航。
|
|
3
|
+
* SDK 先认证再读取当前用户和能力,两阶段都成功才通知 app;身份读取失败保留在登录页展示。
|
|
4
|
+
* 账号密码仅用于本次提交,不在本组件持久化或输出日志。
|
|
5
|
+
*/
|
|
1
6
|
// 认证模块拥有登录表单;登录后的导航由 app 组合层处理。
|
|
2
7
|
import { useState, type FormEvent } from 'react';
|
|
3
8
|
import { client } from '@app/contracts';
|
|
4
9
|
import { errorMessages } from '../../../shared/lib/error-message';
|
|
5
10
|
import { Brand } from '../../../shared/ui/brand';
|
|
6
11
|
|
|
12
|
+
/**
|
|
13
|
+
* 维护本地忙碌与错误状态,使用统一错误文案;成功调用 onLogin 更新应用会话状态。
|
|
14
|
+
* 页面不直接消费未验证的 returnTo,也不会把网络错误当成登录成功。
|
|
15
|
+
*/
|
|
7
16
|
export function LoginPage({ onLogin }: { onLogin: () => void }) {
|
|
8
17
|
const [busy, setBusy] = useState(false);
|
|
9
18
|
const [error, setError] = useState('');
|
|
10
19
|
|
|
20
|
+
/**
|
|
21
|
+
* 读取表单中的账号密码并等待 client.login;SDK 完成会话协议与用户能力同步。
|
|
22
|
+
* 返回失败结果时保留页面并提示原因,成功后回跳由 app 路由处理。
|
|
23
|
+
* NOT_FOUND 来自认证后读取当前身份的 GET 结果,单独提示服务配置问题,不误报为账号密码错误。
|
|
24
|
+
*/
|
|
11
25
|
async function submit(event: FormEvent<HTMLFormElement>) {
|
|
12
26
|
event.preventDefault();
|
|
13
27
|
const form = new FormData(event.currentTarget);
|
|
@@ -17,7 +31,10 @@ export function LoginPage({ onLogin }: { onLogin: () => void }) {
|
|
|
17
31
|
const result = await client.login(String(form.get('username')), String(form.get('password')));
|
|
18
32
|
setBusy(false);
|
|
19
33
|
if (result.ok) onLogin();
|
|
20
|
-
else
|
|
34
|
+
else if (result.code === 'NOT_FOUND') {
|
|
35
|
+
// login 的返回类型包含 loadMe 的读取错误;先处理该分支,剩余代码仍由 CommonError 映射穷尽检查。
|
|
36
|
+
setError('无法加载当前登录信息,请检查 API 配置或联系管理员。');
|
|
37
|
+
} else setError(errorMessages[result.code]);
|
|
21
38
|
}
|
|
22
39
|
|
|
23
40
|
return (
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 成员详情路由,从地址栏独立校验和加载成员,支持直接访问与刷新。
|
|
3
|
+
* 姓名、联系方式等均来自安全契约结果,敏感字段是否可见由后端决定。
|
|
4
|
+
*/
|
|
5
|
+
import { Link, useParams } from 'react-router-dom';
|
|
6
|
+
import { UserSchema, useUser } from '@app/contracts';
|
|
7
|
+
import { PageState, QueryFailure } from '../../../shared/ui/page-state';
|
|
8
|
+
|
|
9
|
+
/** ID 来自地址栏,详情不依赖“先打开列表”的内存对象,也不自行放宽读侧权限。 */
|
|
10
|
+
/**
|
|
11
|
+
* 校验 URL 中的成员 id,非法时显示地址错误与返回目录入口;通过后才挂载读取组件。
|
|
12
|
+
*/
|
|
13
|
+
export function UserDetailPage() {
|
|
14
|
+
const { id } = useParams();
|
|
15
|
+
const parsed = UserSchema.shape.id.safeParse(id);
|
|
16
|
+
if (!parsed.success)
|
|
17
|
+
return <PageState title="成员地址无效" description="请从成员目录选择有效记录。" to="/directory/users" />;
|
|
18
|
+
return <UserDetail id={parsed.data} />;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 对合法 id 调用 useUser,分别呈现加载、权限/范围/网络错误及真实成员信息。
|
|
23
|
+
* 电话邮箱为空时显示未填写;该展示不能恢复被服务端掩码或裁剪的原始字段。
|
|
24
|
+
*/
|
|
25
|
+
function UserDetail({ id }: { id: string }) {
|
|
26
|
+
const query = useUser(id);
|
|
27
|
+
if (query.isPending)
|
|
28
|
+
return (
|
|
29
|
+
<p className="muted" role="status">
|
|
30
|
+
正在读取成员详情…
|
|
31
|
+
</p>
|
|
32
|
+
);
|
|
33
|
+
if (query.isError)
|
|
34
|
+
return <QueryFailure error={query.error} to="/directory/users" retry={() => void query.refetch()} />;
|
|
35
|
+
const user = query.data;
|
|
36
|
+
return (
|
|
37
|
+
<>
|
|
38
|
+
<div className="page-heading">
|
|
39
|
+
<div>
|
|
40
|
+
<p className="eyebrow">成员详情</p>
|
|
41
|
+
<h1>{user.name}</h1>
|
|
42
|
+
<p className="muted">联系方式按当前账号的权限展示。</p>
|
|
43
|
+
</div>
|
|
44
|
+
<Link className="route-link" to="/directory/users">
|
|
45
|
+
← 返回成员目录
|
|
46
|
+
</Link>
|
|
47
|
+
</div>
|
|
48
|
+
<section className="directory-panel">
|
|
49
|
+
<dl className="record-facts">
|
|
50
|
+
<div>
|
|
51
|
+
<dt>账号</dt>
|
|
52
|
+
<dd>{user.username}</dd>
|
|
53
|
+
</div>
|
|
54
|
+
<div>
|
|
55
|
+
<dt>状态</dt>
|
|
56
|
+
<dd>{user.status === 'active' ? '使用中' : '已停用'}</dd>
|
|
57
|
+
</div>
|
|
58
|
+
<div>
|
|
59
|
+
<dt>电话</dt>
|
|
60
|
+
<dd>{user.phone ?? '未填写'}</dd>
|
|
61
|
+
</div>
|
|
62
|
+
<div>
|
|
63
|
+
<dt>邮箱</dt>
|
|
64
|
+
<dd>{user.email ?? '未填写'}</dd>
|
|
65
|
+
</div>
|
|
66
|
+
<div>
|
|
67
|
+
<dt>成员 ID</dt>
|
|
68
|
+
<dd>{user.id}</dd>
|
|
69
|
+
</div>
|
|
70
|
+
<div>
|
|
71
|
+
<dt>加入时间</dt>
|
|
72
|
+
<dd>{new Date(user.createdAt).toLocaleString('zh-CN')}</dd>
|
|
73
|
+
</div>
|
|
74
|
+
</dl>
|
|
75
|
+
</section>
|
|
76
|
+
</>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 组织成员的只读目录,使用生成的 useUserList 读取当前账号可访问的安全投影。
|
|
3
|
+
* 分页存入 URL,姓名排序、每页二十条;不会直接访问组织基表或承担账号维护。
|
|
4
|
+
*/
|
|
5
|
+
import { Link, useSearchParams } from 'react-router-dom';
|
|
6
|
+
import { useUserList } from '@app/contracts';
|
|
7
|
+
import { QueryFailure } from '../../../shared/ui/page-state';
|
|
8
|
+
|
|
9
|
+
/** 列表来自真实安全接口;分页写入 URL,使刷新、分享和前进后退保留当前页。 */
|
|
10
|
+
/**
|
|
11
|
+
* 从 page 查询参数恢复页码,非法或非正整数按第一页处理。
|
|
12
|
+
* 加载、查询失败与空列表分别展示;列表 total 是当前读取范围的结果,详情链接使用成员 ID。
|
|
13
|
+
*/
|
|
14
|
+
export function UsersPage() {
|
|
15
|
+
const [search, setSearch] = useSearchParams();
|
|
16
|
+
const requested = Number(search.get('page') ?? 1);
|
|
17
|
+
const page = Number.isSafeInteger(requested) && requested > 0 ? requested : 1;
|
|
18
|
+
const query = useUserList({ page, pageSize: 20, sort: 'name' });
|
|
19
|
+
/**
|
|
20
|
+
* 更新页码并保留其他查询参数,第一页移除 page,让浏览器历史承担前进后退。
|
|
21
|
+
*/
|
|
22
|
+
function go(next: number) {
|
|
23
|
+
const params = new URLSearchParams(search);
|
|
24
|
+
next === 1 ? params.delete('page') : params.set('page', String(next));
|
|
25
|
+
setSearch(params);
|
|
26
|
+
}
|
|
27
|
+
if (query.isPending)
|
|
28
|
+
return (
|
|
29
|
+
<p className="muted" role="status">
|
|
30
|
+
正在读取成员目录…
|
|
31
|
+
</p>
|
|
32
|
+
);
|
|
33
|
+
if (query.isError)
|
|
34
|
+
return (
|
|
35
|
+
<QueryFailure
|
|
36
|
+
error={query.error}
|
|
37
|
+
to="/workspace"
|
|
38
|
+
label="返回工作台"
|
|
39
|
+
retry={() => void query.refetch()}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<div className="page-heading">
|
|
45
|
+
<div>
|
|
46
|
+
<p className="eyebrow">团队与协作</p>
|
|
47
|
+
<h1>成员目录</h1>
|
|
48
|
+
<p className="muted">仅显示你有权查看的成员;账号维护请进入系统管理。</p>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
<section className="directory-panel">
|
|
52
|
+
<table>
|
|
53
|
+
<thead>
|
|
54
|
+
<tr>
|
|
55
|
+
<th>成员</th>
|
|
56
|
+
<th>账号</th>
|
|
57
|
+
<th>状态</th>
|
|
58
|
+
<th>查看</th>
|
|
59
|
+
</tr>
|
|
60
|
+
</thead>
|
|
61
|
+
<tbody>
|
|
62
|
+
{query.data.items.map((user) => (
|
|
63
|
+
<tr key={user.id}>
|
|
64
|
+
<td>{user.name}</td>
|
|
65
|
+
<td>{user.username}</td>
|
|
66
|
+
<td>{user.status === 'active' ? '使用中' : '已停用'}</td>
|
|
67
|
+
<td>
|
|
68
|
+
<Link className="record-link" to={`/directory/users/${encodeURIComponent(user.id)}`}>
|
|
69
|
+
成员详情 ↗
|
|
70
|
+
</Link>
|
|
71
|
+
</td>
|
|
72
|
+
</tr>
|
|
73
|
+
))}
|
|
74
|
+
</tbody>
|
|
75
|
+
</table>
|
|
76
|
+
{!query.data.items.length && <p className="directory-empty">当前范围内没有成员。</p>}
|
|
77
|
+
<footer className="directory-pagination">
|
|
78
|
+
<span>
|
|
79
|
+
共 {query.data.total} 人 · 第 {page} 页
|
|
80
|
+
</span>
|
|
81
|
+
<div>
|
|
82
|
+
<button disabled={page === 1} onClick={() => go(page - 1)}>
|
|
83
|
+
上一页
|
|
84
|
+
</button>
|
|
85
|
+
<button disabled={page * 20 >= query.data.total} onClick={() => go(page + 1)}>
|
|
86
|
+
下一页
|
|
87
|
+
</button>
|
|
88
|
+
</div>
|
|
89
|
+
</footer>
|
|
90
|
+
</section>
|
|
91
|
+
</>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 空白业务项目的工作台首页,解释尚未添加业务模块并提供真实成员目录和管理入口。
|
|
3
|
+
* 不注册采购演示、不制造统计;客户业务应建立相邻模块,再经 app 接入导航。
|
|
4
|
+
*/
|
|
1
5
|
// 空白工作台模块:新增业务时建立相邻模块,由 app 导航接入,不在本页堆全部业务。
|
|
2
6
|
import { useCan } from '@app/contracts';
|
|
7
|
+
import { Link } from 'react-router-dom';
|
|
3
8
|
|
|
9
|
+
/**
|
|
10
|
+
* 根据 User.read 的界面能力展示组织相关入口,其余区域显示业务空状态。
|
|
11
|
+
* 拥有读取能力不等于获得全部管理权限;系统管理仍按具体操作检查能力。
|
|
12
|
+
*/
|
|
4
13
|
export function HomePage() {
|
|
5
14
|
const canManage = useCan('User.read');
|
|
6
15
|
return (
|
|
@@ -13,6 +22,14 @@ export function HomePage() {
|
|
|
13
22
|
</div>
|
|
14
23
|
<span className="project-tag">空白业务项目</span>
|
|
15
24
|
</div>
|
|
25
|
+
{canManage && (
|
|
26
|
+
<p className="directory-intro">
|
|
27
|
+
<Link className="record-link" to="/directory/users">
|
|
28
|
+
打开成员目录 ↗
|
|
29
|
+
</Link>
|
|
30
|
+
<span>查看真实组织成员,也可作为新增业务列表与详情页的参考。</span>
|
|
31
|
+
</p>
|
|
32
|
+
)}
|
|
16
33
|
<section className="empty-state" aria-labelledby="empty-title">
|
|
17
34
|
<div className="empty-illustration" aria-hidden="true">
|
|
18
35
|
<div className="sheet sheet-back" />
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 空白应用通用错误码的中文展示表,由登录页和公共外壳共享。
|
|
3
|
+
* 保持与 CommonError 类型覆盖一致;错误文案只供提示,控制流应仍判断客户端 ok/code。
|
|
4
|
+
* 取消和超时由客户端请求生命周期产生,不代表服务端一定没有完成操作,提示用户先确认当前状态。
|
|
5
|
+
*/
|
|
1
6
|
import type { CommonError } from '@app/contracts';
|
|
2
7
|
|
|
3
8
|
// 通用错误提示不依赖具体业务,登录页和应用壳复用它。
|
|
9
|
+
/**
|
|
10
|
+
* 完整映射认证、授权、输入、限流、服务异常、网络、取消和超时错误;新增业务领域错误由其模块单独说明。
|
|
11
|
+
* Record 保留穷尽检查,SDK 增加通用错误码时必须补充文案,不能用空提示或静默默认值掩盖缺项。
|
|
12
|
+
*/
|
|
4
13
|
export const errorMessages: Record<CommonError, string> = {
|
|
5
14
|
UNAUTHENTICATED: '账号或密码不正确,请重新输入。',
|
|
6
15
|
FORBIDDEN: '当前账号无法执行此操作,请联系管理员。',
|
|
@@ -8,4 +17,6 @@ export const errorMessages: Record<CommonError, string> = {
|
|
|
8
17
|
RATE_LIMITED: '操作过于频繁,请稍后重试。',
|
|
9
18
|
INTERNAL: '服务暂时无法处理请求,请稍后重试。',
|
|
10
19
|
NETWORK: '无法连接服务,请检查网络和 API 是否已启动。',
|
|
20
|
+
CANCELLED: '请求已取消,请确认当前页面状态后重试。',
|
|
21
|
+
TIMEOUT: '请求超时,请检查网络并确认操作结果后重试。',
|
|
11
22
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 登录回跳的应用边界:只接受本应用的业务路径,阻止外站及认证/API/静态资源路径回跳。
|
|
3
|
+
* 由路由组合层调用,纯解析 URL 字符串,不发起导航或网络请求。
|
|
4
|
+
*/
|
|
5
|
+
/** 登录回跳只接受本应用页面;固定基址只用于解析,不会发起网络请求。 */
|
|
6
|
+
/**
|
|
7
|
+
* 提取 search 中的 returnTo,使用固定解析基准检查同源路径并排除保留前缀。
|
|
8
|
+
* @param search 登录页查询字符串,可含 URL 编码的 returnTo。
|
|
9
|
+
* @param fallback 缺失、格式不合法或越界时返回的可信业务首页。
|
|
10
|
+
* @returns 可回跳路径,保留其查询参数与 hash;不返回外站 URL。
|
|
11
|
+
*/
|
|
12
|
+
export function loginReturnTo(search: string, fallback: string): string {
|
|
13
|
+
const requested = new URLSearchParams(search).get('returnTo');
|
|
14
|
+
const origin = 'https://cage.local';
|
|
15
|
+
if (!requested?.startsWith('/') || !URL.canParse(requested, origin)) return fallback;
|
|
16
|
+
const url = new URL(requested, origin);
|
|
17
|
+
// 保留查询条件和锚点;阻止外站跳转、登录循环及把服务端入口当成 React 页面。
|
|
18
|
+
if (url.origin !== origin || /^\/(login|admin|api|auth|hooks|assets)(\/|$)/.test(url.pathname))
|
|
19
|
+
return fallback;
|
|
20
|
+
return url.pathname + url.search + url.hash;
|
|
21
|
+
}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 登录页与工作台复用的品牌组件,只负责显示品牌及首页链接。
|
|
3
|
+
* 依赖 Router 的 Link 导航,不读取会话、不决定用户权限,也不导入具体业务模块。
|
|
4
|
+
*/
|
|
1
5
|
// 登录与工作台共用的品牌组件;只呈现,不读取模块或会话。
|
|
6
|
+
import { Link } from 'react-router-dom';
|
|
7
|
+
/**
|
|
8
|
+
* 渲染可访问名称完整的首页链接;未登录时目标页面仍由 app 的会话守卫处理。
|
|
9
|
+
*/
|
|
2
10
|
export function Brand() {
|
|
3
11
|
return (
|
|
4
|
-
<
|
|
12
|
+
<Link className="brand" to="/workspace" aria-label="Cage 工作台首页">
|
|
5
13
|
<span className="brand-mark">C</span>
|
|
6
14
|
<span>
|
|
7
15
|
Cage<span className="brand-dot">.</span>
|
|
8
16
|
</span>
|
|
9
|
-
</
|
|
17
|
+
</Link>
|
|
10
18
|
);
|
|
11
19
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 共享页面状态卡与返回链接的样式,采购和空白模板均由同步流程取得同一版本。
|
|
3
|
+
* 颜色变量由应用主题提供;这里只排版标题、说明和操作,具体错误分类由 PageState/QueryFailure 决定。
|
|
4
|
+
*/
|
|
5
|
+
/* 与两套工作台共享的纸面状态卡;状态清楚可读,链接保持可聚焦和可复制。 */
|
|
6
|
+
.route-state {
|
|
7
|
+
margin: 32px 0;
|
|
8
|
+
padding: 68px 36px;
|
|
9
|
+
border: 1px solid var(--line);
|
|
10
|
+
border-radius: 7px;
|
|
11
|
+
background: #fdfefb;
|
|
12
|
+
text-align: center;
|
|
13
|
+
}
|
|
14
|
+
.route-state-mark {
|
|
15
|
+
display: block;
|
|
16
|
+
margin-bottom: 20px;
|
|
17
|
+
color: #7d9866;
|
|
18
|
+
font:
|
|
19
|
+
italic 42px Georgia,
|
|
20
|
+
serif;
|
|
21
|
+
}
|
|
22
|
+
.route-state h1 {
|
|
23
|
+
font-size: 26px;
|
|
24
|
+
letter-spacing: -0.5px;
|
|
25
|
+
}
|
|
26
|
+
.route-state p {
|
|
27
|
+
margin: 16px 0 26px;
|
|
28
|
+
color: var(--muted);
|
|
29
|
+
font-size: 13px;
|
|
30
|
+
line-height: 1.9;
|
|
31
|
+
}
|
|
32
|
+
.route-link {
|
|
33
|
+
display: inline-flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
padding: 10px 16px;
|
|
36
|
+
border: 1px solid var(--line);
|
|
37
|
+
border-radius: 4px;
|
|
38
|
+
background: #f1f5eb;
|
|
39
|
+
color: var(--green);
|
|
40
|
+
font-size: 12px;
|
|
41
|
+
}
|
|
42
|
+
.route-link:hover {
|
|
43
|
+
background: #e4ecd9;
|
|
44
|
+
}
|
|
45
|
+
.route-state > button {
|
|
46
|
+
margin-left: 12px;
|
|
47
|
+
}
|