@yss123/cage-cli 0.2.0 → 0.3.1

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 (629) 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/approval-tree.d.ts +88 -0
  8. package/dist/approval-tree.js +18 -0
  9. package/dist/approval-tree.js.map +1 -0
  10. package/dist/baseline.d.ts +8 -0
  11. package/dist/baseline.js +13 -0
  12. package/dist/baseline.js.map +1 -1
  13. package/dist/bootstrap.d.ts +20 -0
  14. package/dist/bootstrap.js +75 -0
  15. package/dist/bootstrap.js.map +1 -0
  16. package/dist/budget.d.ts +7 -0
  17. package/dist/budget.js +49 -5
  18. package/dist/budget.js.map +1 -1
  19. package/dist/capabilities.d.ts +38 -0
  20. package/dist/capabilities.js +148 -0
  21. package/dist/capabilities.js.map +1 -0
  22. package/dist/config.d.ts +31 -2
  23. package/dist/config.js +22 -0
  24. package/dist/config.js.map +1 -1
  25. package/dist/context.d.ts +14 -0
  26. package/dist/context.js +35 -2
  27. package/dist/context.js.map +1 -1
  28. package/dist/create-prompt.d.ts +7 -0
  29. package/dist/create-prompt.js +12 -0
  30. package/dist/create-prompt.js.map +1 -1
  31. package/dist/create.d.ts +21 -1
  32. package/dist/create.js +23 -1
  33. package/dist/create.js.map +1 -1
  34. package/dist/derive.d.ts +23 -0
  35. package/dist/derive.js +40 -0
  36. package/dist/derive.js.map +1 -1
  37. package/dist/dev.d.ts +6 -0
  38. package/dist/dev.js +11 -0
  39. package/dist/dev.js.map +1 -1
  40. package/dist/doctor.d.ts +98 -0
  41. package/dist/doctor.js +93 -0
  42. package/dist/doctor.js.map +1 -0
  43. package/dist/env.d.ts +6 -0
  44. package/dist/env.js +11 -0
  45. package/dist/env.js.map +1 -1
  46. package/dist/findings.d.ts +27 -0
  47. package/dist/findings.js +20 -0
  48. package/dist/findings.js.map +1 -1
  49. package/dist/gen/contracts.d.ts +28 -0
  50. package/dist/gen/contracts.js +52 -12
  51. package/dist/gen/contracts.js.map +1 -1
  52. package/dist/gen/index.d.ts +26 -0
  53. package/dist/gen/index.js +69 -1
  54. package/dist/gen/index.js.map +1 -1
  55. package/dist/gen/zod-code.d.ts +42 -0
  56. package/dist/gen/zod-code.js +178 -16
  57. package/dist/gen/zod-code.js.map +1 -1
  58. package/dist/graph-approvals.d.ts +70 -0
  59. package/dist/graph-approvals.js +93 -0
  60. package/dist/graph-approvals.js.map +1 -0
  61. package/dist/graph-business.d.ts +135 -0
  62. package/dist/graph-business.js +404 -0
  63. package/dist/graph-business.js.map +1 -0
  64. package/dist/graph-command.d.ts +17 -0
  65. package/dist/graph-command.js +21 -0
  66. package/dist/graph-command.js.map +1 -1
  67. package/dist/graph-model.d.ts +44 -0
  68. package/dist/graph-model.js +36 -3
  69. package/dist/graph-model.js.map +1 -1
  70. package/dist/graph-presentation.d.ts +16 -0
  71. package/dist/graph-presentation.js +10 -1
  72. package/dist/graph-presentation.js.map +1 -1
  73. package/dist/graph-query.d.ts +95 -0
  74. package/dist/graph-query.js +62 -0
  75. package/dist/graph-query.js.map +1 -1
  76. package/dist/graph-relations.d.ts +20 -0
  77. package/dist/graph-relations.js +42 -10
  78. package/dist/graph-relations.js.map +1 -1
  79. package/dist/graph-search.d.ts +35 -0
  80. package/dist/graph-search.js +36 -0
  81. package/dist/graph-search.js.map +1 -1
  82. package/dist/graph-snapshot.d.ts +26 -0
  83. package/dist/graph-snapshot.js +30 -1
  84. package/dist/graph-snapshot.js.map +1 -1
  85. package/dist/graph.d.ts +13 -2
  86. package/dist/graph.js +61 -4
  87. package/dist/graph.js.map +1 -1
  88. package/dist/impact.d.ts +50 -0
  89. package/dist/impact.js +62 -2
  90. package/dist/impact.js.map +1 -1
  91. package/dist/index.d.ts +10 -0
  92. package/dist/index.js +10 -0
  93. package/dist/index.js.map +1 -1
  94. package/dist/init.d.ts +12 -0
  95. package/dist/init.js +15 -0
  96. package/dist/init.js.map +1 -1
  97. package/dist/load.d.ts +10 -0
  98. package/dist/load.js +15 -0
  99. package/dist/load.js.map +1 -1
  100. package/dist/main.d.ts +6 -0
  101. package/dist/main.js +93 -5
  102. package/dist/main.js.map +1 -1
  103. package/dist/migrate.d.ts +12 -0
  104. package/dist/migrate.js +17 -0
  105. package/dist/migrate.js.map +1 -1
  106. package/dist/ontology.d.ts +67 -2
  107. package/dist/ontology.js +94 -4
  108. package/dist/ontology.js.map +1 -1
  109. package/dist/process-rules.d.ts +21 -0
  110. package/dist/process-rules.js +100 -0
  111. package/dist/process-rules.js.map +1 -0
  112. package/dist/process.d.ts +32 -0
  113. package/dist/process.js +32 -0
  114. package/dist/process.js.map +1 -1
  115. package/dist/project-structure.d.ts +5 -0
  116. package/dist/project-structure.js +10 -0
  117. package/dist/project-structure.js.map +1 -1
  118. package/dist/registration.d.ts +16 -0
  119. package/dist/registration.js +56 -0
  120. package/dist/registration.js.map +1 -0
  121. package/dist/rules.d.ts +52 -0
  122. package/dist/rules.js +58 -0
  123. package/dist/rules.js.map +1 -1
  124. package/dist/runtime-diagnostics.d.ts +27 -0
  125. package/dist/runtime-diagnostics.js +60 -0
  126. package/dist/runtime-diagnostics.js.map +1 -0
  127. package/dist/snapshots.d.ts +40 -0
  128. package/dist/snapshots.js +62 -0
  129. package/dist/snapshots.js.map +1 -0
  130. package/dist/static-check.d.ts +33 -0
  131. package/dist/static-check.js +37 -1
  132. package/dist/static-check.js.map +1 -1
  133. package/dist/sync.d.ts +11 -0
  134. package/dist/sync.js +66 -9
  135. package/dist/sync.js.map +1 -1
  136. package/dist/upgrade.d.ts +16 -0
  137. package/dist/upgrade.js +21 -0
  138. package/dist/upgrade.js.map +1 -1
  139. package/dist/verify.d.ts +14 -0
  140. package/dist/verify.js +19 -0
  141. package/dist/verify.js.map +1 -1
  142. package/dist/version.js +5 -0
  143. package/dist/version.js.map +1 -1
  144. package/graph-ui/app.js +321 -27
  145. package/graph-ui/approval-flow.js +185 -0
  146. package/graph-ui/approval-parallel.js +110 -0
  147. package/graph-ui/approval-tree.js +17 -0
  148. package/graph-ui/business.css +1011 -0
  149. package/graph-ui/business.js +566 -0
  150. package/graph-ui/flowchart-interaction.js +177 -0
  151. package/graph-ui/flowchart.js +1059 -0
  152. package/graph-ui/graph-approvals.js +92 -0
  153. package/graph-ui/graph-business.js +403 -0
  154. package/graph-ui/graph-model.js +36 -3
  155. package/graph-ui/graph-relations.js +42 -10
  156. package/graph-ui/graph-search.js +36 -0
  157. package/graph-ui/index.html +26 -12
  158. package/graph-ui/model.js +28 -0
  159. package/graph-ui/relations.js +4 -0
  160. package/graph-ui/structure.js +44 -0
  161. package/graph-ui/style.css +12 -0
  162. package/package.json +6 -4
  163. package/templates/app/.dockerignore +2 -0
  164. package/templates/app/.env.example +10 -0
  165. package/templates/app/AGENTS.md +50 -10
  166. package/templates/app/README.md +9 -5
  167. package/templates/app/_gitignore +1 -0
  168. package/templates/app/apps/api/package.json +2 -2
  169. package/templates/app/apps/api/src/main.ts +5 -1
  170. package/templates/app/apps/api/src/worker.ts +3 -0
  171. package/templates/app/apps/web/index.html +1 -0
  172. package/templates/app/apps/web/package.json +7 -3
  173. package/templates/app/apps/web/src/app/app.tsx +122 -12
  174. package/templates/app/apps/web/src/app/styles/global.css +88 -8
  175. package/templates/app/apps/web/src/app/use-session.ts +43 -0
  176. package/templates/app/apps/web/src/app/workspace-layout.tsx +58 -19
  177. package/templates/app/apps/web/src/main.tsx +5 -0
  178. package/templates/app/apps/web/src/modules/approval/components/task-operation-dialog.tsx +61 -0
  179. package/templates/app/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +13 -5
  180. package/templates/app/apps/web/src/modules/approval/index.ts +4 -0
  181. package/templates/app/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +89 -38
  182. package/templates/app/apps/web/src/modules/approval/pages/approval-tasks.css +18 -0
  183. package/templates/app/apps/web/src/modules/auth/index.ts +4 -0
  184. package/templates/app/apps/web/src/modules/auth/pages/login-page.tsx +12 -0
  185. package/templates/app/apps/web/src/modules/inventory/index.ts +4 -0
  186. package/templates/app/apps/web/src/modules/inventory/pages/inventory-page.tsx +29 -1
  187. package/templates/app/apps/web/src/modules/purchase/components/approval-history.tsx +67 -0
  188. package/templates/app/apps/web/src/modules/purchase/components/order-attachments.tsx +29 -0
  189. package/templates/app/apps/web/src/modules/purchase/components/order-reviewers.tsx +38 -0
  190. package/templates/app/apps/web/src/modules/purchase/components/purchase-assistant-context.tsx +17 -0
  191. package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.css +6 -0
  192. package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.tsx +32 -3
  193. package/templates/app/apps/web/src/modules/purchase/index.ts +5 -0
  194. package/templates/app/apps/web/src/modules/purchase/lib/presentation.ts +20 -0
  195. package/templates/app/apps/web/src/modules/purchase/pages/purchase-intelligence.css +17 -0
  196. package/templates/app/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +156 -0
  197. package/templates/app/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +54 -22
  198. package/templates/app/apps/web/src/modules/workspace/index.ts +5 -0
  199. package/templates/app/apps/web/src/modules/workspace/pages/home-page.tsx +44 -0
  200. package/templates/app/apps/web/src/shared/hooks/use-command.ts +13 -0
  201. package/templates/app/apps/web/src/shared/lib/error-message.ts +15 -1
  202. package/templates/app/apps/web/src/shared/lib/navigation.ts +21 -0
  203. package/templates/app/apps/web/src/shared/ui/approval-member-picker.css +10 -0
  204. package/templates/app/apps/web/src/shared/ui/approval-member-picker.tsx +51 -0
  205. package/templates/app/apps/web/src/shared/ui/assistant-markdown.tsx +19 -0
  206. package/templates/app/apps/web/src/shared/ui/assistant-panel.css +134 -0
  207. package/templates/app/apps/web/src/shared/ui/assistant-panel.tsx +200 -0
  208. package/templates/app/apps/web/src/shared/ui/assistant-scope.tsx +72 -0
  209. package/templates/app/apps/web/src/shared/ui/attachment-upload.css +28 -0
  210. package/templates/app/apps/web/src/shared/ui/attachment-upload.tsx +163 -0
  211. package/templates/app/apps/web/src/shared/ui/notice.tsx +7 -0
  212. package/templates/app/apps/web/src/shared/ui/page-heading.tsx +7 -0
  213. package/templates/app/apps/web/src/shared/ui/page-state.css +47 -0
  214. package/templates/app/apps/web/src/shared/ui/page-state.tsx +76 -0
  215. package/templates/app/apps/web/src/shared/ui/require-capability.tsx +34 -0
  216. package/templates/app/apps/web/vite.config.ts +5 -0
  217. package/templates/app/cage.config.json +23 -0
  218. package/templates/app/db/migrations/0002_draft.sql +12 -0
  219. package/templates/app/db/migrations/0003_draft.sql +8 -0
  220. package/templates/app/db/migrations/0004_draft.sql +27 -0
  221. package/templates/app/deploy/nginx.conf +52 -0
  222. package/templates/app/docs/ai/README.md +28 -2
  223. package/templates/app/docs/ai/approvals.md +260 -0
  224. package/templates/app/docs/ai/architecture.md +26 -0
  225. package/templates/app/docs/ai/commenting.md +203 -0
  226. package/templates/app/docs/ai/diagnostics.md +80 -0
  227. package/templates/app/docs/ai/files.md +93 -0
  228. package/templates/app/docs/ai/intelligence.md +348 -0
  229. package/templates/app/docs/ai/ontology-context.md +59 -4
  230. package/templates/app/docs/ai/primitives.md +34 -1
  231. package/templates/app/docs/ai/project-structure.md +8 -3
  232. package/templates/app/docs/ai/purchase-example.md +35 -7
  233. package/templates/app/docs/ai/recipes.md +10 -2
  234. package/templates/app/docs/ai/routing.md +86 -0
  235. package/templates/app/docs/ai/troubleshooting.md +5 -0
  236. package/templates/app/docs/ai/workflow.md +24 -11
  237. package/templates/app/docs/errors/CAGE-AI-001.md +61 -0
  238. package/templates/app/docs/errors/CAGE-APPR-001.md +13 -9
  239. package/templates/app/docs/errors/CAGE-APPR-002.md +2 -2
  240. package/templates/app/docs/errors/CAGE-APPR-003.md +1 -1
  241. package/templates/app/docs/errors/CAGE-APPR-004.md +4 -4
  242. package/templates/app/docs/errors/CAGE-APPR-005.md +4 -4
  243. package/templates/app/docs/errors/CAGE-APPR-006.md +29 -8
  244. package/templates/app/docs/errors/CAGE-APPR-007.md +9 -7
  245. package/templates/app/docs/errors/CAGE-APPR-010.md +5 -5
  246. package/templates/app/docs/errors/CAGE-AUDIT-001.md +53 -0
  247. package/templates/app/docs/errors/CAGE-AUTH-001.md +1 -1
  248. package/templates/app/docs/errors/CAGE-BIZ-001.md +51 -0
  249. package/templates/app/docs/errors/CAGE-BIZ-002.md +49 -0
  250. package/templates/app/docs/errors/CAGE-BUDGET-001.md +1 -1
  251. package/templates/app/docs/errors/CAGE-BUDGET-002.md +1 -1
  252. package/templates/app/docs/errors/CAGE-BUDGET-003.md +1 -1
  253. package/templates/app/docs/errors/CAGE-CMD-002.md +1 -1
  254. package/templates/app/docs/errors/CAGE-CMD-003.md +1 -1
  255. package/templates/app/docs/errors/CAGE-CONN-001.md +3 -3
  256. package/templates/app/docs/errors/CAGE-CONN-002.md +1 -1
  257. package/templates/app/docs/errors/CAGE-CONN-003.md +1 -1
  258. package/templates/app/docs/errors/CAGE-CONN-004.md +1 -1
  259. package/templates/app/docs/errors/CAGE-CONN-005.md +1 -1
  260. package/templates/app/docs/errors/CAGE-CONN-006.md +1 -1
  261. package/templates/app/docs/errors/CAGE-CONN-007.md +6 -6
  262. package/templates/app/docs/errors/CAGE-CONN-008.md +5 -5
  263. package/templates/app/docs/errors/CAGE-CONN-009.md +6 -6
  264. package/templates/app/docs/errors/CAGE-CONN-010.md +6 -6
  265. package/templates/app/docs/errors/CAGE-CONN-011.md +1 -1
  266. package/templates/app/docs/errors/CAGE-CONN-012.md +1 -1
  267. package/templates/app/docs/errors/CAGE-CONN-013.md +49 -0
  268. package/templates/app/docs/errors/CAGE-CONN-014.md +48 -0
  269. package/templates/app/docs/errors/CAGE-CONN-015.md +49 -0
  270. package/templates/app/docs/errors/CAGE-CREATE-001.md +10 -9
  271. package/templates/app/docs/errors/CAGE-DECL-001.md +2 -2
  272. package/templates/app/docs/errors/CAGE-DECL-002.md +7 -6
  273. package/templates/app/docs/errors/CAGE-DECL-003.md +1 -1
  274. package/templates/app/docs/errors/CAGE-DECL-004.md +1 -1
  275. package/templates/app/docs/errors/CAGE-DEP-001.md +2 -2
  276. package/templates/app/docs/errors/CAGE-DEP-002.md +1 -1
  277. package/templates/app/docs/errors/CAGE-DEP-003.md +2 -2
  278. package/templates/app/docs/errors/CAGE-DEP-004.md +1 -1
  279. package/templates/app/docs/errors/CAGE-DEP-005.md +1 -1
  280. package/templates/app/docs/errors/CAGE-DIAG-001.md +53 -0
  281. package/templates/app/docs/errors/CAGE-DLQ-001.md +2 -2
  282. package/templates/app/docs/errors/CAGE-ENV-001.md +4 -4
  283. package/templates/app/docs/errors/CAGE-ENV-002.md +51 -0
  284. package/templates/app/docs/errors/CAGE-ENV-003.md +48 -0
  285. package/templates/app/docs/errors/CAGE-EVENT-001.md +1 -1
  286. package/templates/app/docs/errors/CAGE-EVENT-002.md +7 -7
  287. package/templates/app/docs/errors/CAGE-FIELD-001.md +2 -2
  288. package/templates/app/docs/errors/CAGE-FIELD-002.md +1 -1
  289. package/templates/app/docs/errors/CAGE-FIELD-003.md +1 -1
  290. package/templates/app/docs/errors/CAGE-FIELD-004.md +1 -1
  291. package/templates/app/docs/errors/CAGE-FILE-001.md +56 -0
  292. package/templates/app/docs/errors/CAGE-GEN-001.md +2 -2
  293. package/templates/app/docs/errors/CAGE-GEN-002.md +1 -1
  294. package/templates/app/docs/errors/CAGE-GEN-003.md +53 -0
  295. package/templates/app/docs/errors/CAGE-GRANT-001.md +3 -3
  296. package/templates/app/docs/errors/CAGE-GRANT-002.md +2 -2
  297. package/templates/app/docs/errors/CAGE-GRANT-003.md +7 -6
  298. package/templates/app/docs/errors/CAGE-IMPACT-001.md +2 -2
  299. package/templates/app/docs/errors/CAGE-IMPACT-002.md +52 -0
  300. package/templates/app/docs/errors/CAGE-INIT-001.md +1 -1
  301. package/templates/app/docs/errors/CAGE-INIT-002.md +1 -1
  302. package/templates/app/docs/errors/CAGE-INIT-003.md +2 -2
  303. package/templates/app/docs/errors/CAGE-INIT-004.md +1 -1
  304. package/templates/app/docs/errors/CAGE-INIT-005.md +52 -0
  305. package/templates/app/docs/errors/CAGE-JOB-001.md +1 -1
  306. package/templates/app/docs/errors/CAGE-JOB-002.md +1 -1
  307. package/templates/app/docs/errors/CAGE-JOB-003.md +1 -1
  308. package/templates/app/docs/errors/CAGE-LINT-001.md +1 -1
  309. package/templates/app/docs/errors/CAGE-MIG-001.md +1 -1
  310. package/templates/app/docs/errors/CAGE-MIG-002.md +1 -1
  311. package/templates/app/docs/errors/CAGE-MIG-005.md +1 -1
  312. package/templates/app/docs/errors/CAGE-NAME-001.md +1 -1
  313. package/templates/app/docs/errors/CAGE-NAME-002.md +1 -1
  314. package/templates/app/docs/errors/CAGE-NAME-003.md +3 -3
  315. package/templates/app/docs/errors/CAGE-NAME-004.md +1 -1
  316. package/templates/app/docs/errors/CAGE-NAME-005.md +1 -1
  317. package/templates/app/docs/errors/CAGE-NAME-006.md +1 -1
  318. package/templates/app/docs/errors/CAGE-ORG-001.md +7 -6
  319. package/templates/app/docs/errors/CAGE-PAIR-002.md +6 -5
  320. package/templates/app/docs/errors/CAGE-QUERY-001.md +1 -1
  321. package/templates/app/docs/errors/CAGE-QUERY-002.md +2 -2
  322. package/templates/app/docs/errors/CAGE-QUERY-003.md +7 -6
  323. package/templates/app/docs/errors/CAGE-REACT-001.md +3 -3
  324. package/templates/app/docs/errors/CAGE-REACT-002.md +1 -1
  325. package/templates/app/docs/errors/CAGE-REACT-003.md +50 -0
  326. package/templates/app/docs/errors/CAGE-READS-001.md +9 -6
  327. package/templates/app/docs/errors/CAGE-REG-001.md +13 -12
  328. package/templates/app/docs/errors/CAGE-REG-002.md +2 -1
  329. package/templates/app/docs/errors/CAGE-REG-003.md +1 -1
  330. package/templates/app/docs/errors/CAGE-REL-001.md +14 -14
  331. package/templates/app/docs/errors/CAGE-ROUTE-001.md +7 -6
  332. package/templates/app/docs/errors/CAGE-SCOPE-001.md +1 -1
  333. package/templates/app/docs/errors/CAGE-SCOPE-002.md +2 -2
  334. package/templates/app/docs/errors/CAGE-SECRET-001.md +2 -2
  335. package/templates/app/docs/errors/CAGE-SECRET-002.md +1 -1
  336. package/templates/app/docs/errors/CAGE-SENS-001.md +5 -5
  337. package/templates/app/docs/errors/CAGE-SQL-001.md +1 -1
  338. package/templates/app/docs/errors/CAGE-SQL-002.md +1 -1
  339. package/templates/app/docs/errors/CAGE-STATE-001.md +3 -3
  340. package/templates/app/docs/errors/CAGE-STATE-002.md +1 -1
  341. package/templates/app/docs/errors/CAGE-STATE-003.md +1 -1
  342. package/templates/app/docs/errors/CAGE-STATE-004.md +1 -1
  343. package/templates/app/docs/errors/CAGE-STATE-005.md +3 -2
  344. package/templates/app/docs/errors/CAGE-STATE-010.md +5 -5
  345. package/templates/app/docs/errors/CAGE-STATE-011.md +5 -5
  346. package/templates/app/docs/errors/CAGE-STRUCT-001.md +1 -1
  347. package/templates/app/docs/errors/CAGE-STRUCT-002.md +1 -1
  348. package/templates/app/docs/errors/CAGE-SYNC-001.md +3 -2
  349. package/templates/app/docs/errors/CAGE-TEST-001.md +1 -1
  350. package/templates/app/docs/errors/CAGE-TEST-002.md +50 -0
  351. package/templates/app/docs/errors/CAGE-TEST-003.md +52 -0
  352. package/templates/app/docs/errors/CAGE-UNSAFE-001.md +3 -2
  353. package/templates/app/docs/errors/CAGE-UPGRADE-001.md +2 -2
  354. package/templates/app/docs/errors/CAGE-WORKER-001.md +48 -0
  355. package/templates/app/docs/errors/CAGE-WORKER-002.md +50 -0
  356. package/templates/app/docs/errors/CAGE-WORKER-003.md +48 -0
  357. package/templates/app/docs/errors/README.md +52 -2
  358. package/templates/app/package.json +3 -3
  359. package/templates/app/packages/contracts/package.json +1 -1
  360. package/templates/app/packages/contracts/src/aggregates/approval-member/index.ts +70 -0
  361. package/templates/app/packages/contracts/src/aggregates/dept/index.ts +10 -10
  362. package/templates/app/packages/contracts/src/aggregates/inventory/index.ts +5 -5
  363. package/templates/app/packages/contracts/src/aggregates/inventory-lock/index.ts +4 -4
  364. package/templates/app/packages/contracts/src/aggregates/purchase-order/index.ts +34 -12
  365. package/templates/app/packages/contracts/src/aggregates/role/index.ts +15 -15
  366. package/templates/app/packages/contracts/src/aggregates/user/index.ts +16 -16
  367. package/templates/app/packages/contracts/src/index.ts +16 -1
  368. package/templates/app/packages/contracts/src/queries/index.ts +2 -2
  369. package/templates/app/packages/domain/package.json +5 -4
  370. package/templates/app/packages/domain/src/index.ts +4 -0
  371. package/templates/app/packages/domain/src/integrations/erp.ts +15 -0
  372. package/templates/app/packages/domain/src/integrations/intelligence.ts +20 -0
  373. package/templates/app/packages/domain/src/modules/inventory/index.ts +1 -0
  374. package/templates/app/packages/domain/src/modules/inventory/inventory.ts +12 -0
  375. package/templates/app/packages/domain/src/modules/purchase/approval-members.ts +54 -0
  376. package/templates/app/packages/domain/src/modules/purchase/approvals.ts +38 -6
  377. package/templates/app/packages/domain/src/modules/purchase/index.ts +2 -0
  378. package/templates/app/packages/domain/src/modules/purchase/intelligence.test.ts +224 -0
  379. package/templates/app/packages/domain/src/modules/purchase/jobs.ts +5 -0
  380. package/templates/app/packages/domain/src/modules/purchase/purchase-order.test.ts +9 -0
  381. package/templates/app/packages/domain/src/modules/purchase/purchase-order.ts +125 -4
  382. package/templates/app/packages/domain/src/modules/purchase/queries.ts +5 -0
  383. package/templates/app/packages/domain/src/workflows/purchase-inventory.ts +6 -0
  384. package/templates/app/packages/domain/src/workflows/purchase-process.ts +42 -0
  385. package/templates/blank/.dockerignore +2 -0
  386. package/templates/blank/.env.example +10 -0
  387. package/templates/blank/AGENTS.md +50 -10
  388. package/templates/blank/README.md +4 -1
  389. package/templates/blank/_gitignore +1 -0
  390. package/templates/blank/apps/api/package.json +1 -1
  391. package/templates/blank/apps/api/src/main.ts +4 -1
  392. package/templates/blank/apps/api/src/worker.ts +2 -0
  393. package/templates/blank/apps/web/index.html +1 -0
  394. package/templates/blank/apps/web/package.json +7 -3
  395. package/templates/blank/apps/web/src/app/app.tsx +80 -18
  396. package/templates/blank/apps/web/src/app/styles/global.css +90 -0
  397. package/templates/blank/apps/web/src/app/use-session.ts +43 -0
  398. package/templates/blank/apps/web/src/app/workspace-layout.tsx +30 -5
  399. package/templates/blank/apps/web/src/main.tsx +5 -0
  400. package/templates/blank/apps/web/src/modules/auth/index.ts +4 -0
  401. package/templates/blank/apps/web/src/modules/auth/pages/login-page.tsx +18 -1
  402. package/templates/blank/apps/web/src/modules/directory/index.ts +7 -0
  403. package/templates/blank/apps/web/src/modules/directory/pages/user-detail-page.tsx +78 -0
  404. package/templates/blank/apps/web/src/modules/directory/pages/users-page.tsx +93 -0
  405. package/templates/blank/apps/web/src/modules/workspace/index.ts +4 -0
  406. package/templates/blank/apps/web/src/modules/workspace/pages/home-page.tsx +17 -0
  407. package/templates/blank/apps/web/src/shared/lib/error-message.ts +11 -0
  408. package/templates/blank/apps/web/src/shared/lib/navigation.ts +21 -0
  409. package/templates/blank/apps/web/src/shared/ui/brand.tsx +10 -2
  410. package/templates/blank/apps/web/src/shared/ui/page-state.css +47 -0
  411. package/templates/blank/apps/web/src/shared/ui/page-state.tsx +76 -0
  412. package/templates/blank/apps/web/src/shared/ui/require-capability.tsx +34 -0
  413. package/templates/blank/apps/web/vite.config.ts +5 -0
  414. package/templates/blank/deploy/nginx.conf +52 -0
  415. package/templates/blank/docs/ai/README.md +28 -2
  416. package/templates/blank/docs/ai/approvals.md +260 -0
  417. package/templates/blank/docs/ai/architecture.md +26 -0
  418. package/templates/blank/docs/ai/commenting.md +203 -0
  419. package/templates/blank/docs/ai/diagnostics.md +80 -0
  420. package/templates/blank/docs/ai/files.md +93 -0
  421. package/templates/blank/docs/ai/intelligence.md +348 -0
  422. package/templates/blank/docs/ai/ontology-context.md +59 -4
  423. package/templates/blank/docs/ai/primitives.md +34 -1
  424. package/templates/blank/docs/ai/project-structure.md +8 -3
  425. package/templates/blank/docs/ai/purchase-example.md +35 -7
  426. package/templates/blank/docs/ai/recipes.md +10 -2
  427. package/templates/blank/docs/ai/routing.md +86 -0
  428. package/templates/blank/docs/ai/troubleshooting.md +5 -0
  429. package/templates/blank/docs/ai/workflow.md +24 -11
  430. package/templates/blank/docs/errors/CAGE-AI-001.md +61 -0
  431. package/templates/blank/docs/errors/CAGE-APPR-001.md +13 -9
  432. package/templates/blank/docs/errors/CAGE-APPR-002.md +2 -2
  433. package/templates/blank/docs/errors/CAGE-APPR-003.md +1 -1
  434. package/templates/blank/docs/errors/CAGE-APPR-004.md +4 -4
  435. package/templates/blank/docs/errors/CAGE-APPR-005.md +4 -4
  436. package/templates/blank/docs/errors/CAGE-APPR-006.md +29 -8
  437. package/templates/blank/docs/errors/CAGE-APPR-007.md +9 -7
  438. package/templates/blank/docs/errors/CAGE-APPR-010.md +5 -5
  439. package/templates/blank/docs/errors/CAGE-AUDIT-001.md +53 -0
  440. package/templates/blank/docs/errors/CAGE-AUTH-001.md +1 -1
  441. package/templates/blank/docs/errors/CAGE-BIZ-001.md +51 -0
  442. package/templates/blank/docs/errors/CAGE-BIZ-002.md +49 -0
  443. package/templates/blank/docs/errors/CAGE-BUDGET-001.md +1 -1
  444. package/templates/blank/docs/errors/CAGE-BUDGET-002.md +1 -1
  445. package/templates/blank/docs/errors/CAGE-BUDGET-003.md +1 -1
  446. package/templates/blank/docs/errors/CAGE-CMD-002.md +1 -1
  447. package/templates/blank/docs/errors/CAGE-CMD-003.md +1 -1
  448. package/templates/blank/docs/errors/CAGE-CONN-001.md +3 -3
  449. package/templates/blank/docs/errors/CAGE-CONN-002.md +1 -1
  450. package/templates/blank/docs/errors/CAGE-CONN-003.md +1 -1
  451. package/templates/blank/docs/errors/CAGE-CONN-004.md +1 -1
  452. package/templates/blank/docs/errors/CAGE-CONN-005.md +1 -1
  453. package/templates/blank/docs/errors/CAGE-CONN-006.md +1 -1
  454. package/templates/blank/docs/errors/CAGE-CONN-007.md +6 -6
  455. package/templates/blank/docs/errors/CAGE-CONN-008.md +5 -5
  456. package/templates/blank/docs/errors/CAGE-CONN-009.md +6 -6
  457. package/templates/blank/docs/errors/CAGE-CONN-010.md +6 -6
  458. package/templates/blank/docs/errors/CAGE-CONN-011.md +1 -1
  459. package/templates/blank/docs/errors/CAGE-CONN-012.md +1 -1
  460. package/templates/blank/docs/errors/CAGE-CONN-013.md +49 -0
  461. package/templates/blank/docs/errors/CAGE-CONN-014.md +48 -0
  462. package/templates/blank/docs/errors/CAGE-CONN-015.md +49 -0
  463. package/templates/blank/docs/errors/CAGE-CREATE-001.md +10 -9
  464. package/templates/blank/docs/errors/CAGE-DECL-001.md +2 -2
  465. package/templates/blank/docs/errors/CAGE-DECL-002.md +7 -6
  466. package/templates/blank/docs/errors/CAGE-DECL-003.md +1 -1
  467. package/templates/blank/docs/errors/CAGE-DECL-004.md +1 -1
  468. package/templates/blank/docs/errors/CAGE-DEP-001.md +2 -2
  469. package/templates/blank/docs/errors/CAGE-DEP-002.md +1 -1
  470. package/templates/blank/docs/errors/CAGE-DEP-003.md +2 -2
  471. package/templates/blank/docs/errors/CAGE-DEP-004.md +1 -1
  472. package/templates/blank/docs/errors/CAGE-DEP-005.md +1 -1
  473. package/templates/blank/docs/errors/CAGE-DIAG-001.md +53 -0
  474. package/templates/blank/docs/errors/CAGE-DLQ-001.md +2 -2
  475. package/templates/blank/docs/errors/CAGE-ENV-001.md +4 -4
  476. package/templates/blank/docs/errors/CAGE-ENV-002.md +51 -0
  477. package/templates/blank/docs/errors/CAGE-ENV-003.md +48 -0
  478. package/templates/blank/docs/errors/CAGE-EVENT-001.md +1 -1
  479. package/templates/blank/docs/errors/CAGE-EVENT-002.md +7 -7
  480. package/templates/blank/docs/errors/CAGE-FIELD-001.md +2 -2
  481. package/templates/blank/docs/errors/CAGE-FIELD-002.md +1 -1
  482. package/templates/blank/docs/errors/CAGE-FIELD-003.md +1 -1
  483. package/templates/blank/docs/errors/CAGE-FIELD-004.md +1 -1
  484. package/templates/blank/docs/errors/CAGE-FILE-001.md +56 -0
  485. package/templates/blank/docs/errors/CAGE-GEN-001.md +2 -2
  486. package/templates/blank/docs/errors/CAGE-GEN-002.md +1 -1
  487. package/templates/blank/docs/errors/CAGE-GEN-003.md +53 -0
  488. package/templates/blank/docs/errors/CAGE-GRANT-001.md +3 -3
  489. package/templates/blank/docs/errors/CAGE-GRANT-002.md +2 -2
  490. package/templates/blank/docs/errors/CAGE-GRANT-003.md +7 -6
  491. package/templates/blank/docs/errors/CAGE-IMPACT-001.md +2 -2
  492. package/templates/blank/docs/errors/CAGE-IMPACT-002.md +52 -0
  493. package/templates/blank/docs/errors/CAGE-INIT-001.md +1 -1
  494. package/templates/blank/docs/errors/CAGE-INIT-002.md +1 -1
  495. package/templates/blank/docs/errors/CAGE-INIT-003.md +2 -2
  496. package/templates/blank/docs/errors/CAGE-INIT-004.md +1 -1
  497. package/templates/blank/docs/errors/CAGE-INIT-005.md +52 -0
  498. package/templates/blank/docs/errors/CAGE-JOB-001.md +1 -1
  499. package/templates/blank/docs/errors/CAGE-JOB-002.md +1 -1
  500. package/templates/blank/docs/errors/CAGE-JOB-003.md +1 -1
  501. package/templates/blank/docs/errors/CAGE-LINT-001.md +1 -1
  502. package/templates/blank/docs/errors/CAGE-MIG-001.md +1 -1
  503. package/templates/blank/docs/errors/CAGE-MIG-002.md +1 -1
  504. package/templates/blank/docs/errors/CAGE-MIG-005.md +1 -1
  505. package/templates/blank/docs/errors/CAGE-NAME-001.md +1 -1
  506. package/templates/blank/docs/errors/CAGE-NAME-002.md +1 -1
  507. package/templates/blank/docs/errors/CAGE-NAME-003.md +3 -3
  508. package/templates/blank/docs/errors/CAGE-NAME-004.md +1 -1
  509. package/templates/blank/docs/errors/CAGE-NAME-005.md +1 -1
  510. package/templates/blank/docs/errors/CAGE-NAME-006.md +1 -1
  511. package/templates/blank/docs/errors/CAGE-ORG-001.md +7 -6
  512. package/templates/blank/docs/errors/CAGE-PAIR-002.md +6 -5
  513. package/templates/blank/docs/errors/CAGE-QUERY-001.md +1 -1
  514. package/templates/blank/docs/errors/CAGE-QUERY-002.md +2 -2
  515. package/templates/blank/docs/errors/CAGE-QUERY-003.md +7 -6
  516. package/templates/blank/docs/errors/CAGE-REACT-001.md +3 -3
  517. package/templates/blank/docs/errors/CAGE-REACT-002.md +1 -1
  518. package/templates/blank/docs/errors/CAGE-REACT-003.md +50 -0
  519. package/templates/blank/docs/errors/CAGE-READS-001.md +9 -6
  520. package/templates/blank/docs/errors/CAGE-REG-001.md +13 -12
  521. package/templates/blank/docs/errors/CAGE-REG-002.md +2 -1
  522. package/templates/blank/docs/errors/CAGE-REG-003.md +1 -1
  523. package/templates/blank/docs/errors/CAGE-REL-001.md +14 -14
  524. package/templates/blank/docs/errors/CAGE-ROUTE-001.md +7 -6
  525. package/templates/blank/docs/errors/CAGE-SCOPE-001.md +1 -1
  526. package/templates/blank/docs/errors/CAGE-SCOPE-002.md +2 -2
  527. package/templates/blank/docs/errors/CAGE-SECRET-001.md +2 -2
  528. package/templates/blank/docs/errors/CAGE-SECRET-002.md +1 -1
  529. package/templates/blank/docs/errors/CAGE-SENS-001.md +5 -5
  530. package/templates/blank/docs/errors/CAGE-SQL-001.md +1 -1
  531. package/templates/blank/docs/errors/CAGE-SQL-002.md +1 -1
  532. package/templates/blank/docs/errors/CAGE-STATE-001.md +3 -3
  533. package/templates/blank/docs/errors/CAGE-STATE-002.md +1 -1
  534. package/templates/blank/docs/errors/CAGE-STATE-003.md +1 -1
  535. package/templates/blank/docs/errors/CAGE-STATE-004.md +1 -1
  536. package/templates/blank/docs/errors/CAGE-STATE-005.md +3 -2
  537. package/templates/blank/docs/errors/CAGE-STATE-010.md +5 -5
  538. package/templates/blank/docs/errors/CAGE-STATE-011.md +5 -5
  539. package/templates/blank/docs/errors/CAGE-STRUCT-001.md +1 -1
  540. package/templates/blank/docs/errors/CAGE-STRUCT-002.md +1 -1
  541. package/templates/blank/docs/errors/CAGE-SYNC-001.md +3 -2
  542. package/templates/blank/docs/errors/CAGE-TEST-001.md +1 -1
  543. package/templates/blank/docs/errors/CAGE-TEST-002.md +50 -0
  544. package/templates/blank/docs/errors/CAGE-TEST-003.md +52 -0
  545. package/templates/blank/docs/errors/CAGE-UNSAFE-001.md +3 -2
  546. package/templates/blank/docs/errors/CAGE-UPGRADE-001.md +2 -2
  547. package/templates/blank/docs/errors/CAGE-WORKER-001.md +48 -0
  548. package/templates/blank/docs/errors/CAGE-WORKER-002.md +50 -0
  549. package/templates/blank/docs/errors/CAGE-WORKER-003.md +48 -0
  550. package/templates/blank/docs/errors/README.md +52 -2
  551. package/templates/blank/docs/examples/purchase-admin/apps/api/src/main.ts +5 -1
  552. package/templates/blank/docs/examples/purchase-admin/apps/api/src/worker.ts +3 -0
  553. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/app.tsx +122 -12
  554. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/styles/global.css +88 -8
  555. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/use-session.ts +43 -0
  556. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/workspace-layout.tsx +58 -19
  557. package/templates/blank/docs/examples/purchase-admin/apps/web/src/main.tsx +5 -0
  558. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/components/task-operation-dialog.tsx +61 -0
  559. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +13 -5
  560. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/index.ts +4 -0
  561. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +89 -38
  562. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks.css +18 -0
  563. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/index.ts +4 -0
  564. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/pages/login-page.tsx +12 -0
  565. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/index.ts +4 -0
  566. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/pages/inventory-page.tsx +29 -1
  567. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/approval-history.tsx +67 -0
  568. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/order-attachments.tsx +29 -0
  569. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/order-reviewers.tsx +38 -0
  570. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-assistant-context.tsx +17 -0
  571. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.css +6 -0
  572. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.tsx +32 -3
  573. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/index.ts +5 -0
  574. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/lib/presentation.ts +20 -0
  575. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-intelligence.css +17 -0
  576. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +156 -0
  577. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +54 -22
  578. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/workspace/index.ts +5 -0
  579. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/workspace/pages/home-page.tsx +44 -0
  580. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/hooks/use-command.ts +13 -0
  581. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts +15 -1
  582. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/navigation.ts +21 -0
  583. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/approval-member-picker.css +10 -0
  584. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/approval-member-picker.tsx +51 -0
  585. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-markdown.tsx +19 -0
  586. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-panel.css +134 -0
  587. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-panel.tsx +200 -0
  588. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/assistant-scope.tsx +72 -0
  589. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/attachment-upload.css +28 -0
  590. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/attachment-upload.tsx +163 -0
  591. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/notice.tsx +7 -0
  592. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-heading.tsx +7 -0
  593. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-state.css +47 -0
  594. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-state.tsx +76 -0
  595. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/require-capability.tsx +34 -0
  596. package/templates/blank/docs/examples/purchase-admin/cage.config.json +23 -0
  597. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/approval-member/index.ts +70 -0
  598. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/dept/index.ts +10 -10
  599. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory/index.ts +5 -5
  600. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory-lock/index.ts +4 -4
  601. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/purchase-order/index.ts +34 -12
  602. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/role/index.ts +15 -15
  603. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/user/index.ts +16 -16
  604. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/index.ts +16 -1
  605. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/queries/index.ts +2 -2
  606. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/index.ts +4 -0
  607. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/integrations/erp.ts +15 -0
  608. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/integrations/intelligence.ts +20 -0
  609. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/index.ts +1 -0
  610. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/inventory.ts +12 -0
  611. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approval-members.ts +54 -0
  612. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts +38 -6
  613. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/index.ts +2 -0
  614. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/intelligence.test.ts +224 -0
  615. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/jobs.ts +5 -0
  616. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.test.ts +9 -0
  617. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.ts +125 -4
  618. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/queries.ts +5 -0
  619. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-inventory.ts +6 -0
  620. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-process.ts +42 -0
  621. package/templates/blank/package.json +3 -3
  622. package/templates/blank/packages/contracts/package.json +1 -1
  623. package/templates/blank/packages/contracts/src/aggregates/dept/index.ts +10 -10
  624. package/templates/blank/packages/contracts/src/aggregates/role/index.ts +15 -15
  625. package/templates/blank/packages/contracts/src/aggregates/user/index.ts +16 -16
  626. package/templates/blank/packages/contracts/src/index.ts +1 -1
  627. package/templates/blank/packages/contracts/src/queries/index.ts +1 -1
  628. package/templates/blank/packages/domain/package.json +5 -4
  629. package/templates/blank/packages/domain/src/index.ts +2 -1
