@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,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,32 @@
1
+ /**
2
+ * 采购单审批轨迹展示,按单据与版本读取服务端整理后的轮次和步骤。
3
+ * AI 与人工状态使用同一记录来源;受限详情不显示理由,不能根据缺少细节推断未执行。
4
+ */
5
+ import { useQuery } from '@tanstack/react-query';
6
+ import { client } from '@yss123/cage-client';
7
+
8
+ /**
9
+ * 运行、步骤与人工待办状态的展示映射,未知状态仍由页面按实际值处理。
10
+ */
11
+ const status: Record<string, string> = { queued: '待执行', running: '处理中', passed: '已通过', approved: '已通过', returned: '已退回修改', rejected: '已拒绝', manual: '待人工接管', waiting_manual: '待人工接管', waiting: '待人工审批', cancelled: '已取消', failed: '执行失败', pending: '待处理', skipped: '未执行' };
12
+ /**
13
+ * id 为采购单标识,version 使业务变更后查询重新取数,pending 决定是否每 1500ms 刷新。
14
+ * 只显示历史,不执行审批;detailRestricted 分支尊重服务端裁剪,人工接管也不等于通过。
15
+ */
16
+ export function ApprovalHistory({ id, version, pending }: { id: string; version: number; pending: boolean }) {
17
+ const history = useQuery({ queryKey: ['approval-history', id, version], queryFn: async () => {
18
+ const result = await client.approvalHistory('PurchaseOrder', id);
19
+ if (!result.ok) throw new Error(`审批记录读取失败:${result.code}`);
20
+ return result.data;
21
+ }, refetchInterval: pending ? 1500 : false });
22
+ return <section className="panel approval-history"><div className="section-head"><h2>审批记录</h2><span className="muted">退回后重新提交,从第一步重新审核</span></div>
23
+ {history.isPending && <p className="muted">正在读取审核记录…</p>}
24
+ {history.isError && <p role="alert">{history.error.message}</p>}
25
+ {history.data?.length === 0 && <p className="muted">提交后先进入 AI 材料预审,再按流程交由人工审批。</p>}
26
+ {history.data?.map((round, index) => <div className="review-round" key={round.id}><h3>第 {history.data.length - index} 轮 · {status[round.status] ?? round.status}</h3>
27
+ {round.steps.map(step => <div className="review-step" key={step.index}><strong>{step.index + 1}. {step.kind === 'ai' ? 'AI 材料预审' : '人工审批'} · {status[step.status] ?? step.status}</strong>
28
+ {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>)}</>}
29
+ </div>)}
30
+ </div>)}
31
+ </section>;
32
+ }
@@ -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,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,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
+ }