@rytass/bpm-core-react 0.16.2 → 0.17.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 (247) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +54 -6
  3. package/dist/chunks/admin-pickers-BRHk3qKq.cjs +2 -0
  4. package/dist/chunks/admin-pickers-BRHk3qKq.cjs.map +1 -0
  5. package/dist/chunks/{admin-pickers-DxhzECkD.js → admin-pickers-Djq3h-UR.js} +41 -39
  6. package/dist/chunks/admin-pickers-Djq3h-UR.js.map +1 -0
  7. package/dist/chunks/{categories-DjENouRm.cjs → categories-AxxhqjLZ.cjs} +2 -2
  8. package/dist/chunks/{categories-DjENouRm.cjs.map → categories-AxxhqjLZ.cjs.map} +1 -1
  9. package/dist/chunks/{categories-1gKxeEqw.js → categories-riC33J_j.js} +2 -2
  10. package/dist/chunks/{categories-1gKxeEqw.js.map → categories-riC33J_j.js.map} +1 -1
  11. package/dist/chunks/{compose-DUH9cQ-L.cjs → compose-B7TtrpQJ.cjs} +2 -2
  12. package/dist/chunks/{compose-DUH9cQ-L.cjs.map → compose-B7TtrpQJ.cjs.map} +1 -1
  13. package/dist/chunks/{compose-DO6n0NJi.js → compose-_pREPtfN.js} +2 -2
  14. package/dist/chunks/{compose-DO6n0NJi.js.map → compose-_pREPtfN.js.map} +1 -1
  15. package/dist/chunks/{delegations-BLzYnOae.cjs → delegations-V9HIe3_P.cjs} +2 -2
  16. package/dist/chunks/delegations-V9HIe3_P.cjs.map +1 -0
  17. package/dist/chunks/{delegations-_b4_xBit.js → delegations-xUt1tj8d.js} +2 -1
  18. package/dist/chunks/{delegations-_b4_xBit.js.map → delegations-xUt1tj8d.js.map} +1 -1
  19. package/dist/chunks/{designer-B-cfiob9.js → designer-BiywgNXs.js} +1003 -949
  20. package/dist/chunks/designer-BiywgNXs.js.map +1 -0
  21. package/dist/chunks/designer-ttAqv859.cjs +65 -0
  22. package/dist/chunks/designer-ttAqv859.cjs.map +1 -0
  23. package/dist/chunks/detail-DRCTnuKm.cjs +2 -0
  24. package/dist/chunks/detail-DRCTnuKm.cjs.map +1 -0
  25. package/dist/chunks/{detail-C6NdVijG.js → detail-DScLf42P.js} +183 -156
  26. package/dist/chunks/detail-DScLf42P.js.map +1 -0
  27. package/dist/chunks/orgs-C8usknWb.cjs +2 -0
  28. package/dist/chunks/orgs-C8usknWb.cjs.map +1 -0
  29. package/dist/chunks/{orgs-Bpp1U60k.js → orgs-Lg7tXv2u.js} +584 -544
  30. package/dist/chunks/orgs-Lg7tXv2u.js.map +1 -0
  31. package/dist/chunks/users-DRbuDYF1.js +422 -0
  32. package/dist/chunks/users-DRbuDYF1.js.map +1 -0
  33. package/dist/chunks/users-DVQicZIo.cjs +2 -0
  34. package/dist/chunks/users-DVQicZIo.cjs.map +1 -0
  35. package/dist/components/admin-pickers.d.cts +65 -0
  36. package/dist/components/approval-instance-list-page.d.cts +17 -0
  37. package/dist/components/bpm-form-field.d.cts +10 -0
  38. package/dist/components/bpm-notification-bell-button.d.cts +22 -0
  39. package/dist/components/dashboard-page.d.cts +7 -0
  40. package/dist/components/notification-drawer.d.cts +11 -0
  41. package/dist/components/org-unit-tree-draft-editor.d.cts +24 -0
  42. package/dist/components/org-unit-tree-draft-editor.d.ts +1 -1
  43. package/dist/components/pdf-preview.d.cts +12 -0
  44. package/dist/index.cjs +1 -1
  45. package/dist/index.d.cts +15 -0
  46. package/dist/index.d.ts +15 -15
  47. package/dist/index.js +1 -1
  48. package/dist/lib/auth-provider.d.cts +37 -0
  49. package/dist/lib/format-date-time.d.cts +5 -0
  50. package/dist/lib/format-member-label.d.cts +12 -0
  51. package/dist/lib/member-profile-resolution.d.cts +48 -0
  52. package/dist/lib/notification-drawer-provider.d.cts +27 -0
  53. package/dist/lib/notification-unread-provider.d.cts +25 -0
  54. package/dist/lib/org-tree-draft.d.cts +27 -0
  55. package/dist/lib/providers.d.cts +31 -0
  56. package/dist/lib/router-adapter.d.cts +47 -0
  57. package/dist/lib/routes-config.d.cts +117 -0
  58. package/dist/lib/task-node-label.d.cts +10 -0
  59. package/dist/lib/use-bpm-logout.d.cts +12 -0
  60. package/dist/lib/use-bpm-member.d.cts +17 -0
  61. package/dist/next/BPMNextProviders.d.cts +51 -0
  62. package/dist/next/index.d.cts +2 -0
  63. package/dist/next/index.d.ts +2 -2
  64. package/dist/next/workflow-chat-route.d.cts +17 -0
  65. package/dist/pages/admin/delegations/index.cjs +1 -1
  66. package/dist/pages/admin/delegations/index.d.cts +4 -0
  67. package/dist/pages/admin/delegations/index.js +1 -1
  68. package/dist/pages/admin/orgs/index.cjs +1 -1
  69. package/dist/pages/admin/orgs/index.d.cts +4 -0
  70. package/dist/pages/admin/orgs/index.js +1 -1
  71. package/dist/pages/admin/users/index.cjs +1 -1
  72. package/dist/pages/admin/users/index.d.cts +4 -0
  73. package/dist/pages/admin/users/index.js +1 -1
  74. package/dist/pages/admin/webhook-endpoints/index.d.cts +4 -0
  75. package/dist/pages/cc/index.d.cts +16 -0
  76. package/dist/pages/dashboard/index.d.cts +16 -0
  77. package/dist/pages/delegations/index.d.cts +4 -0
  78. package/dist/pages/inbox/index.d.cts +16 -0
  79. package/dist/pages/instances/detail/index.cjs +1 -1
  80. package/dist/pages/instances/detail/index.d.cts +23 -0
  81. package/dist/pages/instances/detail/index.js +1 -1
  82. package/dist/pages/instances/new/index.d.cts +20 -0
  83. package/dist/pages/login/index.d.cts +21 -0
  84. package/dist/pages/root/index.d.cts +18 -0
  85. package/dist/pages/search/index.d.cts +16 -0
  86. package/dist/pages/sent/index.d.cts +16 -0
  87. package/dist/pages/settings/notifications/index.d.cts +4 -0
  88. package/dist/pages/templates/categories/index.cjs +1 -1
  89. package/dist/pages/templates/categories/index.d.cts +4 -0
  90. package/dist/pages/templates/categories/index.js +1 -1
  91. package/dist/pages/templates/compose/index.cjs +1 -1
  92. package/dist/pages/templates/compose/index.d.cts +25 -0
  93. package/dist/pages/templates/compose/index.js +1 -1
  94. package/dist/pages/templates/designer/index.cjs +1 -1
  95. package/dist/pages/templates/designer/index.d.cts +8 -0
  96. package/dist/pages/templates/designer/index.js +1 -1
  97. package/dist/pages/templates/index.d.cts +4 -0
  98. package/dist/pages/templates/versions/index.d.cts +8 -0
  99. package/dist/views/admin/delegations/AdminDelegationsView.d.cts +2 -0
  100. package/dist/views/admin/delegations/index.cjs +1 -1
  101. package/dist/views/admin/delegations/index.d.cts +1 -0
  102. package/dist/views/admin/delegations/index.d.ts +1 -1
  103. package/dist/views/admin/delegations/index.js +1 -1
  104. package/dist/views/admin/index.cjs +1 -1
  105. package/dist/views/admin/index.d.cts +4 -0
  106. package/dist/views/admin/index.d.ts +4 -4
  107. package/dist/views/admin/index.js +3 -3
  108. package/dist/views/admin/orgs/AdminOrgsView.d.cts +2 -0
  109. package/dist/views/admin/orgs/index.cjs +1 -1
  110. package/dist/views/admin/orgs/index.d.cts +1 -0
  111. package/dist/views/admin/orgs/index.d.ts +1 -1
  112. package/dist/views/admin/orgs/index.js +1 -1
  113. package/dist/views/admin/users/AdminUsersView.d.cts +2 -0
  114. package/dist/views/admin/users/MemberTaskReassignModal.d.cts +13 -0
  115. package/dist/views/admin/users/MemberTaskReassignModal.d.ts +13 -0
  116. package/dist/views/admin/users/index.cjs +1 -1
  117. package/dist/views/admin/users/index.d.cts +1 -0
  118. package/dist/views/admin/users/index.d.ts +1 -1
  119. package/dist/views/admin/users/index.js +1 -1
  120. package/dist/views/admin/webhook-endpoints/AdminWebhookEndpointsView.d.cts +8 -0
  121. package/dist/views/admin/webhook-endpoints/index.d.cts +1 -0
  122. package/dist/views/admin/webhook-endpoints/index.d.ts +1 -1
  123. package/dist/views/cc/CcView.d.cts +7 -0
  124. package/dist/views/cc/index.d.cts +1 -0
  125. package/dist/views/cc/index.d.ts +1 -1
  126. package/dist/views/dashboard/DashboardView.d.cts +7 -0
  127. package/dist/views/dashboard/index.d.cts +1 -0
  128. package/dist/views/dashboard/index.d.ts +1 -1
  129. package/dist/views/delegations/DelegationsView.d.cts +2 -0
  130. package/dist/views/delegations/index.d.cts +1 -0
  131. package/dist/views/delegations/index.d.ts +1 -1
  132. package/dist/views/forms/builder/FormBuilderView.d.cts +18 -0
  133. package/dist/views/forms/builder/index.d.cts +1 -0
  134. package/dist/views/forms/builder/index.d.ts +1 -1
  135. package/dist/views/forms/builder/json-code-editor.d.cts +11 -0
  136. package/dist/views/forms/renderer/FormRendererView.d.cts +24 -0
  137. package/dist/views/forms/renderer/FormRendererView.d.ts +1 -1
  138. package/dist/views/forms/renderer/form-data-source-field.d.cts +65 -0
  139. package/dist/views/forms/renderer/form-field-link.d.cts +16 -0
  140. package/dist/views/forms/renderer/index.d.cts +2 -0
  141. package/dist/views/forms/renderer/index.d.ts +2 -2
  142. package/dist/views/forms/renderer/member-field-options.d.cts +39 -0
  143. package/dist/views/forms/renderer/member-field.d.cts +21 -0
  144. package/dist/views/inbox/InboxView.d.cts +7 -0
  145. package/dist/views/inbox/index.d.cts +1 -0
  146. package/dist/views/inbox/index.d.ts +1 -1
  147. package/dist/views/instances/detail/InstanceDetailView.d.cts +27 -0
  148. package/dist/views/instances/detail/index.cjs +1 -1
  149. package/dist/views/instances/detail/index.d.cts +7 -0
  150. package/dist/views/instances/detail/index.d.ts +7 -7
  151. package/dist/views/instances/detail/index.js +1 -1
  152. package/dist/views/instances/detail/sections/InstanceAttachmentsSection.d.cts +15 -0
  153. package/dist/views/instances/detail/sections/InstanceFormSection.d.cts +36 -0
  154. package/dist/views/instances/detail/sections/InstanceFormSection.d.ts +1 -1
  155. package/dist/views/instances/detail/sections/InstanceHistorySection.d.cts +29 -0
  156. package/dist/views/instances/detail/sections/InstanceSignaturesSection.d.cts +14 -0
  157. package/dist/views/instances/detail/sections/InstanceTasksSection.d.cts +55 -0
  158. package/dist/views/instances/detail/sections/InstanceWebhookDeliveriesSection.d.cts +20 -0
  159. package/dist/views/instances/detail/sections/container-helpers.d.cts +10 -0
  160. package/dist/views/instances/detail/sections/container-helpers.d.ts +1 -1
  161. package/dist/views/instances/detail/sections/shared.d.cts +141 -0
  162. package/dist/views/instances/detail/submitted-by.d.cts +23 -0
  163. package/dist/views/instances/index.d.cts +1 -0
  164. package/dist/views/instances/index.d.ts +1 -1
  165. package/dist/views/instances/new/InstanceNewView.d.cts +15 -0
  166. package/dist/views/instances/new/index.d.cts +1 -0
  167. package/dist/views/instances/new/index.d.ts +1 -1
  168. package/dist/views/login/LoginView.d.cts +47 -0
  169. package/dist/views/login/index.d.cts +1 -0
  170. package/dist/views/login/index.d.ts +1 -1
  171. package/dist/views/root/RootView.d.cts +10 -0
  172. package/dist/views/root/index.d.cts +1 -0
  173. package/dist/views/root/index.d.ts +1 -1
  174. package/dist/views/search/SearchView.d.cts +7 -0
  175. package/dist/views/search/index.d.cts +1 -0
  176. package/dist/views/search/index.d.ts +1 -1
  177. package/dist/views/sent/SentView.d.cts +7 -0
  178. package/dist/views/sent/index.d.cts +1 -0
  179. package/dist/views/sent/index.d.ts +1 -1
  180. package/dist/views/settings/index.d.cts +1 -0
  181. package/dist/views/settings/index.d.ts +1 -1
  182. package/dist/views/settings/notifications/SettingsNotificationsView.d.cts +2 -0
  183. package/dist/views/settings/notifications/index.d.cts +1 -0
  184. package/dist/views/settings/notifications/index.d.ts +1 -1
  185. package/dist/views/templates/TemplatesView.d.cts +7 -0
  186. package/dist/views/templates/categories/TemplateCategoriesView.d.cts +2 -0
  187. package/dist/views/templates/categories/index.cjs +1 -1
  188. package/dist/views/templates/categories/index.d.cts +1 -0
  189. package/dist/views/templates/categories/index.d.ts +1 -1
  190. package/dist/views/templates/categories/index.js +1 -1
  191. package/dist/views/templates/compose/TemplateComposeWizardView.d.cts +21 -0
  192. package/dist/views/templates/compose/index.cjs +1 -1
  193. package/dist/views/templates/compose/index.d.cts +2 -0
  194. package/dist/views/templates/compose/index.d.ts +2 -2
  195. package/dist/views/templates/compose/index.js +1 -1
  196. package/dist/views/templates/compose/steps/ComposeFormStep.d.cts +15 -0
  197. package/dist/views/templates/compose/steps/ComposeReviewStep.d.cts +12 -0
  198. package/dist/views/templates/compose/steps/ComposeWorkflowStep.d.cts +22 -0
  199. package/dist/views/templates/compose/use-template-compose-wizard.d.cts +46 -0
  200. package/dist/views/templates/designer/NotifyWebhookTargetsEditor.d.cts +20 -0
  201. package/dist/views/templates/designer/NotifyWebhookTargetsEditor.d.ts +1 -1
  202. package/dist/views/templates/designer/TemplateDesignerView.d.cts +65 -0
  203. package/dist/views/templates/designer/chrome-workflow-chat.d.cts +12 -0
  204. package/dist/views/templates/designer/chrome-workflow-chat.d.ts +1 -1
  205. package/dist/views/templates/designer/decision-policy-options.d.cts +8 -0
  206. package/dist/views/templates/designer/designer-version-labels.d.cts +12 -0
  207. package/dist/views/templates/designer/dry-run-assignee-label.d.cts +28 -0
  208. package/dist/views/templates/designer/dry-run-sample-data.d.cts +9 -0
  209. package/dist/views/templates/designer/dynamic-form-resolver-options.d.cts +35 -0
  210. package/dist/views/templates/designer/index.cjs +1 -1
  211. package/dist/views/templates/designer/index.d.cts +1 -0
  212. package/dist/views/templates/designer/index.d.ts +1 -1
  213. package/dist/views/templates/designer/index.js +1 -1
  214. package/dist/views/templates/designer/notify-webhook-designer.d.cts +74 -0
  215. package/dist/views/templates/designer/position-level-options.d.cts +17 -0
  216. package/dist/views/templates/designer/use-workflow-chat.d.cts +21 -0
  217. package/dist/views/templates/designer/use-workflow-chat.d.ts +1 -1
  218. package/dist/views/templates/designer/use-workflow-designer-controller.d.cts +41 -0
  219. package/dist/views/templates/designer/workflow-chat-drawer.d.cts +16 -0
  220. package/dist/views/templates/designer/workflow-chat-drawer.d.ts +1 -1
  221. package/dist/views/templates/index.cjs +1 -1
  222. package/dist/views/templates/index.d.cts +3 -0
  223. package/dist/views/templates/index.d.ts +3 -3
  224. package/dist/views/templates/index.js +1 -1
  225. package/dist/views/templates/versions/TemplateVersionsView.d.cts +5 -0
  226. package/dist/views/templates/versions/index.d.cts +1 -0
  227. package/dist/views/templates/versions/index.d.ts +1 -1
  228. package/dist/views/workflow/index.d.cts +4 -0
  229. package/dist/views/workflow/index.d.ts +4 -4
  230. package/package.json +387 -147
  231. package/dist/chunks/admin-pickers-DxhzECkD.js.map +0 -1
  232. package/dist/chunks/admin-pickers-qe3j--gX.cjs +0 -2
  233. package/dist/chunks/admin-pickers-qe3j--gX.cjs.map +0 -1
  234. package/dist/chunks/delegations-BLzYnOae.cjs.map +0 -1
  235. package/dist/chunks/designer-2YvjP_GU.cjs +0 -65
  236. package/dist/chunks/designer-2YvjP_GU.cjs.map +0 -1
  237. package/dist/chunks/designer-B-cfiob9.js.map +0 -1
  238. package/dist/chunks/detail-C6NdVijG.js.map +0 -1
  239. package/dist/chunks/detail-DKobK4Cg.cjs +0 -2
  240. package/dist/chunks/detail-DKobK4Cg.cjs.map +0 -1
  241. package/dist/chunks/orgs-Bpp1U60k.js.map +0 -1
  242. package/dist/chunks/orgs-ywaE1_02.cjs +0 -2
  243. package/dist/chunks/orgs-ywaE1_02.cjs.map +0 -1
  244. package/dist/chunks/users-BSVo_eNe.js +0 -215
  245. package/dist/chunks/users-BSVo_eNe.js.map +0 -1
  246. package/dist/chunks/users-DFSBTDMy.cjs +0 -2
  247. package/dist/chunks/users-DFSBTDMy.cjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,64 @@