@@ -1,53 +1,104 @@
1
- // 审批待办独立成页;结论调用框架审批接口,领域约束仍在后端执行。
1
+ /**
2
+ * 审批收件箱使用 URL 的 view 切换本人、可领取和等待加签任务,刷新/前进后退保留视图。
3
+ * 通过 app 注入详情链接,界面调用客户端完成领取、释放、人员流转与决定;候选身份和版本仍由服务端校验。
4
+ * 原因是行内临时草稿,动作成功后由 useCommand 失效查询并重读;waiting 仅供查看,不显示决定入口。
5
+ */
6
+ import { useState } from 'react';
2
7
  import { client } from '@app/contracts';
3
- import { useCommand } from '../../../shared/hooks/use-command';
8
+ import type { ApprovalTask } from '@yss123/cage-client';
9
+ import { Link, useSearchParams } from 'react-router-dom';
10
+ import { useCommand, type RunCommand } from '../../../shared/hooks/use-command';
4
11
  import { Notice } from '../../../shared/ui/notice';
5
12
  import { PageHeading } from '../../../shared/ui/page-heading';
6
13
  import { useApprovalTasks } from '../hooks/use-approval-tasks';
14
+ import { AssistantPage } from '../../../shared/ui/assistant-scope';
15
+ import './approval-tasks.css';
16
+ import { TaskOperationDialog, type PersonnelOperation } from '../components/task-operation-dialog';
7
17
 
