@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.
Files changed (607) hide show
  1. package/README.md +47 -3
  2. package/bin/cage.js +5 -0
  3. package/bin/create-cage-app.js +5 -0
  4. package/dist/analyze.d.ts +16 -0
  5. package/dist/analyze.js +18 -2
  6. package/dist/analyze.js.map +1 -1
  7. package/dist/baseline.d.ts +8 -0
  8. package/dist/baseline.js +13 -0
  9. package/dist/baseline.js.map +1 -1
  10. package/dist/bootstrap.d.ts +20 -0
  11. package/dist/bootstrap.js +75 -0
  12. package/dist/bootstrap.js.map +1 -0
  13. package/dist/budget.d.ts +7 -0
  14. package/dist/budget.js +49 -5
  15. package/dist/budget.js.map +1 -1
  16. package/dist/capabilities.d.ts +38 -0
  17. package/dist/capabilities.js +133 -0
  18. package/dist/capabilities.js.map +1 -0
  19. package/dist/config.d.ts +31 -2
  20. package/dist/config.js +22 -0
  21. package/dist/config.js.map +1 -1
  22. package/dist/context.d.ts +14 -0
  23. package/dist/context.js +35 -2
  24. package/dist/context.js.map +1 -1
  25. package/dist/create-prompt.d.ts +7 -0
  26. package/dist/create-prompt.js +12 -0
  27. package/dist/create-prompt.js.map +1 -1
  28. package/dist/create.d.ts +21 -1
  29. package/dist/create.js +23 -1
  30. package/dist/create.js.map +1 -1
  31. package/dist/derive.d.ts +23 -0
  32. package/dist/derive.js +40 -0
  33. package/dist/derive.js.map +1 -1
  34. package/dist/dev.d.ts +6 -0
  35. package/dist/dev.js +11 -0
  36. package/dist/dev.js.map +1 -1
  37. package/dist/doctor.d.ts +98 -0
  38. package/dist/doctor.js +93 -0
  39. package/dist/doctor.js.map +1 -0
  40. package/dist/env.d.ts +6 -0
  41. package/dist/env.js +11 -0
  42. package/dist/env.js.map +1 -1
  43. package/dist/findings.d.ts +27 -0
  44. package/dist/findings.js +20 -0
  45. package/dist/findings.js.map +1 -1
  46. package/dist/gen/contracts.d.ts +28 -0
  47. package/dist/gen/contracts.js +52 -12
  48. package/dist/gen/contracts.js.map +1 -1
  49. package/dist/gen/index.d.ts +26 -0
  50. package/dist/gen/index.js +69 -1
  51. package/dist/gen/index.js.map +1 -1
  52. package/dist/gen/zod-code.d.ts +42 -0
  53. package/dist/gen/zod-code.js +178 -16
  54. package/dist/gen/zod-code.js.map +1 -1
  55. package/dist/graph-approvals.d.ts +60 -0
  56. package/dist/graph-approvals.js +80 -0
  57. package/dist/graph-approvals.js.map +1 -0
  58. package/dist/graph-business.d.ts +135 -0
  59. package/dist/graph-business.js +404 -0
  60. package/dist/graph-business.js.map +1 -0
  61. package/dist/graph-command.d.ts +17 -0
  62. package/dist/graph-command.js +21 -0
  63. package/dist/graph-command.js.map +1 -1
  64. package/dist/graph-model.d.ts +44 -0
  65. package/dist/graph-model.js +36 -3
  66. package/dist/graph-model.js.map +1 -1
  67. package/dist/graph-presentation.d.ts +16 -0
  68. package/dist/graph-presentation.js +10 -1
  69. package/dist/graph-presentation.js.map +1 -1
  70. package/dist/graph-query.d.ts +95 -0
  71. package/dist/graph-query.js +62 -0
  72. package/dist/graph-query.js.map +1 -1
  73. package/dist/graph-relations.d.ts +20 -0
  74. package/dist/graph-relations.js +34 -4
  75. package/dist/graph-relations.js.map +1 -1
  76. package/dist/graph-search.d.ts +35 -0
  77. package/dist/graph-search.js +36 -0
  78. package/dist/graph-search.js.map +1 -1
  79. package/dist/graph-snapshot.d.ts +26 -0
  80. package/dist/graph-snapshot.js +30 -1
  81. package/dist/graph-snapshot.js.map +1 -1
  82. package/dist/graph.d.ts +13 -2
  83. package/dist/graph.js +59 -4
  84. package/dist/graph.js.map +1 -1
  85. package/dist/impact.d.ts +50 -0
  86. package/dist/impact.js +62 -2
  87. package/dist/impact.js.map +1 -1
  88. package/dist/index.d.ts +10 -0
  89. package/dist/index.js +10 -0
  90. package/dist/index.js.map +1 -1
  91. package/dist/init.d.ts +12 -0
  92. package/dist/init.js +15 -0
  93. package/dist/init.js.map +1 -1
  94. package/dist/load.d.ts +10 -0
  95. package/dist/load.js +15 -0
  96. package/dist/load.js.map +1 -1
  97. package/dist/main.d.ts +6 -0
  98. package/dist/main.js +93 -5
  99. package/dist/main.js.map +1 -1
  100. package/dist/migrate.d.ts +12 -0
  101. package/dist/migrate.js +17 -0
  102. package/dist/migrate.js.map +1 -1
  103. package/dist/ontology.d.ts +67 -2
  104. package/dist/ontology.js +89 -2
  105. package/dist/ontology.js.map +1 -1
  106. package/dist/process-rules.d.ts +21 -0
  107. package/dist/process-rules.js +100 -0
  108. package/dist/process-rules.js.map +1 -0
  109. package/dist/process.d.ts +32 -0
  110. package/dist/process.js +32 -0
  111. package/dist/process.js.map +1 -1
  112. package/dist/project-structure.d.ts +5 -0
  113. package/dist/project-structure.js +10 -0
  114. package/dist/project-structure.js.map +1 -1
  115. package/dist/registration.d.ts +16 -0
  116. package/dist/registration.js +56 -0
  117. package/dist/registration.js.map +1 -0
  118. package/dist/rules.d.ts +52 -0
  119. package/dist/rules.js +58 -0
  120. package/dist/rules.js.map +1 -1
  121. package/dist/runtime-diagnostics.d.ts +27 -0
  122. package/dist/runtime-diagnostics.js +60 -0
  123. package/dist/runtime-diagnostics.js.map +1 -0
  124. package/dist/snapshots.d.ts +40 -0
  125. package/dist/snapshots.js +62 -0
  126. package/dist/snapshots.js.map +1 -0
  127. package/dist/static-check.d.ts +33 -0
  128. package/dist/static-check.js +37 -1
  129. package/dist/static-check.js.map +1 -1
  130. package/dist/sync.d.ts +11 -0
  131. package/dist/sync.js +65 -8
  132. package/dist/sync.js.map +1 -1
  133. package/dist/upgrade.d.ts +16 -0
  134. package/dist/upgrade.js +21 -0
  135. package/dist/upgrade.js.map +1 -1
  136. package/dist/verify.d.ts +14 -0
  137. package/dist/verify.js +19 -0
  138. package/dist/verify.js.map +1 -1
  139. package/dist/version.js +5 -0
  140. package/dist/version.js.map +1 -1
  141. package/graph-ui/app.js +321 -27
  142. package/graph-ui/approval-flow.js +165 -0
  143. package/graph-ui/business.css +985 -0
  144. package/graph-ui/business.js +549 -0
  145. package/graph-ui/flowchart-interaction.js +177 -0
  146. package/graph-ui/flowchart.js +1059 -0
  147. package/graph-ui/graph-approvals.js +79 -0
  148. package/graph-ui/graph-business.js +403 -0
  149. package/graph-ui/graph-model.js +36 -3
  150. package/graph-ui/graph-relations.js +34 -4
  151. package/graph-ui/graph-search.js +36 -0
  152. package/graph-ui/index.html +26 -12
  153. package/graph-ui/model.js +28 -0
  154. package/graph-ui/relations.js +4 -0
  155. package/graph-ui/structure.js +44 -0
  156. package/graph-ui/style.css +12 -0
  157. package/package.json +6 -4
  158. package/templates/app/.dockerignore +2 -0
  159. package/templates/app/.env.example +10 -0
  160. package/templates/app/AGENTS.md +49 -9
  161. package/templates/app/README.md +4 -1
  162. package/templates/app/_gitignore +1 -0
  163. package/templates/app/apps/api/package.json +2 -2
  164. package/templates/app/apps/api/src/main.ts +5 -1
  165. package/templates/app/apps/api/src/worker.ts +3 -0
  166. package/templates/app/apps/web/index.html +1 -0
  167. package/templates/app/apps/web/package.json +7 -3
  168. package/templates/app/apps/web/src/app/app.tsx +122 -12
  169. package/templates/app/apps/web/src/app/styles/global.css +88 -8
  170. package/templates/app/apps/web/src/app/use-session.ts +43 -0
  171. package/templates/app/apps/web/src/app/workspace-layout.tsx +58 -19
  172. package/templates/app/apps/web/src/main.tsx +5 -0
  173. package/templates/app/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +11 -0
  174. package/templates/app/apps/web/src/modules/approval/index.ts +4 -0
  175. package/templates/app/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +20 -1
  176. package/templates/app/apps/web/src/modules/auth/index.ts +4 -0
  177. package/templates/app/apps/web/src/modules/auth/pages/login-page.tsx +12 -0
  178. package/templates/app/apps/web/src/modules/inventory/index.ts +4 -0
  179. package/templates/app/apps/web/src/modules/inventory/pages/inventory-page.tsx +29 -1
  180. package/templates/app/apps/web/src/modules/purchase/components/approval-history.tsx +32 -0
  181. package/templates/app/apps/web/src/modules/purchase/components/order-attachments.tsx +29 -0
  182. package/templates/app/apps/web/src/modules/purchase/components/purchase-assistant-context.tsx +17 -0
  183. package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.css +6 -0
  184. package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.tsx +25 -3
  185. package/templates/app/apps/web/src/modules/purchase/index.ts +5 -0
  186. package/templates/app/apps/web/src/modules/purchase/lib/presentation.ts +20 -0
  187. package/templates/app/apps/web/src/modules/purchase/pages/purchase-intelligence.css +15 -0
  188. package/templates/app/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +154 -0
  189. package/templates/app/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +52 -22
  190. package/templates/app/apps/web/src/modules/workspace/index.ts +5 -0
  191. package/templates/app/apps/web/src/modules/workspace/pages/home-page.tsx +44 -0
  192. package/templates/app/apps/web/src/shared/hooks/use-command.ts +13 -0
  193. package/templates/app/apps/web/src/shared/lib/error-message.ts +9 -0
  194. package/templates/app/apps/web/src/shared/lib/navigation.ts +21 -0
  195. package/templates/app/apps/web/src/shared/ui/assistant-markdown.tsx +19 -0
  196. package/templates/app/apps/web/src/shared/ui/assistant-panel.css +134 -0
  197. package/templates/app/apps/web/src/shared/ui/assistant-panel.tsx +200 -0
  198. package/templates/app/apps/web/src/shared/ui/assistant-scope.tsx +72 -0
  199. package/templates/app/apps/web/src/shared/ui/attachment-upload.css +28 -0
  200. package/templates/app/apps/web/src/shared/ui/attachment-upload.tsx +163 -0
  201. package/templates/app/apps/web/src/shared/ui/notice.tsx +7 -0
  202. package/templates/app/apps/web/src/shared/ui/page-heading.tsx +7 -0
  203. package/templates/app/apps/web/src/shared/ui/page-state.css +47 -0
  204. package/templates/app/apps/web/src/shared/ui/page-state.tsx +76 -0
  205. package/templates/app/apps/web/src/shared/ui/require-capability.tsx +34 -0
  206. package/templates/app/apps/web/vite.config.ts +5 -0
  207. package/templates/app/cage.config.json +9 -0
  208. package/templates/app/db/migrations/0002_draft.sql +12 -0
  209. package/templates/app/db/migrations/0003_draft.sql +8 -0
  210. package/templates/app/deploy/nginx.conf +52 -0
  211. package/templates/app/docs/ai/README.md +21 -0
  212. package/templates/app/docs/ai/architecture.md +26 -0
  213. package/templates/app/docs/ai/commenting.md +203 -0
  214. package/templates/app/docs/ai/diagnostics.md +80 -0
  215. package/templates/app/docs/ai/files.md +93 -0
  216. package/templates/app/docs/ai/intelligence.md +348 -0
  217. package/templates/app/docs/ai/ontology-context.md +59 -4
  218. package/templates/app/docs/ai/primitives.md +33 -0
  219. package/templates/app/docs/ai/project-structure.md +8 -3
  220. package/templates/app/docs/ai/purchase-example.md +16 -2
  221. package/templates/app/docs/ai/recipes.md +9 -1
  222. package/templates/app/docs/ai/routing.md +86 -0
  223. package/templates/app/docs/ai/troubleshooting.md +5 -0
  224. package/templates/app/docs/ai/workflow.md +24 -11
  225. package/templates/app/docs/errors/CAGE-AI-001.md +61 -0
  226. package/templates/app/docs/errors/CAGE-APPR-001.md +7 -6
  227. package/templates/app/docs/errors/CAGE-APPR-002.md +2 -2
  228. package/templates/app/docs/errors/CAGE-APPR-003.md +1 -1
  229. package/templates/app/docs/errors/CAGE-APPR-004.md +4 -4
  230. package/templates/app/docs/errors/CAGE-APPR-005.md +1 -1
  231. package/templates/app/docs/errors/CAGE-APPR-006.md +12 -6
  232. package/templates/app/docs/errors/CAGE-APPR-007.md +3 -3
  233. package/templates/app/docs/errors/CAGE-APPR-010.md +5 -5
  234. package/templates/app/docs/errors/CAGE-AUDIT-001.md +53 -0
  235. package/templates/app/docs/errors/CAGE-AUTH-001.md +1 -1
  236. package/templates/app/docs/errors/CAGE-BIZ-001.md +51 -0
  237. package/templates/app/docs/errors/CAGE-BIZ-002.md +49 -0
  238. package/templates/app/docs/errors/CAGE-BUDGET-001.md +1 -1
  239. package/templates/app/docs/errors/CAGE-BUDGET-002.md +1 -1
  240. package/templates/app/docs/errors/CAGE-BUDGET-003.md +1 -1
  241. package/templates/app/docs/errors/CAGE-CMD-002.md +1 -1
  242. package/templates/app/docs/errors/CAGE-CMD-003.md +1 -1
  243. package/templates/app/docs/errors/CAGE-CONN-001.md +3 -3
  244. package/templates/app/docs/errors/CAGE-CONN-002.md +1 -1
  245. package/templates/app/docs/errors/CAGE-CONN-003.md +1 -1
  246. package/templates/app/docs/errors/CAGE-CONN-004.md +1 -1
  247. package/templates/app/docs/errors/CAGE-CONN-005.md +1 -1
  248. package/templates/app/docs/errors/CAGE-CONN-006.md +1 -1
  249. package/templates/app/docs/errors/CAGE-CONN-007.md +6 -6
  250. package/templates/app/docs/errors/CAGE-CONN-008.md +5 -5
  251. package/templates/app/docs/errors/CAGE-CONN-009.md +6 -6
  252. package/templates/app/docs/errors/CAGE-CONN-010.md +6 -6
  253. package/templates/app/docs/errors/CAGE-CONN-011.md +1 -1
  254. package/templates/app/docs/errors/CAGE-CONN-012.md +1 -1
  255. package/templates/app/docs/errors/CAGE-CONN-013.md +49 -0
  256. package/templates/app/docs/errors/CAGE-CONN-014.md +48 -0
  257. package/templates/app/docs/errors/CAGE-CONN-015.md +49 -0
  258. package/templates/app/docs/errors/CAGE-CREATE-001.md +10 -9
  259. package/templates/app/docs/errors/CAGE-DECL-001.md +2 -2
  260. package/templates/app/docs/errors/CAGE-DECL-002.md +7 -6
  261. package/templates/app/docs/errors/CAGE-DECL-003.md +1 -1
  262. package/templates/app/docs/errors/CAGE-DECL-004.md +1 -1
  263. package/templates/app/docs/errors/CAGE-DEP-001.md +2 -2
  264. package/templates/app/docs/errors/CAGE-DEP-002.md +1 -1
  265. package/templates/app/docs/errors/CAGE-DEP-003.md +2 -2
  266. package/templates/app/docs/errors/CAGE-DEP-004.md +1 -1
  267. package/templates/app/docs/errors/CAGE-DEP-005.md +1 -1
  268. package/templates/app/docs/errors/CAGE-DIAG-001.md +53 -0
  269. package/templates/app/docs/errors/CAGE-DLQ-001.md +2 -2
  270. package/templates/app/docs/errors/CAGE-ENV-001.md +4 -4
  271. package/templates/app/docs/errors/CAGE-ENV-002.md +51 -0
  272. package/templates/app/docs/errors/CAGE-ENV-003.md +48 -0
  273. package/templates/app/docs/errors/CAGE-EVENT-001.md +1 -1
  274. package/templates/app/docs/errors/CAGE-EVENT-002.md +7 -7
  275. package/templates/app/docs/errors/CAGE-FIELD-001.md +2 -2
  276. package/templates/app/docs/errors/CAGE-FIELD-002.md +1 -1
  277. package/templates/app/docs/errors/CAGE-FIELD-003.md +1 -1
  278. package/templates/app/docs/errors/CAGE-FIELD-004.md +1 -1
  279. package/templates/app/docs/errors/CAGE-FILE-001.md +56 -0
  280. package/templates/app/docs/errors/CAGE-GEN-001.md +2 -2
  281. package/templates/app/docs/errors/CAGE-GEN-002.md +1 -1
  282. package/templates/app/docs/errors/CAGE-GEN-003.md +53 -0
  283. package/templates/app/docs/errors/CAGE-GRANT-001.md +3 -3
  284. package/templates/app/docs/errors/CAGE-GRANT-002.md +2 -2
  285. package/templates/app/docs/errors/CAGE-GRANT-003.md +7 -6
  286. package/templates/app/docs/errors/CAGE-IMPACT-001.md +2 -2
  287. package/templates/app/docs/errors/CAGE-IMPACT-002.md +52 -0
  288. package/templates/app/docs/errors/CAGE-INIT-001.md +1 -1
  289. package/templates/app/docs/errors/CAGE-INIT-002.md +1 -1
  290. package/templates/app/docs/errors/CAGE-INIT-003.md +2 -2
  291. package/templates/app/docs/errors/CAGE-INIT-004.md +1 -1
  292. package/templates/app/docs/errors/CAGE-INIT-005.md +52 -0
  293. package/templates/app/docs/errors/CAGE-JOB-001.md +1 -1
  294. package/templates/app/docs/errors/CAGE-JOB-002.md +1 -1
  295. package/templates/app/docs/errors/CAGE-JOB-003.md +1 -1
  296. package/templates/app/docs/errors/CAGE-LINT-001.md +1 -1
  297. package/templates/app/docs/errors/CAGE-MIG-001.md +1 -1
  298. package/templates/app/docs/errors/CAGE-MIG-002.md +1 -1
  299. package/templates/app/docs/errors/CAGE-MIG-005.md +1 -1
  300. package/templates/app/docs/errors/CAGE-NAME-001.md +1 -1
  301. package/templates/app/docs/errors/CAGE-NAME-002.md +1 -1
  302. package/templates/app/docs/errors/CAGE-NAME-003.md +3 -3
  303. package/templates/app/docs/errors/CAGE-NAME-004.md +1 -1
  304. package/templates/app/docs/errors/CAGE-NAME-005.md +1 -1
  305. package/templates/app/docs/errors/CAGE-NAME-006.md +1 -1
  306. package/templates/app/docs/errors/CAGE-ORG-001.md +7 -6
  307. package/templates/app/docs/errors/CAGE-PAIR-002.md +6 -5
  308. package/templates/app/docs/errors/CAGE-QUERY-001.md +1 -1
  309. package/templates/app/docs/errors/CAGE-QUERY-002.md +2 -2
  310. package/templates/app/docs/errors/CAGE-QUERY-003.md +7 -6
  311. package/templates/app/docs/errors/CAGE-REACT-001.md +3 -3
  312. package/templates/app/docs/errors/CAGE-REACT-002.md +1 -1
  313. package/templates/app/docs/errors/CAGE-REACT-003.md +50 -0
  314. package/templates/app/docs/errors/CAGE-READS-001.md +9 -6
  315. package/templates/app/docs/errors/CAGE-REG-001.md +13 -12
  316. package/templates/app/docs/errors/CAGE-REG-002.md +2 -1
  317. package/templates/app/docs/errors/CAGE-REG-003.md +1 -1
  318. package/templates/app/docs/errors/CAGE-REL-001.md +14 -14
  319. package/templates/app/docs/errors/CAGE-ROUTE-001.md +7 -6
  320. package/templates/app/docs/errors/CAGE-SCOPE-001.md +1 -1
  321. package/templates/app/docs/errors/CAGE-SCOPE-002.md +2 -2
  322. package/templates/app/docs/errors/CAGE-SECRET-001.md +2 -2
  323. package/templates/app/docs/errors/CAGE-SECRET-002.md +1 -1
  324. package/templates/app/docs/errors/CAGE-SENS-001.md +5 -5
  325. package/templates/app/docs/errors/CAGE-SQL-001.md +1 -1
  326. package/templates/app/docs/errors/CAGE-SQL-002.md +1 -1
  327. package/templates/app/docs/errors/CAGE-STATE-001.md +3 -3
  328. package/templates/app/docs/errors/CAGE-STATE-002.md +1 -1
  329. package/templates/app/docs/errors/CAGE-STATE-003.md +1 -1
  330. package/templates/app/docs/errors/CAGE-STATE-004.md +1 -1
  331. package/templates/app/docs/errors/CAGE-STATE-005.md +3 -2
  332. package/templates/app/docs/errors/CAGE-STATE-010.md +5 -5
  333. package/templates/app/docs/errors/CAGE-STATE-011.md +5 -5
  334. package/templates/app/docs/errors/CAGE-STRUCT-001.md +1 -1
  335. package/templates/app/docs/errors/CAGE-STRUCT-002.md +1 -1
  336. package/templates/app/docs/errors/CAGE-SYNC-001.md +3 -2
  337. package/templates/app/docs/errors/CAGE-TEST-001.md +1 -1
  338. package/templates/app/docs/errors/CAGE-TEST-002.md +50 -0
  339. package/templates/app/docs/errors/CAGE-TEST-003.md +52 -0
  340. package/templates/app/docs/errors/CAGE-UNSAFE-001.md +3 -2
  341. package/templates/app/docs/errors/CAGE-UPGRADE-001.md +2 -2
  342. package/templates/app/docs/errors/CAGE-WORKER-001.md +48 -0
  343. package/templates/app/docs/errors/CAGE-WORKER-002.md +50 -0
  344. package/templates/app/docs/errors/CAGE-WORKER-003.md +48 -0
  345. package/templates/app/docs/errors/README.md +50 -0
  346. package/templates/app/package.json +3 -3
  347. package/templates/app/packages/contracts/package.json +1 -1
  348. package/templates/app/packages/contracts/src/aggregates/dept/index.ts +10 -10
  349. package/templates/app/packages/contracts/src/aggregates/inventory/index.ts +5 -5
  350. package/templates/app/packages/contracts/src/aggregates/inventory-lock/index.ts +4 -4
  351. package/templates/app/packages/contracts/src/aggregates/purchase-order/index.ts +26 -12
  352. package/templates/app/packages/contracts/src/aggregates/role/index.ts +15 -15
  353. package/templates/app/packages/contracts/src/aggregates/user/index.ts +16 -16
  354. package/templates/app/packages/contracts/src/index.ts +7 -1
  355. package/templates/app/packages/contracts/src/queries/index.ts +2 -2
  356. package/templates/app/packages/domain/package.json +5 -4
  357. package/templates/app/packages/domain/src/index.ts +4 -0
  358. package/templates/app/packages/domain/src/integrations/erp.ts +15 -0
  359. package/templates/app/packages/domain/src/integrations/intelligence.ts +20 -0
  360. package/templates/app/packages/domain/src/modules/inventory/index.ts +1 -0
  361. package/templates/app/packages/domain/src/modules/inventory/inventory.ts +12 -0
  362. package/templates/app/packages/domain/src/modules/purchase/approvals.ts +22 -1
  363. package/templates/app/packages/domain/src/modules/purchase/index.ts +1 -0
  364. package/templates/app/packages/domain/src/modules/purchase/intelligence.test.ts +62 -0
  365. package/templates/app/packages/domain/src/modules/purchase/jobs.ts +5 -0
  366. package/templates/app/packages/domain/src/modules/purchase/purchase-order.test.ts +9 -0
  367. package/templates/app/packages/domain/src/modules/purchase/purchase-order.ts +70 -0
  368. package/templates/app/packages/domain/src/modules/purchase/queries.ts +5 -0
  369. package/templates/app/packages/domain/src/workflows/purchase-inventory.ts +6 -0
  370. package/templates/app/packages/domain/src/workflows/purchase-process.ts +42 -0
  371. package/templates/blank/.dockerignore +2 -0
  372. package/templates/blank/.env.example +10 -0
  373. package/templates/blank/AGENTS.md +49 -9
  374. package/templates/blank/README.md +4 -1
  375. package/templates/blank/_gitignore +1 -0
  376. package/templates/blank/apps/api/package.json +1 -1
  377. package/templates/blank/apps/api/src/main.ts +4 -1
  378. package/templates/blank/apps/api/src/worker.ts +2 -0
  379. package/templates/blank/apps/web/index.html +1 -0
  380. package/templates/blank/apps/web/package.json +7 -3
  381. package/templates/blank/apps/web/src/app/app.tsx +80 -18
  382. package/templates/blank/apps/web/src/app/styles/global.css +90 -0
  383. package/templates/blank/apps/web/src/app/use-session.ts +43 -0
  384. package/templates/blank/apps/web/src/app/workspace-layout.tsx +30 -5
  385. package/templates/blank/apps/web/src/main.tsx +5 -0
  386. package/templates/blank/apps/web/src/modules/auth/index.ts +4 -0
  387. package/templates/blank/apps/web/src/modules/auth/pages/login-page.tsx +18 -1
  388. package/templates/blank/apps/web/src/modules/directory/index.ts +7 -0
  389. package/templates/blank/apps/web/src/modules/directory/pages/user-detail-page.tsx +78 -0
  390. package/templates/blank/apps/web/src/modules/directory/pages/users-page.tsx +93 -0
  391. package/templates/blank/apps/web/src/modules/workspace/index.ts +4 -0
  392. package/templates/blank/apps/web/src/modules/workspace/pages/home-page.tsx +17 -0
  393. package/templates/blank/apps/web/src/shared/lib/error-message.ts +11 -0
  394. package/templates/blank/apps/web/src/shared/lib/navigation.ts +21 -0
  395. package/templates/blank/apps/web/src/shared/ui/brand.tsx +10 -2
  396. package/templates/blank/apps/web/src/shared/ui/page-state.css +47 -0
  397. package/templates/blank/apps/web/src/shared/ui/page-state.tsx +76 -0
  398. package/templates/blank/apps/web/src/shared/ui/require-capability.tsx +34 -0
  399. package/templates/blank/apps/web/vite.config.ts +5 -0
  400. package/templates/blank/deploy/nginx.conf +52 -0
  401. package/templates/blank/docs/ai/README.md +21 -0
  402. package/templates/blank/docs/ai/architecture.md +26 -0
  403. package/templates/blank/docs/ai/commenting.md +203 -0
  404. package/templates/blank/docs/ai/diagnostics.md +80 -0
  405. package/templates/blank/docs/ai/files.md +93 -0
  406. package/templates/blank/docs/ai/intelligence.md +348 -0
  407. package/templates/blank/docs/ai/ontology-context.md +59 -4
  408. package/templates/blank/docs/ai/primitives.md +33 -0
  409. package/templates/blank/docs/ai/project-structure.md +8 -3
  410. package/templates/blank/docs/ai/purchase-example.md +16 -2
  411. package/templates/blank/docs/ai/recipes.md +9 -1
  412. package/templates/blank/docs/ai/routing.md +86 -0
  413. package/templates/blank/docs/ai/troubleshooting.md +5 -0
  414. package/templates/blank/docs/ai/workflow.md +24 -11
  415. package/templates/blank/docs/errors/CAGE-AI-001.md +61 -0
  416. package/templates/blank/docs/errors/CAGE-APPR-001.md +7 -6
  417. package/templates/blank/docs/errors/CAGE-APPR-002.md +2 -2
  418. package/templates/blank/docs/errors/CAGE-APPR-003.md +1 -1
  419. package/templates/blank/docs/errors/CAGE-APPR-004.md +4 -4
  420. package/templates/blank/docs/errors/CAGE-APPR-005.md +1 -1
  421. package/templates/blank/docs/errors/CAGE-APPR-006.md +12 -6
  422. package/templates/blank/docs/errors/CAGE-APPR-007.md +3 -3
  423. package/templates/blank/docs/errors/CAGE-APPR-010.md +5 -5
  424. package/templates/blank/docs/errors/CAGE-AUDIT-001.md +53 -0
  425. package/templates/blank/docs/errors/CAGE-AUTH-001.md +1 -1
  426. package/templates/blank/docs/errors/CAGE-BIZ-001.md +51 -0
  427. package/templates/blank/docs/errors/CAGE-BIZ-002.md +49 -0
  428. package/templates/blank/docs/errors/CAGE-BUDGET-001.md +1 -1
  429. package/templates/blank/docs/errors/CAGE-BUDGET-002.md +1 -1
  430. package/templates/blank/docs/errors/CAGE-BUDGET-003.md +1 -1
  431. package/templates/blank/docs/errors/CAGE-CMD-002.md +1 -1
  432. package/templates/blank/docs/errors/CAGE-CMD-003.md +1 -1
  433. package/templates/blank/docs/errors/CAGE-CONN-001.md +3 -3
  434. package/templates/blank/docs/errors/CAGE-CONN-002.md +1 -1
  435. package/templates/blank/docs/errors/CAGE-CONN-003.md +1 -1
  436. package/templates/blank/docs/errors/CAGE-CONN-004.md +1 -1
  437. package/templates/blank/docs/errors/CAGE-CONN-005.md +1 -1
  438. package/templates/blank/docs/errors/CAGE-CONN-006.md +1 -1
  439. package/templates/blank/docs/errors/CAGE-CONN-007.md +6 -6
  440. package/templates/blank/docs/errors/CAGE-CONN-008.md +5 -5
  441. package/templates/blank/docs/errors/CAGE-CONN-009.md +6 -6
  442. package/templates/blank/docs/errors/CAGE-CONN-010.md +6 -6
  443. package/templates/blank/docs/errors/CAGE-CONN-011.md +1 -1
  444. package/templates/blank/docs/errors/CAGE-CONN-012.md +1 -1
  445. package/templates/blank/docs/errors/CAGE-CONN-013.md +49 -0
  446. package/templates/blank/docs/errors/CAGE-CONN-014.md +48 -0
  447. package/templates/blank/docs/errors/CAGE-CONN-015.md +49 -0
  448. package/templates/blank/docs/errors/CAGE-CREATE-001.md +10 -9
  449. package/templates/blank/docs/errors/CAGE-DECL-001.md +2 -2
  450. package/templates/blank/docs/errors/CAGE-DECL-002.md +7 -6
  451. package/templates/blank/docs/errors/CAGE-DECL-003.md +1 -1
  452. package/templates/blank/docs/errors/CAGE-DECL-004.md +1 -1
  453. package/templates/blank/docs/errors/CAGE-DEP-001.md +2 -2
  454. package/templates/blank/docs/errors/CAGE-DEP-002.md +1 -1
  455. package/templates/blank/docs/errors/CAGE-DEP-003.md +2 -2
  456. package/templates/blank/docs/errors/CAGE-DEP-004.md +1 -1
  457. package/templates/blank/docs/errors/CAGE-DEP-005.md +1 -1
  458. package/templates/blank/docs/errors/CAGE-DIAG-001.md +53 -0
  459. package/templates/blank/docs/errors/CAGE-DLQ-001.md +2 -2
  460. package/templates/blank/docs/errors/CAGE-ENV-001.md +4 -4
  461. package/templates/blank/docs/errors/CAGE-ENV-002.md +51 -0
  462. package/templates/blank/docs/errors/CAGE-ENV-003.md +48 -0
  463. package/templates/blank/docs/errors/CAGE-EVENT-001.md +1 -1
  464. package/templates/blank/docs/errors/CAGE-EVENT-002.md +7 -7
  465. package/templates/blank/docs/errors/CAGE-FIELD-001.md +2 -2
  466. package/templates/blank/docs/errors/CAGE-FIELD-002.md +1 -1
  467. package/templates/blank/docs/errors/CAGE-FIELD-003.md +1 -1
  468. package/templates/blank/docs/errors/CAGE-FIELD-004.md +1 -1
  469. package/templates/blank/docs/errors/CAGE-FILE-001.md +56 -0
  470. package/templates/blank/docs/errors/CAGE-GEN-001.md +2 -2
  471. package/templates/blank/docs/errors/CAGE-GEN-002.md +1 -1
  472. package/templates/blank/docs/errors/CAGE-GEN-003.md +53 -0
  473. package/templates/blank/docs/errors/CAGE-GRANT-001.md +3 -3
  474. package/templates/blank/docs/errors/CAGE-GRANT-002.md +2 -2
  475. package/templates/blank/docs/errors/CAGE-GRANT-003.md +7 -6
  476. package/templates/blank/docs/errors/CAGE-IMPACT-001.md +2 -2
  477. package/templates/blank/docs/errors/CAGE-IMPACT-002.md +52 -0
  478. package/templates/blank/docs/errors/CAGE-INIT-001.md +1 -1
  479. package/templates/blank/docs/errors/CAGE-INIT-002.md +1 -1
  480. package/templates/blank/docs/errors/CAGE-INIT-003.md +2 -2
  481. package/templates/blank/docs/errors/CAGE-INIT-004.md +1 -1
  482. package/templates/blank/docs/errors/CAGE-INIT-005.md +52 -0
  483. package/templates/blank/docs/errors/CAGE-JOB-001.md +1 -1
  484. package/templates/blank/docs/errors/CAGE-JOB-002.md +1 -1
  485. package/templates/blank/docs/errors/CAGE-JOB-003.md +1 -1
  486. package/templates/blank/docs/errors/CAGE-LINT-001.md +1 -1
  487. package/templates/blank/docs/errors/CAGE-MIG-001.md +1 -1
  488. package/templates/blank/docs/errors/CAGE-MIG-002.md +1 -1
  489. package/templates/blank/docs/errors/CAGE-MIG-005.md +1 -1
  490. package/templates/blank/docs/errors/CAGE-NAME-001.md +1 -1
  491. package/templates/blank/docs/errors/CAGE-NAME-002.md +1 -1
  492. package/templates/blank/docs/errors/CAGE-NAME-003.md +3 -3
  493. package/templates/blank/docs/errors/CAGE-NAME-004.md +1 -1
  494. package/templates/blank/docs/errors/CAGE-NAME-005.md +1 -1
  495. package/templates/blank/docs/errors/CAGE-NAME-006.md +1 -1
  496. package/templates/blank/docs/errors/CAGE-ORG-001.md +7 -6
  497. package/templates/blank/docs/errors/CAGE-PAIR-002.md +6 -5
  498. package/templates/blank/docs/errors/CAGE-QUERY-001.md +1 -1
  499. package/templates/blank/docs/errors/CAGE-QUERY-002.md +2 -2
  500. package/templates/blank/docs/errors/CAGE-QUERY-003.md +7 -6
  501. package/templates/blank/docs/errors/CAGE-REACT-001.md +3 -3
  502. package/templates/blank/docs/errors/CAGE-REACT-002.md +1 -1
  503. package/templates/blank/docs/errors/CAGE-REACT-003.md +50 -0
  504. package/templates/blank/docs/errors/CAGE-READS-001.md +9 -6
  505. package/templates/blank/docs/errors/CAGE-REG-001.md +13 -12
  506. package/templates/blank/docs/errors/CAGE-REG-002.md +2 -1
  507. package/templates/blank/docs/errors/CAGE-REG-003.md +1 -1
  508. package/templates/blank/docs/errors/CAGE-REL-001.md +14 -14
  509. package/templates/blank/docs/errors/CAGE-ROUTE-001.md +7 -6
  510. package/templates/blank/docs/errors/CAGE-SCOPE-001.md +1 -1
  511. package/templates/blank/docs/errors/CAGE-SCOPE-002.md +2 -2
  512. package/templates/blank/docs/errors/CAGE-SECRET-001.md +2 -2
  513. package/templates/blank/docs/errors/CAGE-SECRET-002.md +1 -1
  514. package/templates/blank/docs/errors/CAGE-SENS-001.md +5 -5
  515. package/templates/blank/docs/errors/CAGE-SQL-001.md +1 -1
  516. package/templates/blank/docs/errors/CAGE-SQL-002.md +1 -1
  517. package/templates/blank/docs/errors/CAGE-STATE-001.md +3 -3
  518. package/templates/blank/docs/errors/CAGE-STATE-002.md +1 -1
  519. package/templates/blank/docs/errors/CAGE-STATE-003.md +1 -1
  520. package/templates/blank/docs/errors/CAGE-STATE-004.md +1 -1
  521. package/templates/blank/docs/errors/CAGE-STATE-005.md +3 -2
  522. package/templates/blank/docs/errors/CAGE-STATE-010.md +5 -5
  523. package/templates/blank/docs/errors/CAGE-STATE-011.md +5 -5
  524. package/templates/blank/docs/errors/CAGE-STRUCT-001.md +1 -1
  525. package/templates/blank/docs/errors/CAGE-STRUCT-002.md +1 -1
  526. package/templates/blank/docs/errors/CAGE-SYNC-001.md +3 -2
  527. package/templates/blank/docs/errors/CAGE-TEST-001.md +1 -1
  528. package/templates/blank/docs/errors/CAGE-TEST-002.md +50 -0
  529. package/templates/blank/docs/errors/CAGE-TEST-003.md +52 -0
  530. package/templates/blank/docs/errors/CAGE-UNSAFE-001.md +3 -2
  531. package/templates/blank/docs/errors/CAGE-UPGRADE-001.md +2 -2
  532. package/templates/blank/docs/errors/CAGE-WORKER-001.md +48 -0
  533. package/templates/blank/docs/errors/CAGE-WORKER-002.md +50 -0
  534. package/templates/blank/docs/errors/CAGE-WORKER-003.md +48 -0
  535. package/templates/blank/docs/errors/README.md +50 -0
  536. package/templates/blank/docs/examples/purchase-admin/apps/api/src/main.ts +5 -1
  537. package/templates/blank/docs/examples/purchase-admin/apps/api/src/worker.ts +3 -0
  538. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/app.tsx +122 -12
  539. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/styles/global.css +88 -8
  540. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/use-session.ts +43 -0
  541. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/workspace-layout.tsx +58 -19
  542. package/templates/blank/docs/examples/purchase-admin/apps/web/src/main.tsx +5 -0
  543. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +11 -0
  544. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/index.ts +4 -0
  545. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +20 -1
  546. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/index.ts +4 -0
  547. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/pages/login-page.tsx +12 -0
  548. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/index.ts +4 -0
  549. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/pages/inventory-page.tsx +29 -1
  550. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/approval-history.tsx +32 -0
  551. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/order-attachments.tsx +29 -0
  552. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-assistant-context.tsx +17 -0
  553. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.css +6 -0
  554. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.tsx +25 -3
  555. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/index.ts +5 -0
  556. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/lib/presentation.ts +20 -0
  557. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-intelligence.css +15 -0
  558. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +154 -0
  559. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +52 -22
  560. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/workspace/index.ts +5 -0
  561. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/workspace/pages/home-page.tsx +44 -0
  562. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/hooks/use-command.ts +13 -0
  563. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts +9 -0
  564. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/navigation.ts +21 -0
  565. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-markdown.tsx +19 -0
  566. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-panel.css +134 -0
  567. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-panel.tsx +200 -0
  568. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-scope.tsx +72 -0
  569. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/attachment-upload.css +28 -0
  570. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/attachment-upload.tsx +163 -0
  571. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/notice.tsx +7 -0
  572. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-heading.tsx +7 -0
  573. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-state.css +47 -0
  574. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-state.tsx +76 -0
  575. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/require-capability.tsx +34 -0
  576. package/templates/blank/docs/examples/purchase-admin/cage.config.json +9 -0
  577. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/dept/index.ts +10 -10
  578. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory/index.ts +5 -5
  579. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory-lock/index.ts +4 -4
  580. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/purchase-order/index.ts +26 -12
  581. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/role/index.ts +15 -15
  582. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/user/index.ts +16 -16
  583. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/index.ts +7 -1
  584. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/queries/index.ts +2 -2
  585. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/index.ts +4 -0
  586. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/integrations/erp.ts +15 -0
  587. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/integrations/intelligence.ts +20 -0
  588. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/index.ts +1 -0
  589. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/inventory.ts +12 -0
  590. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts +22 -1
  591. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/index.ts +1 -0
  592. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/intelligence.test.ts +62 -0
  593. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/jobs.ts +5 -0
  594. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.test.ts +9 -0
  595. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.ts +70 -0
  596. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/queries.ts +5 -0
  597. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-inventory.ts +6 -0
  598. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-process.ts +42 -0
  599. package/templates/blank/package.json +3 -3
  600. package/templates/blank/packages/contracts/package.json +1 -1
  601. package/templates/blank/packages/contracts/src/aggregates/dept/index.ts +10 -10
  602. package/templates/blank/packages/contracts/src/aggregates/role/index.ts +15 -15
  603. package/templates/blank/packages/contracts/src/aggregates/user/index.ts +16 -16
  604. package/templates/blank/packages/contracts/src/index.ts +1 -1
  605. package/templates/blank/packages/contracts/src/queries/index.ts +1 -1
  606. package/templates/blank/packages/domain/package.json +5 -4
  607. package/templates/blank/packages/domain/src/index.ts +2 -1