1
+ ## 0.17.1 (2026-10-16)
2
+
3
+ ### 🩹 Fixes
4
+
5
+ - **react:** give the require condition CommonJS declarations ([ab0121b](https://github.com/Rytass/BPMCore/commit/ab0121b))
6
+ - **template:** say a category in use cannot be deleted ([ab4236b](https://github.com/Rytass/BPMCore/commit/ab4236b))
7
+ - **react:** keep org unit and position picker menus on screen ([bb5cbaa](https://github.com/Rytass/BPMCore/commit/bb5cbaa))
8
+ - **react:** fully specify relative imports in emitted declarations ([022b3e2](https://github.com/Rytass/BPMCore/commit/022b3e2))
9
+
10
+ ### ❤️ Thank You
11
+
12
+ - Chia Yu Pai @fantasywind
13
+
14
+ ## 0.17.0 (2026-10-15)
15
+
16
+ ### 🚀 Features
17
+
18
+ - ⚠️ **workflow-engine:** end SLA-terminated instances as EXPIRED ([7dcca4e](https://github.com/Rytass/BPMCore/commit/7dcca4e))
19
+ - **template:** allow lastDecision in edge conditions ([e8b4788](https://github.com/Rytass/BPMCore/commit/e8b4788))
20
+ - **react:** add member task reassignment modal to admin users ([f12e9f8](https://github.com/Rytass/BPMCore/commit/f12e9f8))
21
+ - **workflow-engine:** add administrator in-place task reassignment ([02ce920](https://github.com/Rytass/BPMCore/commit/02ce920))
22
+ - **template-designer:** let the fallback mode pick a position ([257a361](https://github.com/Rytass/BPMCore/commit/257a361))
23
+ - **views:** ask for a successor unit before retiring an org unit ([b79cd5c](https://github.com/Rytass/BPMCore/commit/b79cd5c))
24
+ - ⚠️ **organization:** retire org units into a successor unit ([514e0b2](https://github.com/Rytass/BPMCore/commit/514e0b2))
25
+
26
+ ### 🩹 Fixes
27
+
28
+ - **migrations:** check pg_trgm with the other required extensions ([30a1e58](https://github.com/Rytass/BPMCore/commit/30a1e58))
29
+ - **template:** check CEL roots per call site on publish ([868d236](https://github.com/Rytass/BPMCore/commit/868d236))
30
+ - **instance-detail:** show how a closed instance ended ([a110e08](https://github.com/Rytass/BPMCore/commit/a110e08))
31
+ - ⚠️ **delegation:** order rules by priority, higher first ([96cca55](https://github.com/Rytass/BPMCore/commit/96cca55))
32
+ - **react:** drop a late member task listing in the reassignment modal ([1a7724e](https://github.com/Rytass/BPMCore/commit/1a7724e))
33
+ - **react:** keep reassigned task titles and fit the result table ([3b90530](https://github.com/Rytass/BPMCore/commit/3b90530))
34
+ - **template-designer:** keep unknown approver fallbacks read-only ([5ea88fe](https://github.com/Rytass/BPMCore/commit/5ea88fe))
35
+
36
+ ### ⚠️ Breaking Changes
37
+
38
+ - **workflow-engine:** end SLA-terminated instances as EXPIRED ([7dcca4e](https://github.com/Rytass/BPMCore/commit/7dcca4e))
39
+ an instance ended by SLA `TERMINATE_INSTANCE` is now
40
+ `EXPIRED` instead of `CANCELLED`, and its activity log is
41
+ `INSTANCE_EXPIRED` with a null actor instead of `INSTANCE_CANCELLED` in
42
+ the initiator's name. Instances terminated before this change stay
43
+ CANCELLED. A host implementing `BPMWorkflowEngineService` itself must add
44
+ `expireApprovalInstance`.
45
+ - **delegation:** order rules by priority, higher first ([96cca55](https://github.com/Rytass/BPMCore/commit/96cca55))
46
+ when several active delegation rules of one principal
47
+ match a task, the rule with the highest priority now applies instead of
48
+ the oldest. No data migration is needed: priority had no effect before,
49
+ but principals with overlapping rules may now be delegated differently,
50
+ so review their priorities.
51
+ - **organization:** retire org units into a successor unit ([514e0b2](https://github.com/Rytass/BPMCore/commit/514e0b2))
52
+ deleteOrgUnit now also refuses a unit that has no open
53
+ memberships but still has an open ORG_UNIT-scoped manager rule; it used to
54
+ retire it and leave the rule silently dormant. Pass transferToOrgUnitId (or
55
+ end the rule first) to retire such a unit. The refusal message keeps its
56
+ leading "Org unit with memberships cannot be deleted" sentence.
57
+
58
+ ### ❤️ Thank You
59
+
60
+ - Chia Yu Pai @fantasywind
61
+
1
62
  ## 0.16.2 (2026-10-14)
2
63
 
3
64
  ### 🚀 Features
package/README.md CHANGED
@@ -17,13 +17,23 @@ network query.
17
17
  The controlled `FormBuilderView` loads only the host-registered DataSource
18
18
  Catalog. It filters sources by control capability, supports typed field and
19
19
  constant parameter bindings, preserves bindings when a field key is renamed,
20
- and asks for confirmation before source/mode changes or removing a field used
21
- by another dynamic field. The editor can send the complete schema to the
20
+ and asks for confirmation before switching the selection mode, replacing an
21
+ already-configured DataSource, or removing a field used by another dynamic
22
+ field. Picking a source applies at once; switching away from a configured
23
+ source drops its bindings. The editor can send the complete schema to the
22
24
  server-side lint contract before publishing.
23
25
 
24
26
  ## Status
25
27
 
26
- `0.4.0` (breaking) — drops the bundled navigation shell. BPM views no longer wrap themselves in an `<AppLayout>` / Mezzanine `<Navigation>`; the host owns the layout chrome and mounts BPM views inside its own sidebar / top bar. New host-facing widgets ship in the root barrel: `useBPMMember`, `useBPMLogout`, `<BPMNotificationBellButton />`. See `CHANGELOG.md` for the migration walkthrough, and `docs/integration-guide.md` for a host integration recipe.
28
+ The current version is in `package.json` and `CHANGELOG.md` (the packages
29
+ share one version number under `nx release`).
30
+
31
+ Version 0.4.0 was a breaking release that dropped the bundled navigation shell:
32
+ BPM views no longer wrap themselves in an `<AppLayout>` / Mezzanine `<Navigation>`,
33
+ and the host owns the layout chrome. The 0.4.0 entry in `CHANGELOG.md` has the
34
+ migration walkthrough. Host-facing widgets in the root barrel are `useBPMMember`,
35
+ `useBPMLogout`, and `<BPMNotificationBellButton />`. For a host integration
36
+ recipe, see `docs/integration-guide.md`.
27
37
 
28
38
  ## Install
29
39
 
@@ -31,11 +41,35 @@ server-side lint contract before publishing.
31
41
  pnpm add @rytass/bpm-core-react @rytass/bpm-core-client @rytass/bpm-core-shared @mezzanine-ui/react @mezzanine-ui/icons
32
42
  ```
33
43
 
34
- Peer requirements: React 18+, Mezzanine UI 1.1+. Next.js is required only when consuming the `pages/*` subpath; framework-agnostic consumers can use `views/*` directly with their own router adapter.
44
+ Required peers: `react` / `react-dom` 18+, `@mezzanine-ui/react`, `@mezzanine-ui/core`
45
+ and `@mezzanine-ui/icons` `>=1.0.0` (this workspace develops against Mezzanine
46
+ 1.5.1, pinned in the root `package.json`), plus `@rytass/bpm-core-client` and
47
+ `@rytass/bpm-core-shared` at the same version. Every other peer is declared
48
+ optional in `package.json` → `peerDependenciesMeta` and is needed only by the
49
+ feature that imports it:
50
+
51
+ - `next` (>=15) — the `pages/*` and `next/*` subpaths; framework-agnostic
52
+ consumers can use `views/*` directly with their own router adapter.
53
+ - `@xyflow/react`, `dagre` — the flow designer and flow diagrams.
54
+ - `@hello-pangea/dnd` — drag-and-drop in the form builder.
55
+ - `@uiw/react-codemirror`, `@codemirror/view`, `@codemirror/lang-json` — JSON editors.
56
+ - `react-pdf` (>=10), `pdfjs-dist` (`^5.4.296 || ^6.3.289`) — PDF preview.
57
+ - `ai` (>=6), `@ai-sdk/react`, `@ai-sdk/openai` — the designer AI assistant.
58
+
59
+ `package.json` is the authoritative list.
60
+
61
+ ### Next.js + pnpm setup (conditional)
35
62
 
36
- ### Next.js + pnpm setup
63
+ `transpilePackages` is **not required by default**. The published packages ship
64
+ compiled `dist/` output with `exports` maps. This repository's `apps/client`
65
+ does not set `transpilePackages` (`apps/client/next.config.js`) and imports the
66
+ libraries through tsconfig path aliases to their source.
37
67
 
38
- If your host uses Next.js (15+) with pnpm strict mode, add the package to `transpilePackages` in `next.config.js`. Without this, Next's Turbopack cannot resolve transitive peer-dep imports such as `@rytass/bpm-core-client/workflow` from inside pnpm-isolated `node_modules/.pnpm/...` paths.
68
+ Add the setting only if your build reports an unresolved transitive import
69
+ under pnpm strict mode, for example `@rytass/bpm-core-client/workflow` imported
70
+ from inside `node_modules/.pnpm/...`. This README has **not** reproduced that
71
+ failure against the published packages; treat the snippet below as the workaround
72
+ to try in that case, not as a requirement.
39
73
 
40
74
  ```js
41
75
  /** @type {import('next').NextConfig} */
@@ -54,6 +88,20 @@ module.exports = {
54
88
  };
55
89
  ```
56
90
 
91
+ ### Designer feature flags (Next.js host env)
92
+
93
+ The designer page shim (`pages/templates/designer`) reads these server-side
94
+ environment variables. Set them in the host's `.env.local` or deployment config.
95
+
96
+ - `BPM_AI_ASSISTANT_ENABLED=true`: shows the designer AI assistant. It is hidden
97
+ by default. It also needs `OPENAI_API_KEY` (server-only); without the key the
98
+ toggle shows as a disabled placeholder.
99
+ - `BPM_TEMPLATE_DRY_RUN_ENABLED=false`: hides the 試跑流程 (dry-run) button. The
100
+ button is shown by default; only the exact value `'false'` hides it.
101
+
102
+ For direct control, `TemplateDesignerView` accepts `showAiAssistant`,
103
+ `aiAssistantAvailable`, and `showDryRun` props.
104
+
57
105
  ## Usage
58
106
 
59
107
  ### Host integration shape
@@ -0,0 +1,2 @@
1
+ "use client";require('../admin-pickers.css');const e=require("./format-member-label-t7ANTF62.cjs");let t=require("react"),n=require("@mezzanine-ui/react"),r=require("@rytass/bpm-core-client"),i=require("react/jsx-runtime");var a={pickerHost:`bpm_pickerHost_Sw6gz`},o=320;function s({disabled:e=!1,name:o,onChange:s,placeholder:c=`搜尋姓名或信箱`,size:l,value:d}){let[f,m]=(0,t.useState)(!1),[h,v]=(0,t.useState)(d?[d]:[]);async function y(e){m(!0);try{v((await(0,r.searchMembers)(e)).map(u))}finally{m(!1)}}return(0,i.jsx)(`div`,{className:a.pickerHost,children:(0,i.jsx)(n.AutoComplete,{asyncData:!0,disabled:e,disabledOptionsFilter:!0,emptyText:`沒有符合的會員`,inputProps:{autoCapitalize:`none`,autoCorrect:`off`,name:o,spellCheck:!1},loading:f,loadingText:`搜尋會員中...`,mode:`single`,onChange:e=>s(p(e)),onSearch:y,onSearchTextChange:e=>s(g(e,h)),onVisibilityChange:e=>{e&&y(``)},options:[..._(d?[d]:[],h)],placeholder:c,searchDebounceTime:300,size:l,value:d})})}function c({disabled:e=!1,onChange:r,orgUnits:s,placeholder:c=`選擇組織`,size:l,value:u}){let f=(0,t.useMemo)(()=>s.map(d),[s]);return(0,i.jsx)(`div`,{className:a.pickerHost,children:(0,i.jsx)(n.Select,{clearable:!0,disabled:e,flip:!0,fullWidth:!0,menuMaxHeight:o,onChange:e=>r(m(e)),options:[...f],placeholder:c,size:l,value:u})})}function l({disabled:e=!1,onChange:r,placeholder:s=`選擇職位`,positions:c,size:l,value:u}){let d=(0,t.useMemo)(()=>c.map(f),[c]);return(0,i.jsx)(`div`,{className:a.pickerHost,children:(0,i.jsx)(n.Select,{clearable:!0,disabled:e,flip:!0,fullWidth:!0,menuMaxHeight:o,onChange:e=>r(h(e)),options:[...d],placeholder:s,size:l,value:u})})}function u(t){return{email:t.email,id:t.memberId,name:e.t(t.name,t.email,`dot`)}}function d(e){return{id:e.id,name:`${e.name} · ${e.code}`}}function f(e){return{id:e.id,name:`${e.name} · ${e.code}`}}function p(e){if(!v(e))return null;let t=e.email,n=e.id,r=e.name;return typeof t==`string`&&typeof n==`string`&&typeof r==`string`?{email:t,id:n,name:r}:null}function m(e){if(!v(e))return null;let t=e.id,n=e.name;return typeof t==`string`&&typeof n==`string`?{id:t,name:n}:null}function h(e){if(!v(e))return null;let t=e.id,n=e.name;return typeof t==`string`&&typeof n==`string`?{id:t,name:n}:null}function g(e,t){let n=e.trim().toLocaleLowerCase();if(!n)return null;let r=t.filter(e=>e.id.toLocaleLowerCase()===n||e.email.toLocaleLowerCase()===n||e.name.toLocaleLowerCase()===n);return r.length===1?r[0]:null}function _(e,t){return[...new Map([...e,...t].map(e=>[e.id,e])).values()]}function v(e){return typeof e==`object`&&!!e}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return s}});
2
+ //# sourceMappingURL=admin-pickers-BRHk3qKq.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-pickers-BRHk3qKq.cjs","names":["useState","searchMembers","styles","AutoComplete","useMemo","Select","formatMemberLabel","require_format_member_label","react","_mezzanine_ui_react","_rytass_bpm_core_client","react_jsx_runtime","admin_pickers_module_default","PICKER_MENU_MAX_HEIGHT","MemberPicker","loading","setLoading","options","setOptions","value","handleSearch","searchText","readMemberOption","onChange","readMemberOptionFromValue","option","readUniqueOption","open","mergeMemberOptions","OrgUnitPicker","orgUnits","readOrgUnitOption","readOrgUnitOptionFromValue","PositionPicker","positions","readPositionOption","readPositionOptionFromValue","member","orgUnit","position","isRecord","email","id","name","normalized","matched","selected"],"sources":["../../src/components/admin-pickers.module.scss","../../src/components/admin-pickers.tsx"],"sourcesContent":[".pickerHost {\n width: 100%;\n min-width: 0;\n\n :global(.mzn-auto-complete),\n :global(.mzn-input),\n :global(.mzn-input-container),\n :global(.mzn-select),\n :global(.mzn-select-trigger),\n :global(.mzn-text-field) {\n width: 100%;\n min-width: 0 !important;\n }\n}\n","'use client';\n\nimport { useMemo, useState, type ReactElement } from 'react';\nimport { AutoComplete, Select } from '@mezzanine-ui/react';\nimport { searchMembers } from '@rytass/bpm-core-client';\nimport type {\n OrgUnitRecord,\n PositionRecord,\n} from '@rytass/bpm-core-client/organization';\nimport { formatMemberLabel } from '../lib/format-member-label';\nimport styles from './admin-pickers.module.scss';\n\nexport type MemberOption = Readonly<{\n email: string;\n id: string;\n name: string;\n}>;\n\nexport type OrgUnitOption = Readonly<{\n id: string;\n name: string;\n}>;\n\nexport type PositionOption = Readonly<{\n id: string;\n name: string;\n}>;\n\n// Without a cap the Mezzanine dropdown grows with its options and never\n// mounts its scrollbar, so a long org-unit or position list runs off the\n// viewport with no way to reach the items below the fold. The pickers also\n// set `flip`: near the bottom of a modal the capped menu still overflows\n// unless it may open upwards.\nconst PICKER_MENU_MAX_HEIGHT = 320;\n\ninterface MemberPickerProps {\n readonly disabled?: boolean;\n readonly name: string;\n readonly onChange: (option: MemberOption | null) => void;\n readonly placeholder?: string;\n readonly size?: 'main' | 'sub';\n readonly value: MemberOption | null;\n}\n\ninterface OrgUnitPickerProps {\n readonly disabled?: boolean;\n readonly name: string;\n readonly onChange: (option: OrgUnitOption | null) => void;\n readonly orgUnits: readonly OrgUnitRecord[];\n readonly placeholder?: string;\n readonly size?: 'main' | 'sub';\n readonly value: OrgUnitOption | null;\n}\n\ninterface PositionPickerProps {\n readonly disabled?: boolean;\n readonly name: string;\n readonly onChange: (option: PositionOption | null) => void;\n readonly placeholder?: string;\n readonly positions: readonly PositionRecord[];\n readonly size?: 'main' | 'sub';\n readonly value: PositionOption | null;\n}\n\n/**\n * AutoComplete-backed picker that searches the BPM member directory via\n * `searchMembers()`. Emits a {@link MemberOption} `{ id, name, email }`\n * on selection (or `null` when cleared).\n */\nexport function MemberPicker({\n disabled = false,\n name,\n onChange,\n placeholder = '搜尋姓名或信箱',\n size,\n value,\n}: MemberPickerProps): ReactElement {\n const [loading, setLoading] = useState(false);\n const [options, setOptions] = useState<readonly MemberOption[]>(\n value ? [value] : [],\n );\n\n async function handleSearch(searchText: string): Promise<void> {\n setLoading(true);\n try {\n setOptions((await searchMembers(searchText)).map(readMemberOption));\n } finally {\n setLoading(false);\n }\n }\n\n return (\n <div className={styles.pickerHost}>\n <AutoComplete\n asyncData\n disabled={disabled}\n disabledOptionsFilter\n emptyText=\"沒有符合的會員\"\n inputProps={{\n autoCapitalize: 'none',\n autoCorrect: 'off',\n name,\n spellCheck: false,\n }}\n loading={loading}\n loadingText=\"搜尋會員中...\"\n mode=\"single\"\n onChange={(option): void => onChange(readMemberOptionFromValue(option))}\n onSearch={handleSearch}\n onSearchTextChange={(searchText): void =>\n onChange(readUniqueOption(searchText, options))\n }\n onVisibilityChange={(open): void => {\n if (open) {\n void handleSearch('');\n }\n }}\n options={[...mergeMemberOptions(value ? [value] : [], options)]}\n placeholder={placeholder}\n searchDebounceTime={300}\n size={size}\n value={value}\n />\n </div>\n );\n}\n\n/**\n * Static `<Select>` over a pre-fetched list of org units. Caller is\n * responsible for fetching the org units (typically via\n * `readOrganizationDashboard` from `@rytass/bpm-core-client/organization`).\n */\nexport function OrgUnitPicker({\n disabled = false,\n onChange,\n orgUnits,\n placeholder = '選擇組織',\n size,\n value,\n}: OrgUnitPickerProps): ReactElement {\n const options = useMemo(\n (): readonly OrgUnitOption[] => orgUnits.map(readOrgUnitOption),\n [orgUnits],\n );\n return (\n <div className={styles.pickerHost}>\n <Select\n clearable\n disabled={disabled}\n flip\n fullWidth\n menuMaxHeight={PICKER_MENU_MAX_HEIGHT}\n onChange={(option): void => onChange(readOrgUnitOptionFromValue(option))}\n options={[...options]}\n placeholder={placeholder}\n size={size}\n value={value}\n />\n </div>\n );\n}\n\n/**\n * Static `<Select>` over a pre-fetched list of positions.\n */\nexport function PositionPicker({\n disabled = false,\n onChange,\n placeholder = '選擇職位',\n positions,\n size,\n value,\n}: PositionPickerProps): ReactElement {\n const options = useMemo(\n (): readonly PositionOption[] => positions.map(readPositionOption),\n [positions],\n );\n return (\n <div className={styles.pickerHost}>\n <Select\n clearable\n disabled={disabled}\n flip\n fullWidth\n menuMaxHeight={PICKER_MENU_MAX_HEIGHT}\n onChange={(option): void => onChange(readPositionOptionFromValue(option))}\n options={[...options]}\n placeholder={placeholder}\n size={size}\n value={value}\n />\n </div>\n );\n}\n\nexport function readMemberOption(member: {\n readonly email: string;\n readonly memberId: string;\n readonly name: string;\n}): MemberOption {\n return {\n email: member.email,\n id: member.memberId,\n name: formatMemberLabel(member.name, member.email, 'dot'),\n };\n}\n\nexport function readOrgUnitOption(orgUnit: OrgUnitRecord): OrgUnitOption {\n return { id: orgUnit.id, name: `${orgUnit.name} · ${orgUnit.code}` };\n}\n\nexport function readPositionOption(position: PositionRecord): PositionOption {\n return { id: position.id, name: `${position.name} · ${position.code}` };\n}\n\nfunction readMemberOptionFromValue(value: unknown): MemberOption | null {\n if (!isRecord(value)) return null;\n const email = value.email;\n const id = value.id;\n const name = value.name;\n return typeof email === 'string' &&\n typeof id === 'string' &&\n typeof name === 'string'\n ? { email, id, name }\n : null;\n}\n\nfunction readOrgUnitOptionFromValue(value: unknown): OrgUnitOption | null {\n if (!isRecord(value)) return null;\n const id = value.id;\n const name = value.name;\n return typeof id === 'string' && typeof name === 'string'\n ? { id, name }\n : null;\n}\n\nfunction readPositionOptionFromValue(value: unknown): PositionOption | null {\n if (!isRecord(value)) return null;\n const id = value.id;\n const name = value.name;\n return typeof id === 'string' && typeof name === 'string'\n ? { id, name }\n : null;\n}\n\nfunction readUniqueOption(\n searchText: string,\n options: readonly MemberOption[],\n): MemberOption | null {\n const normalized = searchText.trim().toLocaleLowerCase();\n if (!normalized) return null;\n const matched = options.filter(\n (option) =>\n option.id.toLocaleLowerCase() === normalized ||\n option.email.toLocaleLowerCase() === normalized ||\n option.name.toLocaleLowerCase() === normalized,\n );\n return matched.length === 1 ? matched[0] : null;\n}\n\nfunction mergeMemberOptions(\n selected: readonly MemberOption[],\n options: readonly MemberOption[],\n): readonly MemberOption[] {\n const byId = new Map(\n [...selected, ...options].map(\n (option): readonly [string, MemberOption] => [option.id, option],\n ),\n );\n return [...byId.values()];\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null;\n}\n"],"mappings":"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"}
@@ -4,15 +4,13 @@ import { useMemo as t, useState as n } from "react";
4
4
  import { AutoComplete as r, Select as i } from "@mezzanine-ui/react";
5
5
  import { searchMembers as a } from "@rytass/bpm-core-client";
6
6
  import { jsx as o } from "react/jsx-runtime";
7
- import '../admin-pickers.css';var s = { pickerHost: "bpm_pickerHost_Sw6gz" };
8
- //#endregion
9
- //#region src/components/admin-pickers.tsx
10
- function c({ disabled: e = !1, name: t, onChange: i, placeholder: c = "搜尋姓名或信箱", size: l, value: u }) {
11
- let [f, p] = n(!1), [h, g] = n(u ? [u] : []);
12
- async function y(e) {
7
+ import '../admin-pickers.css';var s = { pickerHost: "bpm_pickerHost_Sw6gz" }, c = 320;
8
+ function l({ disabled: e = !1, name: t, onChange: i, placeholder: c = "搜尋姓名或信箱", size: l, value: u }) {
9
+ let [d, p] = n(!1), [m, g] = n(u ? [u] : []);
10
+ async function _(e) {
13
11
  p(!0);
14
12
  try {
15
- g((await a(e)).map(d));
13
+ g((await a(e)).map(f));
16
14
  } finally {
17
15
  p(!1);
18
16
  }
@@ -30,16 +28,16 @@ function c({ disabled: e = !1, name: t, onChange: i, placeholder: c = "搜尋姓
30
28
  name: t,
31
29
  spellCheck: !1
32
30
  },
33
- loading: f,
31
+ loading: d,
34
32
  loadingText: "搜尋會員中...",
35
33
  mode: "single",
36
- onChange: (e) => i(m(e)),
37
- onSearch: y,
38
- onSearchTextChange: (e) => i(_(e, h)),
34
+ onChange: (e) => i(h(e)),
35
+ onSearch: _,
36
+ onSearchTextChange: (e) => i(v(e, m)),
39
37
  onVisibilityChange: (e) => {
40
- e && y("");
38
+ e && _("");
41
39
  },
42
- options: [...v(u ? [u] : [], h)],
40
+ options: [...y(u ? [u] : [], m)],
43
41
  placeholder: c,
44
42
  searchDebounceTime: 300,
45
43
  size: l,
@@ -47,59 +45,63 @@ function c({ disabled: e = !1, name: t, onChange: i, placeholder: c = "搜尋姓
47
45
  })
48
46
  });
49
47
  }
50
- function l({ disabled: e = !1, onChange: n, orgUnits: r, placeholder: a = "選擇組織", size: c, value: l }) {
51
- let u = t(() => r.map(f), [r]);
48
+ function u({ disabled: e = !1, onChange: n, orgUnits: r, placeholder: a = "選擇組織", size: l, value: u }) {
49
+ let d = t(() => r.map(p), [r]);
52
50
  return /* @__PURE__ */ o("div", {
53
51
  className: s.pickerHost,
54
52
  children: /* @__PURE__ */ o(i, {
55
53
  clearable: !0,
56
54
  disabled: e,
55
+ flip: !0,
57
56
  fullWidth: !0,
58
- onChange: (e) => n(h(e)),
59
- options: [...u],
57
+ menuMaxHeight: c,
58
+ onChange: (e) => n(g(e)),
59
+ options: [...d],
60
60
  placeholder: a,
61
- size: c,
62
- value: l
61
+ size: l,
62
+ value: u
63
63
  })
64
64
  });
65
65
  }
66
- function u({ disabled: e = !1, onChange: n, placeholder: r = "選擇職位", positions: a, size: c, value: l }) {
67
- let u = t(() => a.map(p), [a]);
66
+ function d({ disabled: e = !1, onChange: n, placeholder: r = "選擇職位", positions: a, size: l, value: u }) {
67
+ let d = t(() => a.map(m), [a]);
68
68
  return /* @__PURE__ */ o("div", {
69
69
  className: s.pickerHost,
70
70
  children: /* @__PURE__ */ o(i, {
71
71
  clearable: !0,
72
72
  disabled: e,
73
+ flip: !0,
73
74
  fullWidth: !0,
74
- onChange: (e) => n(g(e)),
75
- options: [...u],
75
+ menuMaxHeight: c,
76
+ onChange: (e) => n(_(e)),
77
+ options: [...d],
76
78
  placeholder: r,
77
- size: c,
78
- value: l
79
+ size: l,
80
+ value: u
79
81
  })
80
82
  });
81
83
  }
82
- function d(t) {
84
+ function f(t) {
83
85
  return {
84
86
  email: t.email,
85
87
  id: t.memberId,
86
88
  name: e(t.name, t.email, "dot")
87
89
  };
88
90
  }
89
- function f(e) {
91
+ function p(e) {
90
92
  return {
91
93
  id: e.id,
92
94
  name: `${e.name} · ${e.code}`
93
95
  };
94
96
  }
95
- function p(e) {
97
+ function m(e) {
96
98
  return {
97
99
  id: e.id,
98
100
  name: `${e.name} · ${e.code}`
99
101
  };
100
102
  }
101
- function m(e) {
102
- if (!y(e)) return null;
103
+ function h(e) {
104
+ if (!b(e)) return null;
103
105
  let t = e.email, n = e.id, r = e.name;
104
106
  return typeof t == "string" && typeof n == "string" && typeof r == "string" ? {
105
107
  email: t,
@@ -107,35 +109,35 @@ function m(e) {
107
109
  name: r
108
110
  } : null;
109
111
  }
110
- function h(e) {
111
- if (!y(e)) return null;
112
+ function g(e) {
113
+ if (!b(e)) return null;
112
114
  let t = e.id, n = e.name;
113
115
  return typeof t == "string" && typeof n == "string" ? {
114
116
  id: t,
115
117
  name: n
116
118
  } : null;
117
119
  }
118
- function g(e) {
119
- if (!y(e)) return null;
120
+ function _(e) {
121
+ if (!b(e)) return null;
120
122
  let t = e.id, n = e.name;
121
123
  return typeof t == "string" && typeof n == "string" ? {
122
124
  id: t,
123
125
  name: n
124
126
  } : null;
125
127
  }
126
- function _(e, t) {
128
+ function v(e, t) {
127
129
  let n = e.trim().toLocaleLowerCase();
128
130
  if (!n) return null;
129
131
  let r = t.filter((e) => e.id.toLocaleLowerCase() === n || e.email.toLocaleLowerCase() === n || e.name.toLocaleLowerCase() === n);
130
132
  return r.length === 1 ? r[0] : null;
131
133
  }
132
- function v(e, t) {
134
+ function y(e, t) {
133
135
  return [...new Map([...e, ...t].map((e) => [e.id, e])).values()];
134
136
  }
135
- function y(e) {
137
+ function b(e) {
136
138
  return typeof e == "object" && !!e;
137
139
  }
138
140
  //#endregion
139
- export { f as a, d as i, l as n, p as o, u as r, c as t };
141
+ export { p as a, f as i, u as n, m as o, d as r, l as t };
140
142
 
141
- //# sourceMappingURL=admin-pickers-DxhzECkD.js.map
143
+ //# sourceMappingURL=admin-pickers-Djq3h-UR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-pickers-Djq3h-UR.js","names":["styles","formatMemberLabel","admin_pickers_module_default","PICKER_MENU_MAX_HEIGHT","MemberPicker","loading","setLoading","useState","options","setOptions","value","handleSearch","searchMembers","searchText","readMemberOption","AutoComplete","onChange","readMemberOptionFromValue","option","readUniqueOption","open","mergeMemberOptions","OrgUnitPicker","useMemo","orgUnits","readOrgUnitOption","Select","readOrgUnitOptionFromValue","PositionPicker","positions","readPositionOption","readPositionOptionFromValue","member","orgUnit","position","isRecord","email","id","name","normalized","matched","selected"],"sources":["../../src/components/admin-pickers.module.scss","../../src/components/admin-pickers.tsx"],"sourcesContent":[".pickerHost {\n width: 100%;\n min-width: 0;\n\n :global(.mzn-auto-complete),\n :global(.mzn-input),\n :global(.mzn-input-container),\n :global(.mzn-select),\n :global(.mzn-select-trigger),\n :global(.mzn-text-field) {\n width: 100%;\n min-width: 0 !important;\n }\n}\n","'use client';\n\nimport { useMemo, useState, type ReactElement } from 'react';\nimport { AutoComplete, Select } from '@mezzanine-ui/react';\nimport { searchMembers } from '@rytass/bpm-core-client';\nimport type {\n OrgUnitRecord,\n PositionRecord,\n} from '@rytass/bpm-core-client/organization';\nimport { formatMemberLabel } from '../lib/format-member-label';\nimport styles from './admin-pickers.module.scss';\n\nexport type MemberOption = Readonly<{\n email: string;\n id: string;\n name: string;\n}>;\n\nexport type OrgUnitOption = Readonly<{\n id: string;\n name: string;\n}>;\n\nexport type PositionOption = Readonly<{\n id: string;\n name: string;\n}>;\n\n// Without a cap the Mezzanine dropdown grows with its options and never\n// mounts its scrollbar, so a long org-unit or position list runs off the\n// viewport with no way to reach the items below the fold. The pickers also\n// set `flip`: near the bottom of a modal the capped menu still overflows\n// unless it may open upwards.\nconst PICKER_MENU_MAX_HEIGHT = 320;\n\ninterface MemberPickerProps {\n readonly disabled?: boolean;\n readonly name: string;\n readonly onChange: (option: MemberOption | null) => void;\n readonly placeholder?: string;\n readonly size?: 'main' | 'sub';\n readonly value: MemberOption | null;\n}\n\ninterface OrgUnitPickerProps {\n readonly disabled?: boolean;\n readonly name: string;\n readonly onChange: (option: OrgUnitOption | null) => void;\n readonly orgUnits: readonly OrgUnitRecord[];\n readonly placeholder?: string;\n readonly size?: 'main' | 'sub';\n readonly value: OrgUnitOption | null;\n}\n\ninterface PositionPickerProps {\n readonly disabled?: boolean;\n readonly name: string;\n readonly onChange: (option: PositionOption | null) => void;\n readonly placeholder?: string;\n readonly positions: readonly PositionRecord[];\n readonly size?: 'main' | 'sub';\n readonly value: PositionOption | null;\n}\n\n/**\n * AutoComplete-backed picker that searches the BPM member directory via\n * `searchMembers()`. Emits a {@link MemberOption} `{ id, name, email }`\n * on selection (or `null` when cleared).\n */\nexport function MemberPicker({\n disabled = false,\n name,\n onChange,\n placeholder = '搜尋姓名或信箱',\n size,\n value,\n}: MemberPickerProps): ReactElement {\n const [loading, setLoading] = useState(false);\n const [options, setOptions] = useState<readonly MemberOption[]>(\n value ? [value] : [],\n );\n\n async function handleSearch(searchText: string): Promise<void> {\n setLoading(true);\n try {\n setOptions((await searchMembers(searchText)).map(readMemberOption));\n } finally {\n setLoading(false);\n }\n }\n\n return (\n <div className={styles.pickerHost}>\n <AutoComplete\n asyncData\n disabled={disabled}\n disabledOptionsFilter\n emptyText=\"沒有符合的會員\"\n inputProps={{\n autoCapitalize: 'none',\n autoCorrect: 'off',\n name,\n spellCheck: false,\n }}\n loading={loading}\n loadingText=\"搜尋會員中...\"\n mode=\"single\"\n onChange={(option): void => onChange(readMemberOptionFromValue(option))}\n onSearch={handleSearch}\n onSearchTextChange={(searchText): void =>\n onChange(readUniqueOption(searchText, options))\n }\n onVisibilityChange={(open): void => {\n if (open) {\n void handleSearch('');\n }\n }}\n options={[...mergeMemberOptions(value ? [value] : [], options)]}\n placeholder={placeholder}\n searchDebounceTime={300}\n size={size}\n value={value}\n />\n </div>\n );\n}\n\n/**\n * Static `<Select>` over a pre-fetched list of org units. Caller is\n * responsible for fetching the org units (typically via\n * `readOrganizationDashboard` from `@rytass/bpm-core-client/organization`).\n */\nexport function OrgUnitPicker({\n disabled = false,\n onChange,\n orgUnits,\n placeholder = '選擇組織',\n size,\n value,\n}: OrgUnitPickerProps): ReactElement {\n const options = useMemo(\n (): readonly OrgUnitOption[] => orgUnits.map(readOrgUnitOption),\n [orgUnits],\n );\n return (\n <div className={styles.pickerHost}>\n <Select\n clearable\n disabled={disabled}\n flip\n fullWidth\n menuMaxHeight={PICKER_MENU_MAX_HEIGHT}\n onChange={(option): void => onChange(readOrgUnitOptionFromValue(option))}\n options={[...options]}\n placeholder={placeholder}\n size={size}\n value={value}\n />\n </div>\n );\n}\n\n/**\n * Static `<Select>` over a pre-fetched list of positions.\n */\nexport function PositionPicker({\n disabled = false,\n onChange,\n placeholder = '選擇職位',\n positions,\n size,\n value,\n}: PositionPickerProps): ReactElement {\n const options = useMemo(\n (): readonly PositionOption[] => positions.map(readPositionOption),\n [positions],\n );\n return (\n <div className={styles.pickerHost}>\n <Select\n clearable\n disabled={disabled}\n flip\n fullWidth\n menuMaxHeight={PICKER_MENU_MAX_HEIGHT}\n onChange={(option): void => onChange(readPositionOptionFromValue(option))}\n options={[...options]}\n placeholder={placeholder}\n size={size}\n value={value}\n />\n </div>\n );\n}\n\nexport function readMemberOption(member: {\n readonly email: string;\n readonly memberId: string;\n readonly name: string;\n}): MemberOption {\n return {\n email: member.email,\n id: member.memberId,\n name: formatMemberLabel(member.name, member.email, 'dot'),\n };\n}\n\nexport function readOrgUnitOption(orgUnit: OrgUnitRecord): OrgUnitOption {\n return { id: orgUnit.id, name: `${orgUnit.name} · ${orgUnit.code}` };\n}\n\nexport function readPositionOption(position: PositionRecord): PositionOption {\n return { id: position.id, name: `${position.name} · ${position.code}` };\n}\n\nfunction readMemberOptionFromValue(value: unknown): MemberOption | null {\n if (!isRecord(value)) return null;\n const email = value.email;\n const id = value.id;\n const name = value.name;\n return typeof email === 'string' &&\n typeof id === 'string' &&\n typeof name === 'string'\n ? { email, id, name }\n : null;\n}\n\nfunction readOrgUnitOptionFromValue(value: unknown): OrgUnitOption | null {\n if (!isRecord(value)) return null;\n const id = value.id;\n const name = value.name;\n return typeof id === 'string' && typeof name === 'string'\n ? { id, name }\n : null;\n}\n\nfunction readPositionOptionFromValue(value: unknown): PositionOption | null {\n if (!isRecord(value)) return null;\n const id = value.id;\n const name = value.name;\n return typeof id === 'string' && typeof name === 'string'\n ? { id, name }\n : null;\n}\n\nfunction readUniqueOption(\n searchText: string,\n options: readonly MemberOption[],\n): MemberOption | null {\n const normalized = searchText.trim().toLocaleLowerCase();\n if (!normalized) return null;\n const matched = options.filter(\n (option) =>\n option.id.toLocaleLowerCase() === normalized ||\n option.email.toLocaleLowerCase() === normalized ||\n option.name.toLocaleLowerCase() === normalized,\n );\n return matched.length === 1 ? matched[0] : null;\n}\n\nfunction mergeMemberOptions(\n selected: readonly MemberOption[],\n options: readonly MemberOption[],\n): readonly MemberOption[] {\n const byId = new Map(\n [...selected, ...options].map(\n (option): readonly [string, MemberOption] => [option.id, option],\n ),\n );\n return [...byId.values()];\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";require('../categories.css');const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./format-date-time-D-cx5M8z.cjs"),n=require("./bpm-form-field-Bc6k4ZEO.cjs");let r=require("react"),i=require("@mezzanine-ui/react"),a=require("react/jsx-runtime"),o=require("@mezzanine-ui/core/form"),s=require("@mezzanine-ui/react/ContentHeader");s=e.t(s,1);let c=require("@mezzanine-ui/icons"),l=require("@rytass/bpm-core-client/template");var u={templateFilterArea:`bpm_templateFilterArea_nUL-R`,templateModalFields:`bpm_templateModalFields_hUxWp`},d=[10,20,50],f=[{key:`ALL`,label:`全部`},{key:`ACTIVE`,label:`啟用`},{key:`INACTIVE`,label:`停用`}];function p(){let[e,n]=(0,r.useState)([]),[p,y]=(0,r.useState)(1),[b,x]=(0,r.useState)(10),[S,C]=(0,r.useState)(`ALL`),[w,T]=(0,r.useState)(0),[E,D]=(0,r.useState)(null),[O,k]=(0,r.useState)(null),[A,j]=(0,r.useState)(!0),[M,N]=(0,r.useState)(null),[P,F]=(0,r.useState)(!1),[I,L]=(0,r.useState)(``),R=(0,r.useCallback)(async()=>{j(!0),k(null);try{let e=await(0,l.listApprovalTemplateCategoriesPage)({page:p,pageSize:b,searchText:I,status:_(S)});n(e.categories),T(e.totalCount)}catch(e){k(v(e))}finally{j(!1)}},[p,b,S,I]);(0,r.useEffect)(()=>{R()},[R]);let z=(0,r.useMemo)(()=>e.map(e=>({...e,key:e.id,updatedAtLabel:t.t(e.updatedAt)})),[e]),B=(0,r.useMemo)(()=>[{dataIndex:`name`,key:`name`,title:`分類名稱`,width:180},{key:`status`,render:e=>(0,a.jsx)(h,{isActive:e.isActive}),title:`狀態`,width:120},{dataIndex:`sortOrder`,key:`sortOrder`,title:`排序`,width:100},{key:`description`,render:e=>(0,a.jsx)(i.Typography,{component:`span`,variant:`body`,children:e.description||`無`}),title:`說明`,width:260},{dataIndex:`updatedAtLabel`,key:`updatedAtLabel`,title:`更新時間`,width:220}],[]);async function V(e){F(!0),k(null);try{M?.type===`EDIT`&&M.record?await(0,l.updateApprovalTemplateCategory)({...e,id:M.record.id}):await(0,l.createApprovalTemplateCategory)(e),N(null),await R()}catch(e){throw k(v(e)),e}finally{F(!1)}}async function H(e){F(!0),k(null);try{await(0,l.updateApprovalTemplateCategory)({description:e.description,id:e.id,isActive:!e.isActive,name:e.name,sortOrder:e.sortOrder}),await R()}catch(e){k(v(e))}finally{F(!1)}}async function U(){if(E){F(!0),k(null);try{await(0,l.deleteApprovalTemplateCategory)(E.id),D(null),await R()}catch(e){k(v(e))}finally{F(!1)}}}let W=(0,r.useMemo)(()=>({render:e=>[{name:`編輯`,onClick:()=>N({record:e,type:`EDIT`})},{name:e.isActive?`停用`:`啟用`,onClick:()=>void H(e),variant:e.isActive?`destructive-secondary`:`base-secondary`},{name:`刪除`,onClick:()=>D({id:e.id,name:e.name}),variant:`destructive-secondary`}],variant:`base-secondary`,width:192}),[R]);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(i.PageHeader,{children:(0,a.jsx)(s.default,{description:`維護簽核模板分類,供模板建立、篩選與列表標示使用。`,title:`簽核模板分類`,children:(0,a.jsx)(i.Button,{icon:c.PlusIcon,iconType:`leading`,onClick:()=>N({record:null,type:`CREATE`}),variant:`base-primary`,children:`建立分類`})})}),(0,a.jsx)(i.SectionGroup,{children:(0,a.jsxs)(i.Section,{filterArea:(0,a.jsx)(i.FilterArea,{className:u.templateFilterArea,size:`sub`,children:(0,a.jsx)(i.FilterLine,{children:(0,a.jsx)(i.Filter,{span:3,children:(0,a.jsx)(i.FormField,{fullWidth:!0,layout:o.FormFieldLayout.VERTICAL,name:`categorySearchText`,children:(0,a.jsx)(i.Input,{fullWidth:!0,onChange:e=>{L(e.target.value),y(1)},placeholder:`關鍵字:搜尋分類名稱或說明`,size:`sub`,value:I,variant:`base`})})})})}),tab:(0,a.jsx)(i.Tab,{activeKey:S,onChange:e=>{C(g(e)),y(1)},children:f.map(e=>(0,a.jsx)(i.TabItem,{children:e.label},e.key))}),children:[O?(0,a.jsx)(i.Typography,{color:`text-error`,variant:`body`,children:O}):null,(0,a.jsx)(i.Table,{actions:W,columns:B,dataSource:z,fullWidth:!0,loading:A||P,pagination:{current:p,onChange:e=>{y(e)},onChangePageSize:e=>{y(1),x(e)},pageSize:b,pageSizeLabel:`每頁筆數`,pageSizeOptions:d,renderResultSummary:(e,t,n)=>`顯示 ${e}-${t} 筆,共 ${n} 筆`,showPageSizeOptions:!0,total:w}})]})})]}),(0,a.jsx)(m,{loading:P,modal:M,onClose:()=>N(null),onSubmit:V}),(0,a.jsx)(i.Modal,{cancelText:`取消`,confirmButtonProps:{variant:`destructive-primary`},confirmText:`刪除`,loading:P,modalStatusType:`error`,modalType:`standard`,onCancel:()=>D(null),onClose:()=>D(null),onConfirm:()=>void U(),open:!!E,showModalFooter:!0,showModalHeader:!0,size:`regular`,supportingText:`若分類已被模板使用,系統會改為停用分類並保留既有模板關聯。`,title:`刪除分類`,children:(0,a.jsxs)(i.Typography,{color:`text-neutral`,variant:`body`,children:[`確定要刪除「`,E?.name??``,`」嗎?`]})})]})}function m({loading:e,modal:t,onClose:o,onSubmit:s}){let[c,l]=(0,r.useState)(``),[d,f]=(0,r.useState)(null),[p,m]=(0,r.useState)(``),[h,g]=(0,r.useState)(`0`);(0,r.useEffect)(()=>{t&&(l(t.record?.description??``),f(null),m(t.record?.name??``),g(String(t.record?.sortOrder??0)))},[t]);async function _(){let e=p.trim(),n=Number(h);if(!e)f(`請輸入分類名稱`);else if(!Number.isInteger(n))f(`排序必須是整數`);else try{await s({description:c.trim()||null,isActive:t?.record?.isActive??!0,name:e,sortOrder:n})}catch(e){f(v(e))}}return(0,a.jsxs)(i.Modal,{cancelText:`取消`,confirmButtonProps:{disabled:!p.trim()},confirmText:t?.type===`EDIT`?`儲存`:`建立`,loading:e,modalType:`standard`,onCancel:o,onClose:o,onConfirm:()=>void _(),open:!!t,showModalFooter:!0,showModalHeader:!0,size:`regular`,title:t?.type===`EDIT`?`編輯分類`:`建立分類`,children:[(0,a.jsxs)(`div`,{className:u.templateModalFields,children:[(0,a.jsx)(n.t,{label:`分類名稱`,name:`categoryName`,required:!0,children:(0,a.jsx)(i.Input,{autoFocus:!0,fullWidth:!0,onChange:e=>{m(e.target.value),f(null)},placeholder:`例如:行政管理`,value:p,variant:`base`})}),(0,a.jsx)(n.t,{label:`排序`,name:`categorySortOrder`,children:(0,a.jsx)(i.Input,{fullWidth:!0,onChange:e=>{g(e.target.value),f(null)},placeholder:`0`,value:h,variant:`base`})}),(0,a.jsx)(n.t,{label:`說明`,name:`categoryDescription`,children:(0,a.jsx)(i.Textarea,{onChange:e=>{l(e.target.value),f(null)},placeholder:`補充分類用途`,value:c})})]}),d?(0,a.jsx)(i.Typography,{color:`text-error`,variant:`body`,children:d}):null]})}function h({isActive:e}){return e?(0,a.jsx)(i.Badge,{size:`sub`,text:`啟用`,variant:`dot-success`}):(0,a.jsx)(i.Badge,{size:`sub`,text:`停用`,variant:`dot-inactive`})}function g(e){return e===`ACTIVE`||e===`INACTIVE`?e:`ALL`}function _(e){return e}function v(e){return e instanceof Error?e.message:`發生未知錯誤`}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return p}});
2
- //# sourceMappingURL=categories-DjENouRm.cjs.map
1
+ "use client";require('../categories.css');const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./format-date-time-D-cx5M8z.cjs"),n=require("./bpm-form-field-Bc6k4ZEO.cjs");let r=require("react"),i=require("@mezzanine-ui/react"),a=require("react/jsx-runtime"),o=require("@mezzanine-ui/core/form"),s=require("@mezzanine-ui/react/ContentHeader");s=e.t(s,1);let c=require("@mezzanine-ui/icons"),l=require("@rytass/bpm-core-client/template");var u={templateFilterArea:`bpm_templateFilterArea_nUL-R`,templateModalFields:`bpm_templateModalFields_hUxWp`},d=[10,20,50],f=[{key:`ALL`,label:`全部`},{key:`ACTIVE`,label:`啟用`},{key:`INACTIVE`,label:`停用`}];function p(){let[e,n]=(0,r.useState)([]),[p,y]=(0,r.useState)(1),[b,x]=(0,r.useState)(10),[S,C]=(0,r.useState)(`ALL`),[w,T]=(0,r.useState)(0),[E,D]=(0,r.useState)(null),[O,k]=(0,r.useState)(null),[A,j]=(0,r.useState)(!0),[M,N]=(0,r.useState)(null),[P,F]=(0,r.useState)(!1),[I,L]=(0,r.useState)(``),R=(0,r.useCallback)(async()=>{j(!0),k(null);try{let e=await(0,l.listApprovalTemplateCategoriesPage)({page:p,pageSize:b,searchText:I,status:_(S)});n(e.categories),T(e.totalCount)}catch(e){k(v(e))}finally{j(!1)}},[p,b,S,I]);(0,r.useEffect)(()=>{R()},[R]);let z=(0,r.useMemo)(()=>e.map(e=>({...e,key:e.id,updatedAtLabel:t.t(e.updatedAt)})),[e]),B=(0,r.useMemo)(()=>[{dataIndex:`name`,key:`name`,title:`分類名稱`,width:180},{key:`status`,render:e=>(0,a.jsx)(h,{isActive:e.isActive}),title:`狀態`,width:120},{dataIndex:`sortOrder`,key:`sortOrder`,title:`排序`,width:100},{key:`description`,render:e=>(0,a.jsx)(i.Typography,{component:`span`,variant:`body`,children:e.description||`無`}),title:`說明`,width:260},{dataIndex:`updatedAtLabel`,key:`updatedAtLabel`,title:`更新時間`,width:220}],[]);async function V(e){F(!0),k(null);try{M?.type===`EDIT`&&M.record?await(0,l.updateApprovalTemplateCategory)({...e,id:M.record.id}):await(0,l.createApprovalTemplateCategory)(e),N(null),await R()}catch(e){throw k(v(e)),e}finally{F(!1)}}async function H(e){F(!0),k(null);try{await(0,l.updateApprovalTemplateCategory)({description:e.description,id:e.id,isActive:!e.isActive,name:e.name,sortOrder:e.sortOrder}),await R()}catch(e){k(v(e))}finally{F(!1)}}async function U(){if(E){F(!0),k(null);try{await(0,l.deleteApprovalTemplateCategory)(E.id),D(null),await R()}catch(e){k(v(e))}finally{F(!1)}}}let W=(0,r.useMemo)(()=>({render:e=>[{name:`編輯`,onClick:()=>N({record:e,type:`EDIT`})},{name:e.isActive?`停用`:`啟用`,onClick:()=>void H(e),variant:e.isActive?`destructive-secondary`:`base-secondary`},{name:`刪除`,onClick:()=>D({id:e.id,name:e.name}),variant:`destructive-secondary`}],variant:`base-secondary`,width:192}),[R]);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(i.PageHeader,{children:(0,a.jsx)(s.default,{description:`維護簽核模板分類,供模板建立、篩選與列表標示使用。`,title:`簽核模板分類`,children:(0,a.jsx)(i.Button,{icon:c.PlusIcon,iconType:`leading`,onClick:()=>N({record:null,type:`CREATE`}),variant:`base-primary`,children:`建立分類`})})}),(0,a.jsx)(i.SectionGroup,{children:(0,a.jsxs)(i.Section,{filterArea:(0,a.jsx)(i.FilterArea,{className:u.templateFilterArea,size:`sub`,children:(0,a.jsx)(i.FilterLine,{children:(0,a.jsx)(i.Filter,{span:3,children:(0,a.jsx)(i.FormField,{fullWidth:!0,layout:o.FormFieldLayout.VERTICAL,name:`categorySearchText`,children:(0,a.jsx)(i.Input,{fullWidth:!0,onChange:e=>{L(e.target.value),y(1)},placeholder:`關鍵字:搜尋分類名稱或說明`,size:`sub`,value:I,variant:`base`})})})})}),tab:(0,a.jsx)(i.Tab,{activeKey:S,onChange:e=>{C(g(e)),y(1)},children:f.map(e=>(0,a.jsx)(i.TabItem,{children:e.label},e.key))}),children:[O?(0,a.jsx)(i.Typography,{color:`text-error`,variant:`body`,children:O}):null,(0,a.jsx)(i.Table,{actions:W,columns:B,dataSource:z,fullWidth:!0,loading:A||P,pagination:{current:p,onChange:e=>{y(e)},onChangePageSize:e=>{y(1),x(e)},pageSize:b,pageSizeLabel:`每頁筆數`,pageSizeOptions:d,renderResultSummary:(e,t,n)=>`顯示 ${e}-${t} 筆,共 ${n} 筆`,showPageSizeOptions:!0,total:w}})]})})]}),(0,a.jsx)(m,{loading:P,modal:M,onClose:()=>N(null),onSubmit:V}),(0,a.jsx)(i.Modal,{cancelText:`取消`,confirmButtonProps:{variant:`destructive-primary`},confirmText:`刪除`,loading:P,modalStatusType:`error`,modalType:`standard`,onCancel:()=>D(null),onClose:()=>D(null),onConfirm:()=>void U(),open:!!E,showModalFooter:!0,showModalHeader:!0,size:`regular`,supportingText:`已被模板使用的分類無法刪除,請改用「停用」。`,title:`刪除分類`,children:(0,a.jsxs)(i.Typography,{color:`text-neutral`,variant:`body`,children:[`確定要刪除「`,E?.name??``,`」嗎?`]})})]})}function m({loading:e,modal:t,onClose:o,onSubmit:s}){let[c,l]=(0,r.useState)(``),[d,f]=(0,r.useState)(null),[p,m]=(0,r.useState)(``),[h,g]=(0,r.useState)(`0`);(0,r.useEffect)(()=>{t&&(l(t.record?.description??``),f(null),m(t.record?.name??``),g(String(t.record?.sortOrder??0)))},[t]);async function _(){let e=p.trim(),n=Number(h);if(!e)f(`請輸入分類名稱`);else if(!Number.isInteger(n))f(`排序必須是整數`);else try{await s({description:c.trim()||null,isActive:t?.record?.isActive??!0,name:e,sortOrder:n})}catch(e){f(v(e))}}return(0,a.jsxs)(i.Modal,{cancelText:`取消`,confirmButtonProps:{disabled:!p.trim()},confirmText:t?.type===`EDIT`?`儲存`:`建立`,loading:e,modalType:`standard`,onCancel:o,onClose:o,onConfirm:()=>void _(),open:!!t,showModalFooter:!0,showModalHeader:!0,size:`regular`,title:t?.type===`EDIT`?`編輯分類`:`建立分類`,children:[(0,a.jsxs)(`div`,{className:u.templateModalFields,children:[(0,a.jsx)(n.t,{label:`分類名稱`,name:`categoryName`,required:!0,children:(0,a.jsx)(i.Input,{autoFocus:!0,fullWidth:!0,onChange:e=>{m(e.target.value),f(null)},placeholder:`例如:行政管理`,value:p,variant:`base`})}),(0,a.jsx)(n.t,{label:`排序`,name:`categorySortOrder`,children:(0,a.jsx)(i.Input,{fullWidth:!0,onChange:e=>{g(e.target.value),f(null)},placeholder:`0`,value:h,variant:`base`})}),(0,a.jsx)(n.t,{label:`說明`,name:`categoryDescription`,children:(0,a.jsx)(i.Textarea,{onChange:e=>{l(e.target.value),f(null)},placeholder:`補充分類用途`,value:c})})]}),d?(0,a.jsx)(i.Typography,{color:`text-error`,variant:`body`,children:d}):null]})}function h({isActive:e}){return e?(0,a.jsx)(i.Badge,{size:`sub`,text:`啟用`,variant:`dot-success`}):(0,a.jsx)(i.Badge,{size:`sub`,text:`停用`,variant:`dot-inactive`})}function g(e){return e===`ACTIVE`||e===`INACTIVE`?e:`ALL`}function _(e){return e}function v(e){return e instanceof Error?e.message:`發生未知錯誤`}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return p}});
2
+ //# sourceMappingURL=categories-AxxhqjLZ.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"categories-DjENouRm.cjs","names":["useState","useCallback","listApprovalTemplateCategoriesPage","useMemo","formatDateTime","Typography","updateApprovalTemplateCategory","createApprovalTemplateCategory","deleteApprovalTemplateCategory","_Fragment","PageHeader","ContentHeader","Button","PlusIcon","SectionGroup","Section","FilterArea","styles","FilterLine","Filter","FormField","FormFieldLayout","Input","Tab","TabItem","Table","Modal","BPMFormField","Textarea","Badge","require_rolldown_runtime","require_format_date_time","require_bpm_form_field","react","_mezzanine_ui_react","react_jsx_runtime","_mezzanine_ui_core_form","_mezzanine_ui_react_ContentHeader","_mezzanine_ui_icons","_rytass_bpm_core_client_template","templates_module_default","CATEGORY_PAGE_SIZE_OPTIONS","CATEGORY_STATUS_TABS","TemplateCategoriesView","categories","setCategories","categoryPage","setCategoryPage","categoryPageSize","setCategoryPageSize","categoryStatus","setCategoryStatus","categoryTotalCount","setCategoryTotalCount","deleteConfirmation","setDeleteConfirmation","error","setError","loading","setLoading","modalState","setModalState","saving","setSaving","searchText","setSearchText","refreshCategories","categoryPageResult","readCategoryStatus","requestError","readErrorMessage","rows","category","columns","CategoryStatusBadge","record","handleSaveCategory","input","handleToggleCategory","handleConfirmDelete","tableActions","event","readCategoryStatusTabKey","activeKey","statusTab","page","pageSize","from","to","total","CategoryModal","description","setDescription","name","setName","sortOrder","setSortOrder","modal","handleConfirm","trimmedName","parsedSortOrder","onSubmit","submitError","onClose","isActive","status"],"sources":["../../src/views/templates/templates.module.scss","../../src/views/templates/categories/TemplateCategoriesView.tsx"],"sourcesContent":[".templateFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.templateModalFields {\n display: grid;\n gap: 16px;\n}\n","'use client';\n\nimport type { ChangeEvent, Key, ReactElement } from 'react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Tab,\n TabItem,\n Table,\n Textarea,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { PlusIcon } from '@mezzanine-ui/icons';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { BPMFormField } from '../../../components/bpm-form-field';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport {\n ApprovalTemplateCategoryRecord,\n ApprovalTemplateCategoryStatus,\n createApprovalTemplateCategory,\n deleteApprovalTemplateCategory,\n listApprovalTemplateCategoriesPage,\n updateApprovalTemplateCategory,\n} from '@rytass/bpm-core-client/template';\nimport styles from '../templates.module.scss';\n\nconst CATEGORY_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst CATEGORY_STATUS_TABS: readonly {\n readonly key: CategoryStatusTabKey;\n readonly label: string;\n}[] = [\n { key: 'ALL', label: '全部' },\n { key: 'ACTIVE', label: '啟用' },\n { key: 'INACTIVE', label: '停用' },\n];\ntype CategoryStatusTabKey = 'ACTIVE' | 'ALL' | 'INACTIVE';\n\ntype CategoryRow = Readonly<\n Record<string, unknown> &\n ApprovalTemplateCategoryRecord & {\n key: string;\n updatedAtLabel: string;\n }\n>;\n\ntype CategoryModalState = Readonly<{\n record: ApprovalTemplateCategoryRecord | null;\n type: 'CREATE' | 'EDIT';\n}>;\n\ntype DeleteConfirmationState = Readonly<{\n id: string;\n name: string;\n}>;\n\n\nexport function TemplateCategoriesView(): ReactElement {\n const [categories, setCategories] = useState<\n readonly ApprovalTemplateCategoryRecord[]\n >([]);\n const [categoryPage, setCategoryPage] = useState(1);\n const [categoryPageSize, setCategoryPageSize] = useState(10);\n const [categoryStatus, setCategoryStatus] =\n useState<CategoryStatusTabKey>('ALL');\n const [categoryTotalCount, setCategoryTotalCount] = useState(0);\n const [deleteConfirmation, setDeleteConfirmation] =\n useState<DeleteConfirmationState | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [modalState, setModalState] = useState<CategoryModalState | null>(null);\n const [saving, setSaving] = useState(false);\n const [searchText, setSearchText] = useState('');\n\n const refreshCategories = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const categoryPageResult = await listApprovalTemplateCategoriesPage({\n page: categoryPage,\n pageSize: categoryPageSize,\n searchText,\n status: readCategoryStatus(categoryStatus),\n });\n\n setCategories(categoryPageResult.categories);\n setCategoryTotalCount(categoryPageResult.totalCount);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [categoryPage, categoryPageSize, categoryStatus, searchText]);\n\n useEffect((): void => {\n void refreshCategories();\n }, [refreshCategories]);\n\n const rows = useMemo(\n (): CategoryRow[] =>\n categories.map((category) => ({\n ...category,\n key: category.id,\n updatedAtLabel: formatDateTime(category.updatedAt),\n })),\n [categories],\n );\n\n const columns = useMemo(\n (): TableColumn<CategoryRow>[] => [\n { dataIndex: 'name', key: 'name', title: '分類名稱', width: 180 },\n {\n key: 'status',\n render: (record: CategoryRow): ReactElement => (\n <CategoryStatusBadge isActive={record.isActive} />\n ),\n title: '狀態',\n width: 120,\n },\n {\n dataIndex: 'sortOrder',\n key: 'sortOrder',\n title: '排序',\n width: 100,\n },\n {\n key: 'description',\n render: (record: CategoryRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {record.description || '無'}\n </Typography>\n ),\n title: '說明',\n width: 260,\n },\n {\n dataIndex: 'updatedAtLabel',\n key: 'updatedAtLabel',\n title: '更新時間',\n width: 220,\n },\n ],\n [],\n );\n\n async function handleSaveCategory(input: CategoryFormInput): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n if (modalState?.type === 'EDIT' && modalState.record) {\n await updateApprovalTemplateCategory({\n ...input,\n id: modalState.record.id,\n });\n } else {\n await createApprovalTemplateCategory(input);\n }\n\n setModalState(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n throw requestError;\n } finally {\n setSaving(false);\n }\n }\n\n async function handleToggleCategory(\n record: ApprovalTemplateCategoryRecord,\n ): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n await updateApprovalTemplateCategory({\n description: record.description,\n id: record.id,\n isActive: !record.isActive,\n name: record.name,\n sortOrder: record.sortOrder,\n });\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n async function handleConfirmDelete(): Promise<void> {\n if (!deleteConfirmation) {\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n await deleteApprovalTemplateCategory(deleteConfirmation.id);\n setDeleteConfirmation(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const tableActions = useMemo(\n (): TableActions<CategoryRow> => ({\n render: (record): ReturnType<TableActions<CategoryRow>['render']> => [\n {\n name: '編輯',\n onClick: (): void => setModalState({ record, type: 'EDIT' }),\n },\n {\n name: record.isActive ? '停用' : '啟用',\n onClick: (): void => void handleToggleCategory(record),\n variant: record.isActive ? 'destructive-secondary' : 'base-secondary',\n },\n {\n name: '刪除',\n onClick: (): void =>\n setDeleteConfirmation({ id: record.id, name: record.name }),\n variant: 'destructive-secondary',\n },\n ],\n variant: 'base-secondary',\n width: 192,\n }),\n [refreshCategories],\n );\n\n return (\n <>\n <>\n <PageHeader>\n <ContentHeader\n description=\"維護簽核模板分類,供模板建立、篩選與列表標示使用。\"\n title=\"簽核模板分類\"\n >\n <Button\n icon={PlusIcon}\n iconType=\"leading\"\n onClick={(): void =>\n setModalState({ record: null, type: 'CREATE' })\n }\n variant=\"base-primary\"\n >\n 建立分類\n </Button>\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.templateFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"categorySearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setCategoryPage(1);\n }}\n placeholder=\"關鍵字:搜尋分類名稱或說明\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n tab={\n <Tab\n activeKey={categoryStatus}\n onChange={(activeKey): void => {\n setCategoryStatus(readCategoryStatusTabKey(activeKey));\n setCategoryPage(1);\n }}\n >\n {CATEGORY_STATUS_TABS.map((statusTab) => (\n <TabItem key={statusTab.key}>{statusTab.label}</TabItem>\n ))}\n </Tab>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n loading={loading || saving}\n pagination={{\n current: categoryPage,\n onChange: (page): void => {\n setCategoryPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setCategoryPage(1);\n setCategoryPageSize(pageSize);\n },\n pageSize: categoryPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: CATEGORY_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: categoryTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n </>\n\n <CategoryModal\n loading={saving}\n modal={modalState}\n onClose={(): void => setModalState(null)}\n onSubmit={handleSaveCategory}\n />\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ variant: 'destructive-primary' }}\n confirmText=\"刪除\"\n loading={saving}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={(): void => setDeleteConfirmation(null)}\n onClose={(): void => setDeleteConfirmation(null)}\n onConfirm={(): void => void handleConfirmDelete()}\n open={Boolean(deleteConfirmation)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"若分類已被模板使用,系統會改為停用分類並保留既有模板關聯。\"\n title=\"刪除分類\"\n >\n <Typography color=\"text-neutral\" variant=\"body\">\n 確定要刪除「{deleteConfirmation?.name ?? ''}」嗎?\n </Typography>\n </Modal>\n </>\n );\n}\n\ninterface CategoryFormInput {\n readonly description: string | null;\n readonly isActive: boolean;\n readonly name: string;\n readonly sortOrder: number;\n}\n\nfunction CategoryModal({\n loading,\n modal,\n onClose,\n onSubmit,\n}: {\n readonly loading: boolean;\n readonly modal: CategoryModalState | null;\n readonly onClose: () => void;\n readonly onSubmit: (input: CategoryFormInput) => Promise<void>;\n}): ReactElement {\n const [description, setDescription] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [name, setName] = useState('');\n const [sortOrder, setSortOrder] = useState('0');\n\n useEffect((): void => {\n if (!modal) {\n return;\n }\n\n setDescription(modal.record?.description ?? '');\n setError(null);\n setName(modal.record?.name ?? '');\n setSortOrder(String(modal.record?.sortOrder ?? 0));\n }, [modal]);\n\n async function handleConfirm(): Promise<void> {\n const trimmedName = name.trim();\n const parsedSortOrder = Number(sortOrder);\n\n if (!trimmedName) {\n setError('請輸入分類名稱');\n return;\n }\n\n if (!Number.isInteger(parsedSortOrder)) {\n setError('排序必須是整數');\n return;\n }\n\n try {\n await onSubmit({\n description: description.trim() || null,\n isActive: modal?.record?.isActive ?? true,\n name: trimmedName,\n sortOrder: parsedSortOrder,\n });\n } catch (submitError: unknown) {\n setError(readErrorMessage(submitError));\n }\n }\n\n return (\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ disabled: !name.trim() }}\n confirmText={modal?.type === 'EDIT' ? '儲存' : '建立'}\n loading={loading}\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={(): void => void handleConfirm()}\n open={Boolean(modal)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={modal?.type === 'EDIT' ? '編輯分類' : '建立分類'}\n >\n <div className={styles.templateModalFields}>\n <BPMFormField label=\"分類名稱\" name=\"categoryName\" required>\n <Input\n autoFocus\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setName(event.target.value);\n setError(null);\n }}\n placeholder=\"例如:行政管理\"\n value={name}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"排序\" name=\"categorySortOrder\">\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setSortOrder(event.target.value);\n setError(null);\n }}\n placeholder=\"0\"\n value={sortOrder}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"說明\" name=\"categoryDescription\">\n <Textarea\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setDescription(event.target.value);\n setError(null);\n }}\n placeholder=\"補充分類用途\"\n value={description}\n />\n </BPMFormField>\n </div>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n </Modal>\n );\n}\n\nfunction CategoryStatusBadge({\n isActive,\n}: {\n readonly isActive: boolean;\n}): ReactElement {\n return isActive ? (\n <Badge size=\"sub\" text=\"啟用\" variant=\"dot-success\" />\n ) : (\n <Badge size=\"sub\" text=\"停用\" variant=\"dot-inactive\" />\n );\n}\n\nfunction readCategoryStatusTabKey(activeKey: Key): CategoryStatusTabKey {\n if (activeKey === 'ACTIVE' || activeKey === 'INACTIVE') {\n return activeKey;\n }\n\n return 'ALL';\n}\n\nfunction readCategoryStatus(\n status: CategoryStatusTabKey,\n): ApprovalTemplateCategoryStatus {\n return status;\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"categories-AxxhqjLZ.cjs","names":["useState","useCallback","listApprovalTemplateCategoriesPage","useMemo","formatDateTime","Typography","updateApprovalTemplateCategory","createApprovalTemplateCategory","deleteApprovalTemplateCategory","_Fragment","PageHeader","ContentHeader","Button","PlusIcon","SectionGroup","Section","FilterArea","styles","FilterLine","Filter","FormField","FormFieldLayout","Input","Tab","TabItem","Table","Modal","BPMFormField","Textarea","Badge","require_rolldown_runtime","require_format_date_time","require_bpm_form_field","react","_mezzanine_ui_react","react_jsx_runtime","_mezzanine_ui_core_form","_mezzanine_ui_react_ContentHeader","_mezzanine_ui_icons","_rytass_bpm_core_client_template","templates_module_default","CATEGORY_PAGE_SIZE_OPTIONS","CATEGORY_STATUS_TABS","TemplateCategoriesView","categories","setCategories","categoryPage","setCategoryPage","categoryPageSize","setCategoryPageSize","categoryStatus","setCategoryStatus","categoryTotalCount","setCategoryTotalCount","deleteConfirmation","setDeleteConfirmation","error","setError","loading","setLoading","modalState","setModalState","saving","setSaving","searchText","setSearchText","refreshCategories","categoryPageResult","readCategoryStatus","requestError","readErrorMessage","rows","category","columns","CategoryStatusBadge","record","handleSaveCategory","input","handleToggleCategory","handleConfirmDelete","tableActions","event","readCategoryStatusTabKey","activeKey","statusTab","page","pageSize","from","to","total","CategoryModal","description","setDescription","name","setName","sortOrder","setSortOrder","modal","handleConfirm","trimmedName","parsedSortOrder","onSubmit","submitError","onClose","isActive","status"],"sources":["../../src/views/templates/templates.module.scss","../../src/views/templates/categories/TemplateCategoriesView.tsx"],"sourcesContent":[".templateFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.templateModalFields {\n display: grid;\n gap: 16px;\n}\n","'use client';\n\nimport type { ChangeEvent, Key, ReactElement } from 'react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Tab,\n TabItem,\n Table,\n Textarea,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { PlusIcon } from '@mezzanine-ui/icons';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { BPMFormField } from '../../../components/bpm-form-field';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport {\n ApprovalTemplateCategoryRecord,\n ApprovalTemplateCategoryStatus,\n createApprovalTemplateCategory,\n deleteApprovalTemplateCategory,\n listApprovalTemplateCategoriesPage,\n updateApprovalTemplateCategory,\n} from '@rytass/bpm-core-client/template';\nimport styles from '../templates.module.scss';\n\nconst CATEGORY_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst CATEGORY_STATUS_TABS: readonly {\n readonly key: CategoryStatusTabKey;\n readonly label: string;\n}[] = [\n { key: 'ALL', label: '全部' },\n { key: 'ACTIVE', label: '啟用' },\n { key: 'INACTIVE', label: '停用' },\n];\ntype CategoryStatusTabKey = 'ACTIVE' | 'ALL' | 'INACTIVE';\n\ntype CategoryRow = Readonly<\n Record<string, unknown> &\n ApprovalTemplateCategoryRecord & {\n key: string;\n updatedAtLabel: string;\n }\n>;\n\ntype CategoryModalState = Readonly<{\n record: ApprovalTemplateCategoryRecord | null;\n type: 'CREATE' | 'EDIT';\n}>;\n\ntype DeleteConfirmationState = Readonly<{\n id: string;\n name: string;\n}>;\n\n\nexport function TemplateCategoriesView(): ReactElement {\n const [categories, setCategories] = useState<\n readonly ApprovalTemplateCategoryRecord[]\n >([]);\n const [categoryPage, setCategoryPage] = useState(1);\n const [categoryPageSize, setCategoryPageSize] = useState(10);\n const [categoryStatus, setCategoryStatus] =\n useState<CategoryStatusTabKey>('ALL');\n const [categoryTotalCount, setCategoryTotalCount] = useState(0);\n const [deleteConfirmation, setDeleteConfirmation] =\n useState<DeleteConfirmationState | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [modalState, setModalState] = useState<CategoryModalState | null>(null);\n const [saving, setSaving] = useState(false);\n const [searchText, setSearchText] = useState('');\n\n const refreshCategories = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const categoryPageResult = await listApprovalTemplateCategoriesPage({\n page: categoryPage,\n pageSize: categoryPageSize,\n searchText,\n status: readCategoryStatus(categoryStatus),\n });\n\n setCategories(categoryPageResult.categories);\n setCategoryTotalCount(categoryPageResult.totalCount);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [categoryPage, categoryPageSize, categoryStatus, searchText]);\n\n useEffect((): void => {\n void refreshCategories();\n }, [refreshCategories]);\n\n const rows = useMemo(\n (): CategoryRow[] =>\n categories.map((category) => ({\n ...category,\n key: category.id,\n updatedAtLabel: formatDateTime(category.updatedAt),\n })),\n [categories],\n );\n\n const columns = useMemo(\n (): TableColumn<CategoryRow>[] => [\n { dataIndex: 'name', key: 'name', title: '分類名稱', width: 180 },\n {\n key: 'status',\n render: (record: CategoryRow): ReactElement => (\n <CategoryStatusBadge isActive={record.isActive} />\n ),\n title: '狀態',\n width: 120,\n },\n {\n dataIndex: 'sortOrder',\n key: 'sortOrder',\n title: '排序',\n width: 100,\n },\n {\n key: 'description',\n render: (record: CategoryRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {record.description || '無'}\n </Typography>\n ),\n title: '說明',\n width: 260,\n },\n {\n dataIndex: 'updatedAtLabel',\n key: 'updatedAtLabel',\n title: '更新時間',\n width: 220,\n },\n ],\n [],\n );\n\n async function handleSaveCategory(input: CategoryFormInput): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n if (modalState?.type === 'EDIT' && modalState.record) {\n await updateApprovalTemplateCategory({\n ...input,\n id: modalState.record.id,\n });\n } else {\n await createApprovalTemplateCategory(input);\n }\n\n setModalState(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n throw requestError;\n } finally {\n setSaving(false);\n }\n }\n\n async function handleToggleCategory(\n record: ApprovalTemplateCategoryRecord,\n ): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n await updateApprovalTemplateCategory({\n description: record.description,\n id: record.id,\n isActive: !record.isActive,\n name: record.name,\n sortOrder: record.sortOrder,\n });\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n async function handleConfirmDelete(): Promise<void> {\n if (!deleteConfirmation) {\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n await deleteApprovalTemplateCategory(deleteConfirmation.id);\n setDeleteConfirmation(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const tableActions = useMemo(\n (): TableActions<CategoryRow> => ({\n render: (record): ReturnType<TableActions<CategoryRow>['render']> => [\n {\n name: '編輯',\n onClick: (): void => setModalState({ record, type: 'EDIT' }),\n },\n {\n name: record.isActive ? '停用' : '啟用',\n onClick: (): void => void handleToggleCategory(record),\n variant: record.isActive ? 'destructive-secondary' : 'base-secondary',\n },\n {\n name: '刪除',\n onClick: (): void =>\n setDeleteConfirmation({ id: record.id, name: record.name }),\n variant: 'destructive-secondary',\n },\n ],\n variant: 'base-secondary',\n width: 192,\n }),\n [refreshCategories],\n );\n\n return (\n <>\n <>\n <PageHeader>\n <ContentHeader\n description=\"維護簽核模板分類,供模板建立、篩選與列表標示使用。\"\n title=\"簽核模板分類\"\n >\n <Button\n icon={PlusIcon}\n iconType=\"leading\"\n onClick={(): void =>\n setModalState({ record: null, type: 'CREATE' })\n }\n variant=\"base-primary\"\n >\n 建立分類\n </Button>\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.templateFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"categorySearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setCategoryPage(1);\n }}\n placeholder=\"關鍵字:搜尋分類名稱或說明\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n tab={\n <Tab\n activeKey={categoryStatus}\n onChange={(activeKey): void => {\n setCategoryStatus(readCategoryStatusTabKey(activeKey));\n setCategoryPage(1);\n }}\n >\n {CATEGORY_STATUS_TABS.map((statusTab) => (\n <TabItem key={statusTab.key}>{statusTab.label}</TabItem>\n ))}\n </Tab>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n loading={loading || saving}\n pagination={{\n current: categoryPage,\n onChange: (page): void => {\n setCategoryPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setCategoryPage(1);\n setCategoryPageSize(pageSize);\n },\n pageSize: categoryPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: CATEGORY_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: categoryTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n </>\n\n <CategoryModal\n loading={saving}\n modal={modalState}\n onClose={(): void => setModalState(null)}\n onSubmit={handleSaveCategory}\n />\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ variant: 'destructive-primary' }}\n confirmText=\"刪除\"\n loading={saving}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={(): void => setDeleteConfirmation(null)}\n onClose={(): void => setDeleteConfirmation(null)}\n onConfirm={(): void => void handleConfirmDelete()}\n open={Boolean(deleteConfirmation)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"已被模板使用的分類無法刪除,請改用「停用」。\"\n title=\"刪除分類\"\n >\n <Typography color=\"text-neutral\" variant=\"body\">\n 確定要刪除「{deleteConfirmation?.name ?? ''}」嗎?\n </Typography>\n </Modal>\n </>\n );\n}\n\ninterface CategoryFormInput {\n readonly description: string | null;\n readonly isActive: boolean;\n readonly name: string;\n readonly sortOrder: number;\n}\n\nfunction CategoryModal({\n loading,\n modal,\n onClose,\n onSubmit,\n}: {\n readonly loading: boolean;\n readonly modal: CategoryModalState | null;\n readonly onClose: () => void;\n readonly onSubmit: (input: CategoryFormInput) => Promise<void>;\n}): ReactElement {\n const [description, setDescription] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [name, setName] = useState('');\n const [sortOrder, setSortOrder] = useState('0');\n\n useEffect((): void => {\n if (!modal) {\n return;\n }\n\n setDescription(modal.record?.description ?? '');\n setError(null);\n setName(modal.record?.name ?? '');\n setSortOrder(String(modal.record?.sortOrder ?? 0));\n }, [modal]);\n\n async function handleConfirm(): Promise<void> {\n const trimmedName = name.trim();\n const parsedSortOrder = Number(sortOrder);\n\n if (!trimmedName) {\n setError('請輸入分類名稱');\n return;\n }\n\n if (!Number.isInteger(parsedSortOrder)) {\n setError('排序必須是整數');\n return;\n }\n\n try {\n await onSubmit({\n description: description.trim() || null,\n isActive: modal?.record?.isActive ?? true,\n name: trimmedName,\n sortOrder: parsedSortOrder,\n });\n } catch (submitError: unknown) {\n setError(readErrorMessage(submitError));\n }\n }\n\n return (\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ disabled: !name.trim() }}\n confirmText={modal?.type === 'EDIT' ? '儲存' : '建立'}\n loading={loading}\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={(): void => void handleConfirm()}\n open={Boolean(modal)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={modal?.type === 'EDIT' ? '編輯分類' : '建立分類'}\n >\n <div className={styles.templateModalFields}>\n <BPMFormField label=\"分類名稱\" name=\"categoryName\" required>\n <Input\n autoFocus\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setName(event.target.value);\n setError(null);\n }}\n placeholder=\"例如:行政管理\"\n value={name}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"排序\" name=\"categorySortOrder\">\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setSortOrder(event.target.value);\n setError(null);\n }}\n placeholder=\"0\"\n value={sortOrder}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"說明\" name=\"categoryDescription\">\n <Textarea\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setDescription(event.target.value);\n setError(null);\n }}\n placeholder=\"補充分類用途\"\n value={description}\n />\n </BPMFormField>\n </div>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n </Modal>\n );\n}\n\nfunction CategoryStatusBadge({\n isActive,\n}: {\n readonly isActive: boolean;\n}): ReactElement {\n return isActive ? (\n <Badge size=\"sub\" text=\"啟用\" variant=\"dot-success\" />\n ) : (\n <Badge size=\"sub\" text=\"停用\" variant=\"dot-inactive\" />\n );\n}\n\nfunction readCategoryStatusTabKey(activeKey: Key): CategoryStatusTabKey {\n if (activeKey === 'ACTIVE' || activeKey === 'INACTIVE') {\n return activeKey;\n }\n\n return 'ALL';\n}\n\nfunction readCategoryStatus(\n status: CategoryStatusTabKey,\n): ApprovalTemplateCategoryStatus {\n return status;\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
@@ -252,7 +252,7 @@ function N() {
252
252
  showModalFooter: !0,
253
253
  showModalHeader: !0,
254
254
  size: "regular",
255
- supportingText: "若分類已被模板使用,系統會改為停用分類並保留既有模板關聯。",
255
+ supportingText: "已被模板使用的分類無法刪除,請改用「停用」。",
256
256
  title: "刪除分類",
257
257
  children: /* @__PURE__ */ w(x, {
258
258
  color: "text-neutral",
@@ -373,4 +373,4 @@ function P(e) {
373
373
  //#endregion
374
374
  export { A as n, N as t };
375
375
 
376
- //# sourceMappingURL=categories-1gKxeEqw.js.map
376
+ //# sourceMappingURL=categories-riC33J_j.js.map