8
- export function ApprovalTasksPage() {
9
- const tasks = useApprovalTasks();
18
+ /** 三种任务视图进入查询参数;只有 pending 会计入原有导航待办数。 */
19
+ const views = { pending: '待我处理', candidates: '可领取', waiting: '等待加签' } as const;
20
+
21
+ /**
22
+ * 为一项任务展示顺序、意见和合法阶段入口。busy 来自整页,提交时防止同页重复操作。
23
+ * 领取/释放/委派归还必须人工填写原因;决定意见可选。每次都提交当前 revision,失败保留草稿供核对。
24
+ * @param task 服务端安全摘要;assigneeId 为空表示尚未领取,不可直接批准。
25
+ * @param orderUrl app 组合的详情 URL 生成器,不访问其他业务模块内部。
26
+ * @param run 统一结果/缓存处理;此组件不乐观修改任务或赋予权限。
27
+ */
28
+ function TaskRow({ task, orderUrl, busy, run }: { task: ApprovalTask; orderUrl: (id: string) => string; busy: boolean; run: RunCommand }) {
29
+ const [reason, setReason] = useState('');
30
+ const [operation, setOperation] = useState<PersonnelOperation | null>(null);
31
+ const waiting = task.status === 'waiting';
32
+ const candidate = task.assigneeId === null;
33
+ const delegated = task.delegation === 'pending';
34
+ const manage = (action: 'claim' | 'unclaim' | 'resolve', success: string) => run(
35
+ () => client.approvalTaskAction(task.id, { action, expectedRevision: task.revision, reason }), success);
36
+ const position = task.signPosition === 'before' ? '前签' : task.signPosition === 'after' ? '后签' : task.taskKind === 'additional' ? '并行加签' : null;
37
+ return (
38
+ <div className="task">
39
+ <div className="approval-task-summary">
40
+ <h3>{task.title}{position && <small className="muted"> · {position}</small>}</h3>
41
+ {!!task.branchPath?.length && <p className="muted">{task.branchPath.map(part => `${part.parallel} · ${part.branch}`).join(' / ')}</p>}
42
+ <p className="mono muted">PO-{task.businessKey.slice(0, 8).toUpperCase()}</p>
43
+ <p className="muted">金额 ¥{String(task.vars.amount ?? '—')}{task.vars.needsSpecialistReview === true ? ' · 含专业会签' : ''}</p>
44
+ <p className="muted">{waiting ? '前置签署完成后恢复处理' : candidate ? '领取后由你负责处理' : delegated ? '受托处理中,请先归还原审批人' : '请核对单据后提交意见'}</p>
45
+ </div>
46
+ <div className="approval-task-controls">
47
+ <div className="actions">
48
+ <Link className="route-link" to={orderUrl(task.businessKey)}>查看采购单 ↗</Link>
49
+ {!waiting && <input aria-label={`${task.title}处理意见`} placeholder={candidate || delegated ? '填写操作原因(必填)' : '审批意见;释放任务时必填'}
50
+ maxLength={1000} value={reason} disabled={busy} onChange={event => setReason(event.target.value)} />}
51
+ </div>
52
+ {!waiting && <div className="actions">
53
+ {candidate ? <button className="primary" disabled={busy || !reason.trim()} onClick={() => void manage('claim', '已领取,请在待我处理中继续')}>领取任务</button>
54
+ : delegated ? <button disabled={busy || !reason.trim()} onClick={() => void manage('resolve', '已归还原审批人')}>归还审批人</button>
55
+ : <>
56
+ {task.assignment === 'candidate' && <button disabled={busy || !reason.trim()} onClick={() => void manage('unclaim', '已释放到候选池')}>释放任务</button>}
57
+ <button disabled={busy} onClick={() => void run(() => client.decideApprovalTask(task.id, 'reject', { expectedRevision: task.revision, comment: reason }), '已驳回')}>驳回</button>
58
+ <button disabled={busy} onClick={() => void run(() => client.decideApprovalTask(task.id, 'return', { expectedRevision: task.revision, comment: reason }), '已退回修改')}>退回修改</button>
59
+ <button disabled={busy} className="primary" onClick={() => void run(() => client.decideApprovalTask(task.id, 'approve', { expectedRevision: task.revision, comment: reason }), '已通过审批')}>通过审批</button>
60
+ {task.actions.transfer && <button disabled={busy} onClick={() => setOperation('transfer')}>转办</button>}
61
+ {task.actions.delegate && <button disabled={busy} onClick={() => setOperation('delegate')}>委派</button>}
62
+ {task.actions.addSign && task.taskKind === 'original' && task.signPositions.map(position => <button key={position} disabled={busy}
63
+ onClick={() => setOperation(position)}>{position === 'before' ? '前加签' : position === 'after' ? '后加签' : '并行加签'}</button>)}
64
+ </>}
65
+ </div>}
66
+ </div>
67
+ {operation && <TaskOperationDialog task={task} operation={operation} close={() => setOperation(null)} />}
68
+ </div>
69
+ );
70
+ }
71
+
72
+ /**
73
+ * 读取当前视图并区分加载、错误与空结果;URL 中未知 view 按默认待办读取,不制造无效请求。
74
+ * @param orderUrl 由 app 将业务主键转为采购详情地址;组件只改变 view 参数,保留其他 URL 参数。
75
+ * 人员操作使用业务协作名录,不读取完整组织用户;动作政策与签序来自实例快照,不按最新声明猜测在途权限。
76
+ */
77
+ export function ApprovalTasksPage({ orderUrl }: { orderUrl: (id: string) => string }) {
78
+ const [params, setParams] = useSearchParams();
79
+ const selected = params.get('view');
80
+ const view = selected === 'candidates' || selected === 'waiting' ? selected : 'pending';
81
+ const tasks = useApprovalTasks(view);
10
82
  const { run, busy, message, clearMessage } = useCommand();
11
83
  return (
12
84
  <>
13
- <PageHeading eyebrow="APPROVAL INBOX" title="审批待办" description="处理分配给你的审批任务。" />
85
+ <AssistantPage context={{ page: { id: 'approval-inbox', title: '审批待办', kind: 'list', route: '/approvals' },
86
+ visibleContent: [{ id: 'visible-tasks', text: tasks.data ? `${views[view]}当前显示 ${tasks.data.length} 项,以重新查询为准。` : '审批任务正在读取或暂时不可用。' }],
87
+ }} suggestions={['查看当前待我处理的审批', '通过、驳回和退回修改有什么区别?']} />
88
+ <PageHeading eyebrow="APPROVAL INBOX" title="审批待办" description="领取、处理和跟进你的审批任务。" />
14
89
  {message && <Notice onClose={clearMessage}>{message}</Notice>}
15
- <section className="panel">
90
+ <section className="panel approval-inbox">
16
91
  <div className="section-head">
17
- <h2>待你处理</h2>
18
- <button onClick={() => void tasks.refetch()}>刷新</button>
92
+ <div className="actions" aria-label="审批任务视图">
93
+ {Object.entries(views).map(([key, label]) => <button key={key} disabled={busy} aria-pressed={key === view} className={key === view ? 'primary' : ''}
94
+ onClick={() => setParams(previous => { const next = new URLSearchParams(previous); if (key === 'pending') next.delete('view'); else next.set('view', key); return next; })}>{label}</button>)}
95
+ </div>
96
+ <button disabled={busy || tasks.isFetching} onClick={() => void tasks.refetch()}>刷新</button>
19
97
  </div>
20
98
  {tasks.isError && <p className="error">{tasks.error.message}</p>}
21
- {tasks.isPending && <p className="empty">正在读取待办…</p>}
22
- {tasks.data?.map((task) => (
23
- <div className="task" key={task.id}>
24
- <div>
25
- <h3>{task.title}</h3>
26
- <p className="mono muted">PO-{task.businessKey.slice(0, 8).toUpperCase()}</p>
27
- </div>
28
- <div className="actions">
29
- <button
30
- disabled={busy}
31
- onClick={() => run(() => client.post(`/api/approval/tasks/${task.id}/reject`), '已驳回')}
32
- >
33
- 驳回
34
- </button>
35
- <button
36
- disabled={busy}
37
- className="primary"
38
- onClick={() => run(() => client.post(`/api/approval/tasks/${task.id}/approve`), '已通过审批')}
39
- >
40
- 通过审批
41
- </button>
42
- </div>
43
- </div>
44
- ))}
45
- {tasks.data?.length === 0 && (
46
- <div className="empty">
47
- <h3>待办已清空</h3>
48
- <p>新任务分配给你后会显示在这里。</p>
49
- </div>
50
- )}
99
+ {tasks.isPending && <p className="empty">正在读取任务…</p>}
100
+ {tasks.data?.map(task => <TaskRow key={`${task.id}:${task.revision}`} task={task} orderUrl={orderUrl} busy={busy} run={run} />)}
101
+ {tasks.data?.length === 0 && <div className="empty"><h3>当前没有{views[view]}的任务</h3><p>任务分配或签署顺序变化后,可刷新查看。</p></div>}
51
102
  </section>
52
103
  </>
53
104
  );
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 审批收件箱局部布局,沿用工作台配色与按钮;将意见、详情及提交分行,避免窄屏压缩链接。
3
+ * 只作用于审批模块,桌面保留事项/操作两栏,空间不足自动换行;等待阶段由组件省略操作控件。
4
+ */
5
+ .approval-inbox .task { flex-wrap: wrap; gap: 20px; }
6
+ .approval-task-summary { flex: 1 1 240px; }
7
+ .approval-task-controls { display: grid; gap: 12px; flex: 0 1 420px; }
8
+ .approval-task-controls .actions { align-items: center; flex-wrap: wrap; }
9
+ .approval-task-controls .route-link { white-space: nowrap; }
10
+ .approval-task-controls input { min-width: 180px; flex: 1; }
11
+ .approval-operation-dialog { width: min(760px, calc(100vw - 32px)); max-height: calc(100vh - 48px); overflow-y: auto; }
12
+ .approval-operation-dialog form { padding: 24px; }
13
+ .approval-operation-dialog textarea { width: 100%; }
14
+ .approval-inbox .section-head { gap: 16px; flex-wrap: wrap; }
15
+ @media (max-width: 660px) {
16
+ .approval-task-controls { flex-basis: 100%; }
17
+ .approval-inbox .task { padding: 20px; }
18
+ }
@@ -1 +1,5 @@
1
+ /**
2
+ * auth 模块的公开入口,对 app 暴露登录页。
3
+ * 外部组合通过本入口导入,页面、组件与内部 helper 的组织由模块自行维护。
4
+ */
1
5
  export { LoginPage } from './pages/login-page';
@@ -1,11 +1,23 @@
1
+ /**
2
+ * 采购应用认证页面,收集账号密码并调用契约客户端登录。
3
+ * 会话协议和当前能力同步由 SDK 负责;成功后的安全回跳交给 app 路由层。
4
+ */
1
5
  // 登录页属于认证模块;表单反馈留在模块内,会话切换由 app 处理。
2
6
  import { useState, type FormEvent } from 'react';
3
7
  import { client } from '@app/contracts';
4
8
  import { errorMessage } from '../../../shared/lib/error-message';
5
9
 
10
+ /**
11
+ * 维护提交中与失败提示,成功调用 onLogin 通知路由切换。
12
+ * 不在组件内持久化密码,不自行跳转未校验的 returnTo。
13
+ */
6
14
  export function LoginPage({ onLogin }: { onLogin: () => void }) {
7
15
  const [error, setError] = useState('');
8
16
  const [busy, setBusy] = useState(false);
17
+ /**
18
+ * 阻止浏览器原生表单跳转,读取本次输入并等待 client.login。
19
+ * 成功后由 app 进入已登录路由;错误通过统一映射显示,输入与凭证不写入日志。
20
+ */
9
21
  async function submit(event: FormEvent<HTMLFormElement>) {
10
22
  event.preventDefault();
11
23
  setBusy(true);
@@ -1 +1,5 @@
1
+ /**
2
+ * inventory 模块的公开入口,对 app 暴露库存页。
3
+ * 外部组合通过本入口导入,页面、组件与内部 helper 的组织由模块自行维护。
4
+ */
1
5
  export { InventoryPage } from './pages/inventory-page';
@@ -1,14 +1,43 @@
1
+ /**
2
+ * 库存示例页,读取库存安全列表并通过生成命令创建库存记录。
3
+ * 这只是采购范例中的库存展示与输入入口,资源占用和释放仍通过领域流程落实。
4
+ */
1
5
  // 库存模块只管理台账页面,登记物料通过生成的 Inventory.create 命令。
2
6
  import { can, inventory, useInventoryList } from '@app/contracts';
3
7
  import { useCommand } from '../../../shared/hooks/use-command';
4
8
  import { Notice } from '../../../shared/ui/notice';
5
9
  import { PageHeading } from '../../../shared/ui/page-heading';
10
+ import { QueryFailure } from '../../../shared/ui/page-state';
11
+ import { AssistantPage } from '../../../shared/ui/assistant-scope';
6
12
 
13
+ /**
14
+ * 读取最多前一百条库存并展示查询失败、加载和空结果;列表不代表系统全部库存。
15
+ * 创建成功后重置表单并刷新查询,失败保留输入。助理上下文只列当前可见记录 ID,服务端仍核验读取范围。
16
+ */
7
17
  export function InventoryPage() {
8
18
  const stock = useInventoryList({ pageSize: 100 });
9
19
  const { run, busy, message, clearMessage } = useCommand();
20
+ if (stock.isPending)
21
+ return (
22
+ <p className="empty" role="status">
23
+ 正在读取库存台账…
24
+ </p>
25
+ );
26
+ // 页面已打开后权限仍可能变化,必须处理安全接口的拒绝,不能把失败展示成空台账。
27
+ if (stock.isError)
28
+ return (
29
+ <QueryFailure
30
+ error={stock.error}
31
+ to="/workspace"
32
+ label="返回工作台"
33
+ retry={() => void stock.refetch()}
34
+ />
35
+ );
10
36
  return (
11
37
  <>
38
+ <AssistantPage context={{ page: { id: 'inventory', title: '库存台账', kind: 'list', route: '/inventory' },
39
+ view: { aggregate: 'Inventory', input: { pageSize: 100 }, visibleIds: stock.data.items.map(item => item.id) },
40
+ }} suggestions={['汇总当前可见的物料库存', '查询库存时有哪些权限限制?']} />
12
41
  <PageHeading eyebrow="INVENTORY" title="库存台账" description="查看物料与当前登记数量。" />
13
42
  {message && <Notice onClose={clearMessage}>{message}</Notice>}
14
43
  <section className="panel">
@@ -48,7 +77,6 @@ export function InventoryPage() {
48
77
  </button>
49
78
  </form>
50
79
  )}
51
- {stock.isError && <p className="error">{stock.error.message}</p>}
52
80
  <table>
53
81
  <thead>
54
82
  <tr>
@@ -0,0 +1,67 @@
1
+ /**
2
+ * 采购单审批轨迹展示,按单据与版本读取服务端整理后的轮次和步骤。
3
+ * AI 与人工状态使用同一记录来源;并行节点按服务端分支路径展示,序号仅定位节点、不代表串行先后。
4
+ * 受限详情不显示理由,不能根据缺少细节推断未执行;该组件只消费框架历史,不实现审批调度。
5
+ */
6
+ import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
7
+ import { client } from '@yss123/cage-client';
8
+ import { useApprovalMembers } from '../../../shared/ui/approval-member-picker';
9
+
10
+ /** 人工操作与引擎自动激活都来自已提交历史;未知动作保留原值,不能伪装成通过。 */
11
+ const actionLabels: Record<string, string> = { transfer: '转办', delegate: '委派协助', resolve: '归还审批人', addSign: '增加签署人', claim: '领取任务', unclaim: '释放任务', approved: '通过审批', rejected: '驳回', returned: '退回修改', cancelled: '取消流程', activated: '激活等待任务', skipped: '结束其余待办', advanced: '推进审批分支', urged: '申请人催办', reminded: '流程时限提醒' };
12
+ const positionLabels: Record<string, string> = { before: '前加签', after: '后加签', parallel: '并行加签' };
13
+
14
+ /**
15
+ * 运行、步骤与人工待办状态的展示映射,未知状态仍由页面按实际值处理。
16
+ */
17
+ const status: Record<string, string> = { queued: '待执行', running: '处理中', passed: '已通过', approved: '已通过', returned: '已退回修改', rejected: '已拒绝', manual: '待人工接管', waiting_manual: '待人工接管', waiting: '待人工审批', cancelled: '已取消', failed: '执行失败', pending: '待处理', skipped: '未执行' };
18
+ /**
19
+ * id 为采购单标识,version 使业务变更后查询重新取数,pending 决定是否每 1500ms 刷新。
20
+ * 只显示历史,不执行审批;detailRestricted 分支尊重服务端裁剪,人工接管也不等于通过。
21
+ */
22
+ export function ApprovalHistory({ id, version, pending }: { id: string; version: number; pending: boolean }) {
23
+ const history = useQuery({ queryKey: ['approval-history', id, version], queryFn: async () => {
24
+ const result = await client.approvalHistory('PurchaseOrder', id);
25
+ if (!result.ok) throw new Error(`审批记录读取失败:${result.code}`);
26
+ return result.data;
27
+ }, refetchInterval: pending ? 1500 : false });
28
+ const members = useApprovalMembers(1, true);
29
+ // 跨轮次动作按服务端游标递增分页;轮询重读已经打开的页,避免只更新首屏或丢失后续意见。
30
+ const actions = useInfiniteQuery({ queryKey: ['approval-task-history', id], initialPageParam: undefined as string | undefined,
31
+ queryFn: async ({ pageParam }) => {
32
+ const result = await client.approvalTaskHistory('po-approval', 'PurchaseOrder', id, { after: pageParam, limit: 50 });
33
+ if (!result.ok) throw new Error(`操作记录读取失败:${result.code}`);
34
+ return result.data;
35
+ }, getNextPageParam: last => last.nextCursor ?? undefined, refetchInterval: pending ? 1500 : false });
36
+ const person = (userId: string | null) => userId ? members.data?.items.find(member => member.userId === userId)?.displayName ?? userId.slice(0, 8) : '系统';
37
+ return <section className="panel approval-history"><div className="section-head"><h2>审批记录</h2><span className="muted">退回后重新提交,从第一步重新审核</span></div>
38
+ {history.isPending && <p className="muted">正在读取审核记录…</p>}
39
+ {history.isError && <p role="alert">{history.error.message}</p>}
40
+ {history.data?.length === 0 && <p className="muted">提交后按已配置流程审批,实际环节和并行分支将在此显示。</p>}
41
+ {history.data?.map((round, index) => <div className="review-round" key={round.id}><h3>第 {history.data.length - index} 轮 · {status[round.status] ?? round.status}</h3>
42
+ {(round.currentSteps?.length ?? 0) > 1 && <p className="muted">当前有 {round.currentSteps!.length} 个审批环节并行办理,全部完成后才继续。</p>}
43
+ {round.steps.map(step => <div className="review-step" key={step.index}><strong>{step.index + 1}. {step.title ?? (step.kind === 'ai' ? 'AI 材料预审' : '人工审批')} · {status[step.status] ?? step.status}</strong>
44
+ {!!step.branchPath?.length && <p className="muted">{step.branchPath.map(part => `${part.parallel} · ${part.branch}`).join(' / ')}</p>}
45
+ {step.detailRestricted ? <p>当前权限无法查看审核解释。</p> : <>{step.decision && <p>{step.decision.reason}</p>}{step.decision?.suggestions.map((text, i) => <p className="review-suggestion" key={i}>{text}</p>)}</>}
46
+ </div>)}
47
+ </div>)}
48
+ <div className="review-actions"><h3>人员流转与处理意见</h3>
49
+ {actions.isPending && <p role="status">正在读取操作记录…</p>}
50
+ {actions.isError && <p role="alert">{actions.error.message} <button onClick={() => void actions.refetch()}>重试</button></p>}
51
+ {actions.data?.pages.flatMap(page => page.items).map(item => {
52
+ const placement = typeof item.details.placement === 'string' ? positionLabels[item.details.placement] : undefined;
53
+ const targets = Array.isArray(item.details.userIds) ? item.details.userIds.filter((value): value is string => typeof value === 'string') : [];
54
+ const to = typeof item.details.to === 'string' ? item.details.to : null;
55
+ const roundIndex = history.data?.findIndex(round => round.id === item.instanceId) ?? -1;
56
+ return <article className="review-step" key={item.id}>
57
+ <strong>{person(item.actorId)} · {placement ?? actionLabels[item.action] ?? item.action}</strong>
58
+ <small className="muted">{roundIndex >= 0 ? `第 ${history.data!.length - roundIndex} 轮 · ` : ''}{new Date(item.createdAt).toLocaleString('zh-CN')}</small>
59
+ {targets.length > 0 ? <p>签署人:{targets.map(person).join('、')}</p> : to && ['transfer', 'delegate', 'resolve'].includes(item.action) ? <p>交给:{person(to)}</p> : null}
60
+ {item.reason && <p>{item.reason}</p>}
61
+ </article>;
62
+ })}
63
+ {actions.data?.pages[0].items.length === 0 && <p className="muted">尚无人工操作记录。</p>}
64
+ {actions.hasNextPage && <button disabled={actions.isFetchingNextPage} onClick={() => void actions.fetchNextPage()}>加载后续记录</button>}
65
+ </div>
66
+ </section>;
67
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * 采购详情的附件展示与草稿编辑区,将上传准备和领域关联保存明确分开。
3
+ * 读取到新版本后重新采用服务端附件列表并收起编辑,避免旧草稿覆盖已变化记录。
4
+ */
5
+ import { useEffect, useState } from 'react';
6
+ import { can, purchaseOrder, PurchaseOrderFileFields, type PurchaseOrder } from '@app/contracts';
7
+ import { AttachmentUpload, FileLink } from '../../../shared/ui/attachment-upload';
8
+ import { useCommand } from '../../../shared/hooks/use-command';
9
+
10
+ /**
11
+ * order 为已读取的采购记录,包含版本、状态和已绑定附件 ID。
12
+ * 仅草稿且具有 setAttachments 能力时显示编辑入口;保存传入 id 与 version,服务端重新校验。
13
+ * value 是临时选择列表,移除或上传不会自动修改单据;成功后的缓存刷新带回正式结果。
14
+ */
15
+ export function OrderAttachments({ order }: { order: PurchaseOrder }) {
16
+ const [editing, setEditing] = useState(false);
17
+ const [value, setValue] = useState(order.attachments);
18
+ const [uploading, setUploading] = useState(false);
19
+ const { run, busy, message } = useCommand();
20
+ useEffect(() => { setValue(order.attachments); setEditing(false); }, [order.version, order.attachments]);
21
+ return <section aria-label="采购附件">
22
+ <div className="section-head"><h2>采购附件</h2>{order.status === 'draft' && can('PurchaseOrder.setAttachments') && <button type="button" onClick={() => setEditing(!editing)}>{editing ? '收起编辑' : '编辑附件'}</button>}</div>
23
+ {editing ? <>
24
+ <AttachmentUpload command="PurchaseOrder.setAttachments" field="attachments" targetId={order.id} policy={PurchaseOrderFileFields.attachments} value={value} onChange={setValue} onBusyChange={setUploading} />
25
+ <button type="button" disabled={uploading || busy} onClick={() => run(() => purchaseOrder.setAttachments(order.id, { attachments: value }, order.version), '附件已保存')}>保存附件</button>
26
+ {message && <p role="status">{message}</p>}
27
+ </> : order.attachments.length ? <ul>{order.attachments.map(id => <li key={id}><FileLink id={id} /></li>)}</ul> : <p className="muted">暂无附件</p>}
28
+ </section>;
29
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 采购详情中的专业复核配置,申请人在草稿或退回后选择实际会签人。
3
+ * 配置通过 setReviewers 命令保存,提交后只读;页面不能更改已经生成的审批实例快照。
4
+ * 名录只用于展示与选人,服务端重查有效名录、提单人隔离、状态、版本及命令权限。
5
+ */
6
+ import { useState } from 'react';
7
+ import { can, purchaseOrder, type PurchaseOrder } from '@app/contracts';
8
+ import { useCommand } from '../../../shared/hooks/use-command';
9
+ import { ApprovalMemberPicker, useApprovalMembers } from '../../../shared/ui/approval-member-picker';
10
+ import { Notice } from '../../../shared/ui/notice';
11
+
12
+ /**
13
+ * order 为安全读取的最新单据;null 草稿表示未编辑,避免后台刷新覆盖用户尚未保存的选人。
14
+ * 保存携带当前业务版本,成功清空本地草稿并刷新缓存;失败保留选择和提示,不重试写入。
15
+ * 名录首页外或已不可读人员保留 UUID,不能把查不到名称解释为已删除或无需签署。
16
+ */
17
+ export function OrderReviewers({ order }: { order: PurchaseOrder }) {
18
+ const [draft, setDraft] = useState<{ enabled: boolean; ids: string[] } | null>(null);
19
+ const members = useApprovalMembers(1, true);
20
+ const { run, busy, message, clearMessage } = useCommand();
21
+ const editable = order.status === 'draft' && can('PurchaseOrder.setReviewers');
22
+ const selection = draft ?? { enabled: order.needsSpecialistReview ?? false, ids: order.reviewerIds ?? [] };
23
+ const names = (order.reviewerIds ?? []).map(id => members.data?.items.find(member => member.userId === id)?.displayName ?? id);
24
+ return <section className="purpose-editor">
25
+ <h2>审批安排{draft && editable && <small className="muted"> · 尚未保存</small>}</h2>
26
+ <p className="muted">{selection.enabled ? '需要所选专业人员全部签署。' : '本单不要求专业人员会签。'}具体流转与其他审批环节以提交后的流程记录为准。</p>
27
+ {message && <Notice onClose={clearMessage}>{message}</Notice>}
28
+ {editable ? <>
29
+ <label className="review-checkbox"><input type="checkbox" checked={selection.enabled} disabled={busy}
30
+ onChange={event => setDraft({ ...selection, enabled: event.target.checked })} />需要专业会签(技术、质量等)</label>
31
+ {selection.enabled && <ApprovalMemberPicker value={selection.ids} onChange={ids => setDraft({ ...selection, ids })} exclude={[order.createdBy]} disabled={busy} />}
32
+ <div className="actions"><span className="muted">退回后可重新安排,重提将开启新一轮审批。</span>
33
+ <button disabled={busy || draft === null || (selection.enabled && !selection.ids.length)} onClick={async () => {
34
+ if (await run(() => purchaseOrder.setReviewers(order.id, { needsSpecialistReview: selection.enabled, reviewerIds: selection.enabled ? selection.ids : [] }, order.version), '审批安排已保存')) setDraft(null);
35
+ }}>保存审批安排</button></div>
36
+ </> : <p>{order.needsSpecialistReview ? `专业会签:${names.join('、')}` : '无需专业会签'}</p>}
37
+ </section>;
38
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 采购模块与公共助理之间的语义适配层,提供采购字段、命令的中文展示名称。
3
+ * 页面通过 context 显式传入当前所见,不导入领域运行时或自行获取越权数据。
4
+ */
5
+ import type { PageContextInput } from '@yss123/cage-client';
6
+ import { AssistantPage } from '../../../shared/ui/assistant-scope';
7
+
8
+ // 页面只补充业务文案与上下文,工具发现、授权及命令执行全部使用框架 SDK。
9
+ /**
10
+ * 将页面快照与建议问题注册到最近的 AssistantScope,不再创建第二个面板。
11
+ * context 中的记录/草稿来源由页面负责;标签仅用于确认展示,工具与授权仍来自框架。
12
+ */
13
+ export function PurchaseAssistantContext({ context, suggestions }: { context: PageContextInput; suggestions: string[] }) {
14
+ return <AssistantPage context={context} suggestions={suggestions}
15
+ fieldLabels={{ reason: '采购用途', amount: '金额', items: '物料', key: '偏好名称', value: '偏好内容', comment: '审批意见', outcome: '审批结果' }}
16
+ commandLabels={{ 'PurchaseOrder.submit': '提交采购审批', 'PurchaseOrder.explainPurpose': '保存采购用途', 'PurchaseOrder.cancel': '取消采购单', 'PurchaseOrder.create': '新建采购单' }} />;
17
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 采购创建弹窗的高度与滚动约束。附件任务和恢复列表可能增高表单,使用动态视口高度保留边距。
3
+ * 滚动在弹窗内完成,限制滚动传播,确保保存按钮在窄屏仍可滚动到达。
4
+ */
5
+ /* 附件与恢复任务会增加表单高度;滚动留在弹窗内,保证窄屏仍可完成保存。 */
6
+ .purchase-order-dialog { max-height: calc(100dvh - 48px); overflow-y: auto; overscroll-behavior: contain; }
@@ -1,12 +1,33 @@
1
+ /**
2
+ * 采购草稿创建表单,收集物料、数量、元金额字符串、用途、电话和已上传附件。
3
+ * 创建人、部门和初始状态由领域及内核确定;浏览器表单限制只承担输入体验。
4
+ */
1
5
  // 只处理采购草稿输入;状态迁移、创建人和权限仍由领域命令决定。
2
6
  import { useState, type FormEvent } from 'react';
3
- import { purchaseOrder } from '@app/contracts';
7
+ import { purchaseOrder, PurchaseOrderFileFields, currentUser } 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';
11
+ import { ApprovalMemberPicker } from '../../../shared/ui/approval-member-picker';
5
12
 
13
+ /**
14
+ * 在弹窗中维护附件列表、上传状态和提交状态。
15
+ * close 由列表控制弹窗关闭;run 使用 useCommand 协议返回成功布尔值,失败保留输入。
16
+ * 上传中的文件不能随表单提交,上传完成仍需 create 命令正式绑定。
17
+ */
6
18
  export function PurchaseOrderForm({ close, run }: { close: () => void; run: RunCommand }) {
7
19
  const [busy, setBusy] = useState(false);
20
+ const [attachments, setAttachments] = useState<string[]>([]);
21
+ const [uploading, setUploading] = useState(false);
22
+ const [needsSpecialistReview, setNeedsSpecialistReview] = useState(false);
23
+ const [reviewerIds, setReviewerIds] = useState<string[]>([]);
24
+ /**
25
+ * 读取 FormData 并构造创建输入:金额保留字符串,数量转数值,可选空文本转 undefined。
26
+ * 等待 run 返回后解除提交态,只有成功才关闭;依赖 run 返回结果,不额外捕获任意抛出异常。
27
+ */
8
28
  async function submit(event: FormEvent<HTMLFormElement>) {
9
29
  event.preventDefault();
30
+ if (uploading || busy) return;
10
31
  setBusy(true);
11
32
  const data = new FormData(event.currentTarget);
12
33
  const ok = await run(
@@ -14,7 +35,10 @@ export function PurchaseOrderForm({ close, run }: { close: () => void; run: RunC
14
35
  purchaseOrder.create({
15
36
  amount: String(data.get('amount')),
16
37
  supplierPhone: String(data.get('phone')) || undefined,
38
+ reason: String(data.get('reason')) || undefined,
17
39
  items: [{ sku: String(data.get('sku')), qty: Number(data.get('qty')) }],
40
+ attachments,
41
+ needsSpecialistReview, reviewerIds: needsSpecialistReview ? reviewerIds : [],
18
42
  }),
19
43
  '采购单已保存为草稿',
20
44
  );
@@ -23,7 +47,7 @@ export function PurchaseOrderForm({ close, run }: { close: () => void; run: RunC
23
47
  }
24
48
  return (
25
49
  <div className="modal-backdrop">
26
- <section role="dialog" aria-modal="true" aria-labelledby="new-order-title" className="modal">
50
+ <section role="dialog" aria-modal="true" aria-labelledby="new-order-title" className="modal purchase-order-dialog">
27
51
  <div className="section-head">
28
52
  <h2 id="new-order-title">新建采购单</h2>
29
53
  <button onClick={close} aria-label="关闭">
@@ -50,7 +74,12 @@ export function PurchaseOrderForm({ close, run }: { close: () => void; run: RunC
50
74
  <input name="phone" type="tel" maxLength={32} />
51
75
  </label>
52
76
  <p className="muted">保存后可在列表提交审批,创建人和部门会自动填写。</p>
53
- <button className="primary" disabled={busy}>
77
+ <label>采购用途与必要性<textarea name="reason" rows={3} maxLength={2000} placeholder="说明具体用途;提交后先进行 AI 材料预审" /></label>
78
+ <label className="review-checkbox"><input type="checkbox" checked={needsSpecialistReview} onChange={event => setNeedsSpecialistReview(event.target.checked)} />需要专业会签(技术、质量等)</label>
79
+ {needsSpecialistReview && <ApprovalMemberPicker value={reviewerIds} onChange={setReviewerIds} exclude={currentUser() ? [currentUser()!.id] : []} disabled={busy} />}
80
+ <p className="muted">{needsSpecialistReview ? '所选专业人员需要全部签署。' : '本次不要求专业会签。'}提交后可在审批记录查看实际环节和办理进度。</p>
81
+ <AttachmentUpload command="PurchaseOrder.create" field="attachments" policy={PurchaseOrderFileFields.attachments} value={attachments} onChange={setAttachments} onBusyChange={setUploading} />
82
+ <button className="primary" disabled={busy || uploading || (needsSpecialistReview && reviewerIds.length === 0)}>
54
83
  {busy ? '保存中…' : '保存草稿'}
55
84
  </button>
56
85
  </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,17 @@
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; }
16
+ .review-actions { margin-top: 24px; border-top: 1px solid var(--line, #e4e8df); padding-top: 12px; }
17
+ .review-actions small { display: block; }