@@ -1,12 +1,30 @@
1
+ /**
2
+ * 采购草稿创建表单,收集物料、数量、元金额字符串、用途、电话和已上传附件。
3
+ * 创建人、部门和初始状态由领域及内核确定;浏览器表单限制只承担输入体验。
4
+ */
1
5
  // 只处理采购草稿输入;状态迁移、创建人和权限仍由领域命令决定。
2
6
  import { useState, type FormEvent } from 'react';
3
- import { purchaseOrder } from '@app/contracts';
7
+ import { purchaseOrder, PurchaseOrderFileFields } from '@app/contracts';
4
8
  import type { RunCommand } from '../../../shared/hooks/use-command';
9
+ import { AttachmentUpload } from '../../../shared/ui/attachment-upload';
10
+ import './purchase-order-form.css';
5
11
 
12
+ /**
13
+ * 在弹窗中维护附件列表、上传状态和提交状态。
14
+ * close 由列表控制弹窗关闭;run 使用 useCommand 协议返回成功布尔值,失败保留输入。
15
+ * 上传中的文件不能随表单提交,上传完成仍需 create 命令正式绑定。
16
+ */
6
17
  export function PurchaseOrderForm({ close, run }: { close: () => void; run: RunCommand }) {
7
18
  const [busy, setBusy] = useState(false);
19
+ const [attachments, setAttachments] = useState<string[]>([]);
20
+ const [uploading, setUploading] = useState(false);
21
+ /**
22
+ * 读取 FormData 并构造创建输入:金额保留字符串,数量转数值,可选空文本转 undefined。
23
+ * 等待 run 返回后解除提交态,只有成功才关闭;依赖 run 返回结果,不额外捕获任意抛出异常。
24
+ */
8
25
  async function submit(event: FormEvent<HTMLFormElement>) {
9
26
  event.preventDefault();
27
+ if (uploading || busy) return;
10
28
  setBusy(true);
11
29
  const data = new FormData(event.currentTarget);
12
30
  const ok = await run(
@@ -14,7 +32,9 @@ export function PurchaseOrderForm({ close, run }: { close: () => void; run: RunC
14
32
  purchaseOrder.create({
15
33
  amount: String(data.get('amount')),
16
34
  supplierPhone: String(data.get('phone')) || undefined,
35
+ reason: String(data.get('reason')) || undefined,
17
36
  items: [{ sku: String(data.get('sku')), qty: Number(data.get('qty')) }],
37
+ attachments,
18
38
  }),
19
39
  '采购单已保存为草稿',
20
40
  );
@@ -23,7 +43,7 @@ export function PurchaseOrderForm({ close, run }: { close: () => void; run: RunC
23
43
  }
24
44
  return (
25
45
  <div className="modal-backdrop">
26
- <section role="dialog" aria-modal="true" aria-labelledby="new-order-title" className="modal">
46
+ <section role="dialog" aria-modal="true" aria-labelledby="new-order-title" className="modal purchase-order-dialog">
27
47
  <div className="section-head">
28
48
  <h2 id="new-order-title">新建采购单</h2>
29
49
  <button onClick={close} aria-label="关闭">
@@ -50,7 +70,9 @@ export function PurchaseOrderForm({ close, run }: { close: () => void; run: RunC
50
70
  <input name="phone" type="tel" maxLength={32} />
51
71
  </label>
52
72
  <p className="muted">保存后可在列表提交审批,创建人和部门会自动填写。</p>
53
- <button className="primary" disabled={busy}>
73
+ <label>采购用途与必要性<textarea name="reason" rows={3} maxLength={2000} placeholder="说明具体用途;提交后先进行 AI 材料预审" /></label>
74
+ <AttachmentUpload command="PurchaseOrder.create" field="attachments" policy={PurchaseOrderFileFields.attachments} value={attachments} onChange={setAttachments} onBusyChange={setUploading} />
75
+ <button className="primary" disabled={busy || uploading}>
54
76
  {busy ? '保存中…' : '保存草稿'}
55
77
  </button>
56
78
  </form>
@@ -1,2 +1,7 @@
1
+ /**
2
+ * purchase 模块的公开入口,对 app 暴露采购列表和独立详情页。
3
+ * 外部组合通过本入口导入,页面、组件与内部 helper 的组织由模块自行维护。
4
+ */
1
5
  // 模块公开入口:应用壳只需要页面,不依赖内部表单与本地状态。
2
6
  export { PurchaseOrdersPage } from './pages/purchase-orders-page';
7
+ export { PurchaseOrderDetailPage } from './pages/purchase-order-detail-page';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 采购页面共享的展示映射与金额格式;不承载状态迁移、权限或精确金额计算。
3
+ */
4
+ // 同一业务的列表与详情共用展示口径;不把采购状态塞入全局 shared。
5
+ /**
6
+ * 采购记录状态到中文名称的界面映射,状态合法性与迁移规则仍以领域声明为准。
7
+ */
8
+ export const statusLabels: Record<string, string> = {
9
+ draft: '草稿',
10
+ pending_approval: '待审批',
11
+ approved: '已通过',
12
+ received: '已收货',
13
+ rejected: '已驳回',
14
+ cancelled: '已取消',
15
+ };
16
+ /**
17
+ * 把以元表示的金额字符串转为人民币格式显示;Number 转换只用于展示,不能用于账务计算。
18
+ */
19
+ export const currency = (value: string) =>
20
+ new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }).format(Number(value));
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 采购详情的用途编辑区和审批轮次轨迹样式;实际审批状态来自 ApprovalHistory 的接口结果。
3
+ * 纵向步骤线表示展示层次,颜色不产生通过/退回判断;文本框允许用户按内容调整高度。
4
+ */
5
+ .purchase-detail-content { min-width: 0; }
6
+ .purchase-detail-content .panel { margin-bottom: 22px; }
7
+ .purpose-editor { border-top: 1px solid var(--line, #e4e8df); padding: 20px 0; }
8
+ .purpose-editor > label { display: block; font-weight: 600; margin-bottom: 12px; }
9
+ .purpose-editor textarea { width: 100%; box-sizing: border-box; padding: 12px; border: 1px solid #d9e0d8; border-radius: 8px; font: inherit; resize: vertical; }
10
+ .purpose-editor .actions { margin-top: 12px; justify-content: space-between; }
11
+ .review-round { margin-top: 20px; }
12
+ .review-round h3 { font-size: 14px; color: #36594d; }
13
+ .review-step { margin: 14px 0; border-left: 2px solid #bbd0bd; padding: 4px 0 4px 18px; font-size: 13px; line-height: 1.8; }
14
+ .review-step p { margin: 6px 0; }
15
+ .review-suggestion { color: #956128; }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * 采购详情路由,直接从地址栏校验单据 ID 并独立读取,支持刷新与深链接。
3
+ * 详情由真实契约数据构建,附件、审批轨迹和助理上下文通过同模块组件组合。
4
+ */
5
+ // 详情通过 URL 中的记录 ID 重新读取安全接口;刷新或新标签页不依赖列表的内存状态。
6
+ import { Link, useParams } from 'react-router-dom';
7
+ import { useEffect, useState } from 'react';
8
+ import { can, PurchaseOrderSchema, purchaseOrder, usePurchaseOrder } from '@app/contracts';
9
+ import { useCommand } from '../../../shared/hooks/use-command';
10
+ import { PageState, QueryFailure } from '../../../shared/ui/page-state';
11
+ import { PageHeading } from '../../../shared/ui/page-heading';
12
+ import { Notice } from '../../../shared/ui/notice';
13
+ import { currency, statusLabels } from '../lib/presentation';
14
+ import { OrderAttachments } from '../components/order-attachments';
15
+ import { PurchaseAssistantContext } from '../components/purchase-assistant-context';
16
+ import { ApprovalHistory } from '../components/approval-history';
17
+ import './purchase-intelligence.css';
18
+
19
+ const list = '/procurement/orders';
20
+
21
+ /**
22
+ * 从 URL 参数获取 id,使用生成 schema 校验;无效地址给返回列表入口。
23
+ * 有效 ID 作为 OrderDetail 的 key,使切换记录时重置本地未保存用途草稿。
24
+ */
25
+ export function PurchaseOrderDetailPage() {
26
+ const { id } = useParams();
27
+ const parsed = PurchaseOrderSchema.shape.id.safeParse(id);
28
+ if (!parsed.success)
29
+ return <PageState title="单据地址无效" description="请从采购单列表选择有效单据。" to={list} />;
30
+ return <OrderDetail key={parsed.data} id={parsed.data} />;
31
+ }
32
+
33
+ /**
34
+ * 按 id 读取单据;待审批期间定时刷新,错误不作为空记录处理。
35
+ * 用途草稿用 null 区分尚未编辑与主动清空;保存成功后回到服务端数据,命令使用当前 version。
36
+ * 助理收到明确区分的已保存 record 与未保存 draft;审批历史及附件仍各自按权限读取。
37
+ */
38
+ function OrderDetail({ id }: { id: string }) {
39
+ const query = usePurchaseOrder(id);
40
+ const [draftReason, setDraftReason] = useState<string | null>(null);
41
+ useEffect(() => {
42
+ if (query.data?.status !== 'pending_approval') return;
43
+ const timer = setInterval(() => void query.refetch(), 1500);
44
+ return () => clearInterval(timer);
45
+ }, [query.data?.status, query.refetch]);
46
+ const { run, busy, message, clearMessage } = useCommand();
47
+ if (query.isPending)
48
+ return (
49
+ <p className="empty" role="status">
50
+ 正在读取采购单…
51
+ </p>
52
+ );
53
+ if (query.isError) return <QueryFailure error={query.error} to={list} retry={() => void query.refetch()} />;
54
+ const order = query.data;
55
+ return (
56
+ <>
57
+ <PageHeading
58
+ eyebrow={`PO-${order.id.slice(0, 8).toUpperCase()}`}
59
+ title="采购单详情"
60
+ description="单据内容与最新状态,以服务端记录为准。"
61
+ >
62
+ <Link className="route-link" to={list}>
63
+ ← 返回采购单
64
+ </Link>
65
+ </PageHeading>
66
+ {message && <Notice onClose={clearMessage}>{message}</Notice>}
67
+ <div className="purchase-detail-content"><section className="panel">
68
+ <div className="section-head">
69
+ <span className={`badge ${order.status}`}>{statusLabels[order.status]}</span>
70
+ <div className="actions">
71
+ {order.status === 'draft' && can('PurchaseOrder.submit') && (
72
+ <button
73
+ className="primary"
74
+ disabled={busy}
75
+ onClick={() => run(() => purchaseOrder.submit(id, order.version), '已提交审批')}
76
+ >
77
+ 提交审批
78
+ </button>
79
+ )}
80
+ {['draft', 'pending_approval', 'approved'].includes(order.status) &&
81
+ can('PurchaseOrder.cancel') && (
82
+ <button
83
+ disabled={busy}
84
+ onClick={() => run(() => purchaseOrder.cancel(id, order.version), '采购单已取消')}
85
+ >
86
+ 取消采购单
87
+ </button>
88
+ )}
89
+ </div>
90
+ </div>
91
+ <dl className="record-facts">
92
+ <div>
93
+ <dt>采购金额</dt>
94
+ <dd className="amount">{currency(order.amount)}</dd>
95
+ </div>
96
+ <div>
97
+ <dt>ERP 单号</dt>
98
+ <dd>{order.erpNo ?? '尚未同步'}</dd>
99
+ </div>
100
+ <div>
101
+ <dt>供应商电话</dt>
102
+ <dd>{order.supplierPhone ?? '未填写'}</dd>
103
+ </div>
104
+ <div>
105
+ <dt>创建时间</dt>
106
+ <dd>{new Date(order.createdAt).toLocaleString('zh-CN')}</dd>
107
+ </div>
108
+ <div>
109
+ <dt>单据 ID</dt>
110
+ <dd className="mono">{order.id}</dd>
111
+ </div>
112
+ <div>
113
+ <dt>当前版本</dt>
114
+ <dd>{order.version}</dd>
115
+ </div>
116
+ </dl>
117
+ <div className="purpose-editor"><label htmlFor="purchase-purpose">采购用途与必要性</label>
118
+ {order.status === 'draft' && can('PurchaseOrder.explainPurpose') ? <>
119
+ <textarea id="purchase-purpose" rows={4} maxLength={2000} value={draftReason ?? order.reason ?? ''} onChange={e => setDraftReason(e.target.value)} placeholder="说明用于什么工作、需要哪些物料以及采购原因" />
120
+ <div className="actions"><small className="muted">助理可检查此处尚未保存的内容。</small><button disabled={busy || draftReason === null || !draftReason.trim()} onClick={async () => {
121
+ if (await run(() => purchaseOrder.explainPurpose(id, { reason: draftReason! }, order.version), '采购用途已保存')) setDraftReason(null);
122
+ }}>保存用途</button></div>
123
+ </> : <p>{order.reason || '尚未填写用途'}</p>}
124
+ </div>
125
+ <OrderAttachments order={order} />
126
+ <div className="section-head">
127
+ <h2>采购物料</h2>
128
+ <span className="muted">{order.items.length} 项</span>
129
+ </div>
130
+ <table>
131
+ <thead>
132
+ <tr>
133
+ <th>物料编码</th>
134
+ <th>采购数量</th>
135
+ </tr>
136
+ </thead>
137
+ <tbody>
138
+ {order.items.map((item, index) => (
139
+ <tr key={`${item.sku}-${index}`}>
140
+ <td className="mono">{item.sku}</td>
141
+ <td>{item.qty}</td>
142
+ </tr>
143
+ ))}
144
+ </tbody>
145
+ </table>
146
+ </section>
147
+ <ApprovalHistory id={id} version={order.version} pending={order.status === 'pending_approval'} /></div>
148
+ <PurchaseAssistantContext context={{ page: { id: 'purchase-order-detail', title: '采购单详情', kind: 'detail', route: `/procurement/orders/${id}` },
149
+ subject: { aggregate: 'PurchaseOrder', id, observedVersion: order.version },
150
+ ...(draftReason !== null && order.status === 'draft' ? { draft: { command: 'PurchaseOrder.explainPurpose', values: { reason: draftReason }, baseVersion: order.version } } : {}),
151
+ }} suggestions={['检查当前采购用途是否符合审核要求', '这张采购单现在到哪一步了?', '帮我提交当前采购单']} />
152
+ </>
153
+ );
154
+ }
@@ -1,26 +1,40 @@
1
+ /**
2
+ * 采购列表页:从 URL 恢复状态筛选与页码,查询真实单据,提供创建、提交、取消和后台导出入口。
3
+ * 通过采购模块入口进入路由;审批待办统计由 app 注入,查询和写入均消费生成契约。
4
+ */
1
5
  // 采购页面拥有筛选、分页、新建和导出状态;其他模块不用了解这些实现细节。
2
6
  import { useState } from 'react';
7
+ import { Link, useSearchParams } from 'react-router-dom';
3
8
  import { can, client, purchaseOrder, usePurchaseOrderList, useTransfer } from '@app/contracts';
4
9
  import { useCommand } from '../../../shared/hooks/use-command';
5
10
  import { errorMessage } from '../../../shared/lib/error-message';
6
11
  import { Notice } from '../../../shared/ui/notice';
7
12
  import { PageHeading } from '../../../shared/ui/page-heading';
13
+ import { QueryFailure } from '../../../shared/ui/page-state';
8
14
  import { PurchaseOrderForm } from '../components/purchase-order-form';
15
+ import { currency, statusLabels } from '../lib/presentation';
16
+ import { PurchaseAssistantContext } from '../components/purchase-assistant-context';
9
17
 
10
- const statusLabels: Record<string, string> = {
11
- draft: '草稿',
12
- pending_approval: '待审批',
13
- approved: '已通过',
14
- received: '已收货',
15
- rejected: '已驳回',
16
- cancelled: '已取消',
17
- };
18
- const currency = (value: string) =>
19
- new Intl.NumberFormat('zh-CN', { style: 'currency', currency: 'CNY' }).format(Number(value));
20
-
18
+ /**
19
+ * 按状态及每页十条读取服务端列表,加载、失败和空结果分别呈现。
20
+ * pendingTasks 可省略,表示待办数量尚不可用;创建弹窗和导出任务 ID 只保存在当前页面状态。
21
+ * 提交/取消携带读取到的 version,服务端负责并发、状态及授权检查;导出完成后才显示下载入口。
22
+ */
21
23
  export function PurchaseOrdersPage({ pendingTasks }: { pendingTasks?: number }) {
22
- const [status, setStatus] = useState('');
23
- const [page, setPage] = useState(1);
24
+ // 可分享、刷新恢复的列表条件进入 URL;弹窗和提交中的临时状态继续留在组件里。
25
+ const [search, setSearch] = useSearchParams();
26
+ const status = search.get('status') ?? '';
27
+ const requestedPage = Number(search.get('page') ?? 1);
28
+ const page = Number.isSafeInteger(requestedPage) && requestedPage > 0 ? requestedPage : 1;
29
+ /**
30
+ * 更新 status/page 查询参数并保留其他参数;第一页省略 page。切换条件由调用方传入第一页。
31
+ */
32
+ function filter(nextStatus: string, nextPage: number) {
33
+ const params = new URLSearchParams(search);
34
+ nextStatus ? params.set('status', nextStatus) : params.delete('status');
35
+ nextPage === 1 ? params.delete('page') : params.set('page', String(nextPage));
36
+ setSearch(params);
37
+ }
24
38
  const [creating, setCreating] = useState(false);
25
39
  const [exporting, setExporting] = useState(false);
26
40
  const [exportId, setExportId] = useState<string | null>(null);
@@ -29,6 +43,10 @@ export function PurchaseOrdersPage({ pendingTasks }: { pendingTasks?: number })
29
43
  const { run, busy, message, clearMessage } = useCommand();
30
44
  // 列表和统计来自服务端;待办数量由 app 组合层传入,采购模块不依赖审批页面。
31
45
  const orders = usePurchaseOrderList({ status, page, pageSize: 10, sort: '-createdAt' });
46
+ /**
47
+ * 提交当前状态筛选的后台导出任务。成功只取得任务 ID,由 useTransfer 继续观察完成/失败/过期。
48
+ * 不把任务接受当作 CSV 已可下载,也不只导出当前十条表格数据。
49
+ */
32
50
  async function exportCsv() {
33
51
  setExporting(true);
34
52
  setExportError('');
@@ -37,6 +55,15 @@ export function PurchaseOrdersPage({ pendingTasks }: { pendingTasks?: number })
37
55
  if (result.ok) setExportId(result.data.id);
38
56
  else setExportError(errorMessage(result));
39
57
  }
58
+ if (orders.isError)
59
+ return (
60
+ <QueryFailure
61
+ error={orders.error}
62
+ to="/workspace"
63
+ label="返回工作台"
64
+ retry={() => void orders.refetch()}
65
+ />
66
+ );
40
67
  return (
41
68
  <>
42
69
  <PageHeading eyebrow="PURCHASE ORDERS" title="采购单" description="每一张单据的进展,清晰可见。">
@@ -104,8 +131,7 @@ export function PurchaseOrdersPage({ pendingTasks }: { pendingTasks?: number })
104
131
  className={status === value ? 'active' : ''}
105
132
  key={value}
106
133
  onClick={() => {
107
- setStatus(value);
108
- setPage(1);
134
+ filter(value, 1);
109
135
  }}
110
136
  >
111
137
  {label}
@@ -120,10 +146,6 @@ export function PurchaseOrdersPage({ pendingTasks }: { pendingTasks?: number })
120
146
  </div>
121
147
  {orders.isPending ? (
122
148
  <p className="empty">正在读取采购单…</p>
123
- ) : orders.isError ? (
124
- <p role="alert" className="error">
125
- 读取失败:{orders.error.message}
126
- </p>
127
149
  ) : (
128
150
  <>
129
151
  <div className="table-wrap">
@@ -142,7 +164,12 @@ export function PurchaseOrdersPage({ pendingTasks }: { pendingTasks?: number })
142
164
  {orders.data.items.map((order) => (
143
165
  <tr key={order.id}>
144
166
  <td>
145
- <strong className="mono">PO-{order.id.slice(0, 8).toUpperCase()}</strong>
167
+ <Link
168
+ className="record-link mono"
169
+ to={`/procurement/orders/${encodeURIComponent(order.id)}`}
170
+ >
171
+ PO-{order.id.slice(0, 8).toUpperCase()}
172
+ </Link>
146
173
  <small>{new Date(order.createdAt).toLocaleString('zh-CN')}</small>
147
174
  </td>
148
175
  <td>{order.items.map((i) => `${i.sku} × ${i.qty}`).join('、')}</td>
@@ -193,10 +220,10 @@ export function PurchaseOrdersPage({ pendingTasks }: { pendingTasks?: number })
193
220
  共 {orders.data.total} 单 · 第 {page} 页
194
221
  </span>
195
222
  <div>
196
- <button disabled={page === 1} onClick={() => setPage(page - 1)}>
223
+ <button disabled={page === 1} onClick={() => filter(status, page - 1)}>
197
224
  上一页
198
225
  </button>
199
- <button disabled={page * 10 >= orders.data.total} onClick={() => setPage(page + 1)}>
226
+ <button disabled={page * 10 >= orders.data.total} onClick={() => filter(status, page + 1)}>
200
227
  下一页
201
228
  </button>
202
229
  </div>
@@ -205,6 +232,9 @@ export function PurchaseOrdersPage({ pendingTasks }: { pendingTasks?: number })
205
232
  )}
206
233
  </section>
207
234
  {creating && <PurchaseOrderForm close={() => setCreating(false)} run={run} />}
235
+ <PurchaseAssistantContext context={{ page: { id: 'purchase-orders', title: '采购单列表', kind: 'list', route: `/procurement/orders?${search}` },
236
+ view: { aggregate: 'PurchaseOrder', input: { status, page, pageSize: 10 }, visibleIds: orders.data?.items.map(o => o.id) ?? [] },
237
+ }} suggestions={['汇总本月我有权限查看的采购金额', '采购单的审批规则是什么?']} />
208
238
  </>
209
239
  );
210
240
  }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * workspace 模块的公开入口,对 app 暴露工作台首页。
3
+ * 外部组合通过本入口导入,页面、组件与内部 helper 的组织由模块自行维护。
4
+ */
5
+ export { HomePage } from './pages/home-page';
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 采购工作台首页,提供采购、审批和库存的固定业务入口,不伪造业务统计。
3
+ * 实际记录与待办在对应页面通过生成契约读取,系统管理由独立页面负责。
4
+ */
5
+ import { Link } from 'react-router-dom';
6
+ import { PageHeading } from '../../../shared/ui/page-heading';
7
+ import { AssistantPage } from '../../../shared/ui/assistant-scope';
8
+
9
+ // 工作台展示实际配置的业务入口,不制造演示统计;访问权限由目标路由及接口检查。
10
+ /**
11
+ * 渲染导航卡片,链接进入各模块独立 URL。
12
+ * 本页不按能力隐藏卡片,进入页面后仍需通过路由守卫和服务端检查。
13
+ */
14
+ export function HomePage() {
15
+ return (
16
+ <>
17
+ <AssistantPage context={{ page: { id: 'workspace', title: '采购工作台', kind: 'dashboard', route: '/workspace' },
18
+ visibleContent: [{ id: 'workspace-entries', text: '工作台提供采购单、审批待办和库存台账入口。' }],
19
+ }} suggestions={['我有哪些待处理的审批?', '了解采购单的完整处理流程', '汇总本月我能查看的采购金额']} />
20
+ <PageHeading
21
+ eyebrow="WORKSPACE"
22
+ title="采购工作台"
23
+ description="从采购、审批到库存,每个页面都有可以收藏和分享的地址。"
24
+ />
25
+ <section className="entry-grid">
26
+ <Link to="/procurement/orders">
27
+ <small>01 / PROCUREMENT</small>
28
+ <h2>采购单 ↗</h2>
29
+ <p>查看单据、新建采购与跟踪处理进度。</p>
30
+ </Link>
31
+ <Link to="/approvals">
32
+ <small>02 / APPROVALS</small>
33
+ <h2>审批待办 ↗</h2>
34
+ <p>处理分配给你的任务,直接打开关联单据。</p>
35
+ </Link>
36
+ <Link to="/inventory">
37
+ <small>03 / INVENTORY</small>
38
+ <h2>库存台账 ↗</h2>
39
+ <p>查看物料与登记数量。</p>
40
+ </Link>
41
+ </section>
42
+ </>
43
+ );
44
+ }
@@ -1,8 +1,16 @@
1
+ /**
2
+ * 页面命令调用的轻量状态封装,统一提交中状态、成功反馈和领域错误文案。
3
+ * 消费契约客户端返回的 CageResult;不会自行写数据库或将前端显示状态当作授权。
4
+ */
1
5
  import { useState } from 'react';
2
6
  import { useQueryClient } from '@tanstack/react-query';
3
7
  import type { CageResult } from '@app/contracts';
4
8
  import { errorMessage } from '../lib/error-message';
5
9
 
10
+ /**
11
+ * 页面传入 action 执行一次命令,success 为业务成功提示。
12
+ * Promise<boolean> 只表示本次结果是否 ok,供表单决定关闭或保留草稿;调用者仍需提供目标和版本。
13
+ */
6
14
  export type RunCommand = (
7
15
  action: () => Promise<CageResult<unknown, string>>,
8
16
  success: string,
@@ -12,6 +20,11 @@ export type RunCommand = (
12
20
  * 页面间复用提交状态和结果提示,不包含采购或库存的业务判断。
13
21
  * 成功后使查询缓存失效,让列表从服务端重新读取版本和状态;写入仍由生成 SDK 完成。
14
22
  */
23
+ /**
24
+ * 返回 run、busy、message 和 clearMessage,供按钮禁用与结果提示复用。
25
+ * run 等待 action 返回 CageResult 后解除 busy,成功时请求刷新查询缓存,失败时保留错误文案。
26
+ * 本封装没有捕获 action 抛出的异常;action 应遵守客户端的结果协议,不能把它当作任意异步任务的异常屏障。
27
+ */
15
28
  export function useCommand() {
16
29
  const cache = useQueryClient();
17
30
  const [busy, setBusy] = useState(false);
@@ -1,6 +1,15 @@
1
+ /**
2
+ * 把契约命令的失败结果转换为页面提示,集中维护本应用的错误码文案。
3
+ * 请求追踪标识只用于排错关联,不能用显示文案代替对 ok/code 的控制流判断。
4
+ */
1
5
  import type { CageResult } from '@app/contracts';
2
6
 
3
7
  /** 将框架错误码转换成操作提示,保留 requestId 供用户向维护人员反馈。 */
8
+ /**
9
+ * 选取 domainCode(缺省时取 code)查文案,未知值保留错误码,并在存在 requestId 时附上短追踪标识。
10
+ * @param r 已经判定 ok 为 false 的客户端结果,不接收成功数据。
11
+ * @returns 适合 Notice 展示的错误描述;不会重试命令或修改结果。
12
+ */
4
13
  export function errorMessage(r: Extract<CageResult<unknown, string>, { ok: false }>): string {
5
14
  const labels: Record<string, string> = {
6
15
  FORBIDDEN: '你没有执行此操作的权限',
@@ -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
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 助理输出的 Markdown 展示边界,支持 GFM 表格及链接,并跳过原始 HTML。
3
+ * 图片转换为纯文字说明,避免模型文本直接触发图片加载;业务状态仍由真实查询呈现。
4
+ */
5
+ import Markdown from 'react-markdown';
6
+ import remarkGfm from 'remark-gfm';
7
+
8
+ /** 模型文字是不可信内容:支持 Markdown/GFM,不执行 HTML,也不自动加载模型给出的远端图片。 */
9
+ /**
10
+ * 将 children 中的模型文本渲染为受限 Markdown;外链使用独立窗口和 noopener 等关系属性。
11
+ * 只负责显示,不执行代码块、不把文本转成业务命令,也不对内容的事实准确性背书。
12
+ */
13
+ export function AssistantMarkdown({ children }: { children: string }) {
14
+ return <div className="assistant-markdown"><Markdown remarkPlugins={[remarkGfm]} skipHtml components={{
15
+ a: ({ children, href }) => <a href={href} target="_blank" rel="noopener noreferrer">{children}</a>,
16
+ img: ({ alt }) => <span className="assistant-image-label">[图片:{alt || '未提供说明'}]</span>,
17
+ table: ({ children }) => <div className="assistant-table"><table>{children}</table></div>,
18
+ }}>{children}</Markdown></div>;
19
+ }