@rytass/bpm-core-react 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +54 -6
  3. package/dist/chunks/{FormBuilderView-CLg-JYmu.js → FormBuilderView-BYL42M0d.js} +13 -33
  4. package/dist/chunks/FormBuilderView-BYL42M0d.js.map +1 -0
  5. package/dist/chunks/FormBuilderView-aBcA-_tt.cjs +3 -0
  6. package/dist/chunks/FormBuilderView-aBcA-_tt.cjs.map +1 -0
  7. package/dist/chunks/FormRendererView-DRFi_IH7.cjs +4 -0
  8. package/dist/chunks/FormRendererView-DRFi_IH7.cjs.map +1 -0
  9. package/dist/chunks/{FormRendererView-DU9LLrOF.js → FormRendererView-cZGOOjU5.js} +319 -332
  10. package/dist/chunks/FormRendererView-cZGOOjU5.js.map +1 -0
  11. package/dist/chunks/{admin-pickers-DLlG_1du.js → admin-pickers-DxhzECkD.js} +52 -51
  12. package/dist/chunks/admin-pickers-DxhzECkD.js.map +1 -0
  13. package/dist/chunks/admin-pickers-qe3j--gX.cjs +2 -0
  14. package/dist/chunks/admin-pickers-qe3j--gX.cjs.map +1 -0
  15. package/dist/chunks/approval-instance-list-page-CPXMP3PL.js.map +1 -1
  16. package/dist/chunks/approval-instance-list-page-ax0pm8bt.cjs.map +1 -1
  17. package/dist/chunks/auth-provider-4BeCw7cI.cjs.map +1 -1
  18. package/dist/chunks/auth-provider-B5oPmvk2.js.map +1 -1
  19. package/dist/chunks/bpm-form-field-Bc6k4ZEO.cjs.map +1 -1
  20. package/dist/chunks/bpm-form-field-Cao0rMol.js.map +1 -1
  21. package/dist/chunks/{categories-DGlxuC6X.js → categories-1gKxeEqw.js} +4 -10
  22. package/dist/chunks/categories-1gKxeEqw.js.map +1 -0
  23. package/dist/chunks/{categories-Yid05WAX.cjs → categories-DjENouRm.cjs} +2 -2
  24. package/dist/chunks/categories-DjENouRm.cjs.map +1 -0
  25. package/dist/chunks/{compose-BtQPBd1Q.js → compose-Crklayzy.js} +4 -4
  26. package/dist/chunks/compose-Crklayzy.js.map +1 -0
  27. package/dist/chunks/{compose-DZhTQOt4.cjs → compose-DZ7RKvUp.cjs} +2 -2
  28. package/dist/chunks/compose-DZ7RKvUp.cjs.map +1 -0
  29. package/dist/chunks/dashboard-page-BWQBSGDq.cjs +2 -0
  30. package/dist/chunks/dashboard-page-BWQBSGDq.cjs.map +1 -0
  31. package/dist/chunks/{dashboard-page-CeCLV1tp.js → dashboard-page-DdBvV6YI.js} +15 -16
  32. package/dist/chunks/dashboard-page-DdBvV6YI.js.map +1 -0
  33. package/dist/chunks/delegations-Ckf03icM.cjs +2 -0
  34. package/dist/chunks/delegations-Ckf03icM.cjs.map +1 -0
  35. package/dist/chunks/{delegations-DRupxJ4_.js → delegations-MxF3uJGW.js} +205 -215
  36. package/dist/chunks/delegations-MxF3uJGW.js.map +1 -0
  37. package/dist/chunks/delegations-V9HIe3_P.cjs +2 -0
  38. package/dist/chunks/delegations-V9HIe3_P.cjs.map +1 -0
  39. package/dist/chunks/{delegations-B8gLSt0i.js → delegations-xUt1tj8d.js} +168 -173
  40. package/dist/chunks/delegations-xUt1tj8d.js.map +1 -0
  41. package/dist/chunks/{designer-CrbBRPUU.js → designer-D9eFma1W.js} +1180 -1134
  42. package/dist/chunks/designer-D9eFma1W.js.map +1 -0
  43. package/dist/chunks/designer-sJGTGYOf.cjs +65 -0
  44. package/dist/chunks/designer-sJGTGYOf.cjs.map +1 -0
  45. package/dist/chunks/detail-DRCTnuKm.cjs +2 -0
  46. package/dist/chunks/detail-DRCTnuKm.cjs.map +1 -0
  47. package/dist/chunks/{detail-_c21WQla.js → detail-DScLf42P.js} +849 -836
  48. package/dist/chunks/detail-DScLf42P.js.map +1 -0
  49. package/dist/chunks/format-date-time-CB-LxzqT.js.map +1 -1
  50. package/dist/chunks/format-date-time-D-cx5M8z.cjs.map +1 -1
  51. package/dist/chunks/format-member-label-DudVI55w.js +13 -0
  52. package/dist/chunks/format-member-label-DudVI55w.js.map +1 -0
  53. package/dist/chunks/format-member-label-t7ANTF62.cjs +2 -0
  54. package/dist/chunks/format-member-label-t7ANTF62.cjs.map +1 -0
  55. package/dist/chunks/login-AtVxZBsC.cjs.map +1 -1
  56. package/dist/chunks/login-C6qlTC6F.js.map +1 -1
  57. package/dist/chunks/notifications-Bc4mgSds.cjs.map +1 -1
  58. package/dist/chunks/notifications-DJ5wzCye.js.map +1 -1
  59. package/dist/chunks/{orgs-BpqVu5ka.js → orgs-CY0i1fie.js} +787 -766
  60. package/dist/chunks/orgs-CY0i1fie.js.map +1 -0
  61. package/dist/chunks/orgs-Dr0FFoZo.cjs +2 -0
  62. package/dist/chunks/orgs-Dr0FFoZo.cjs.map +1 -0
  63. package/dist/chunks/providers-DHtDZFNA.cjs.map +1 -1
  64. package/dist/chunks/providers-J5eyByQj.js.map +1 -1
  65. package/dist/chunks/router-adapter--gYs13E8.cjs.map +1 -1
  66. package/dist/chunks/router-adapter-DftlFTOd.js.map +1 -1
  67. package/dist/chunks/routes-config-C1j_vFKo.cjs.map +1 -1
  68. package/dist/chunks/routes-config-yZbESPlw.js.map +1 -1
  69. package/dist/chunks/task-node-label-BF6UllxP.cjs.map +1 -1
  70. package/dist/chunks/task-node-label-CWZgOBj0.js.map +1 -1
  71. package/dist/chunks/use-bpm-member-D57LKe_u.js.map +1 -1
  72. package/dist/chunks/use-bpm-member-DpNAQtNC.cjs.map +1 -1
  73. package/dist/chunks/users-BCyePbwB.js +422 -0
  74. package/dist/chunks/users-BCyePbwB.js.map +1 -0
  75. package/dist/chunks/users-DVBJluFR.cjs +2 -0
  76. package/dist/chunks/users-DVBJluFR.cjs.map +1 -0
  77. package/dist/index.cjs +1 -1
  78. package/dist/index.cjs.map +1 -1
  79. package/dist/index.js +2 -2
  80. package/dist/index.js.map +1 -1
  81. package/dist/lib/format-member-label.d.ts +12 -0
  82. package/dist/next/index.cjs.map +1 -1
  83. package/dist/next/index.js.map +1 -1
  84. package/dist/next/workflow-chat-route.cjs.map +1 -1
  85. package/dist/next/workflow-chat-route.js.map +1 -1
  86. package/dist/pages/admin/delegations/index.cjs +1 -1
  87. package/dist/pages/admin/delegations/index.cjs.map +1 -1
  88. package/dist/pages/admin/delegations/index.js +1 -1
  89. package/dist/pages/admin/delegations/index.js.map +1 -1
  90. package/dist/pages/admin/orgs/index.cjs +1 -1
  91. package/dist/pages/admin/orgs/index.cjs.map +1 -1
  92. package/dist/pages/admin/orgs/index.js +1 -1
  93. package/dist/pages/admin/orgs/index.js.map +1 -1
  94. package/dist/pages/admin/users/index.cjs +1 -1
  95. package/dist/pages/admin/users/index.cjs.map +1 -1
  96. package/dist/pages/admin/users/index.js +1 -1
  97. package/dist/pages/admin/users/index.js.map +1 -1
  98. package/dist/pages/admin/webhook-endpoints/index.cjs.map +1 -1
  99. package/dist/pages/admin/webhook-endpoints/index.js.map +1 -1
  100. package/dist/pages/cc/index.cjs.map +1 -1
  101. package/dist/pages/cc/index.js.map +1 -1
  102. package/dist/pages/dashboard/index.cjs.map +1 -1
  103. package/dist/pages/dashboard/index.js.map +1 -1
  104. package/dist/pages/delegations/index.cjs +1 -1
  105. package/dist/pages/delegations/index.cjs.map +1 -1
  106. package/dist/pages/delegations/index.js +1 -1
  107. package/dist/pages/delegations/index.js.map +1 -1
  108. package/dist/pages/inbox/index.cjs.map +1 -1
  109. package/dist/pages/inbox/index.js.map +1 -1
  110. package/dist/pages/instances/detail/index.cjs +1 -1
  111. package/dist/pages/instances/detail/index.cjs.map +1 -1
  112. package/dist/pages/instances/detail/index.js +1 -1
  113. package/dist/pages/instances/detail/index.js.map +1 -1
  114. package/dist/pages/instances/new/index.cjs.map +1 -1
  115. package/dist/pages/instances/new/index.js.map +1 -1
  116. package/dist/pages/login/index.cjs.map +1 -1
  117. package/dist/pages/login/index.js.map +1 -1
  118. package/dist/pages/root/index.cjs.map +1 -1
  119. package/dist/pages/root/index.js.map +1 -1
  120. package/dist/pages/search/index.cjs.map +1 -1
  121. package/dist/pages/search/index.js.map +1 -1
  122. package/dist/pages/sent/index.cjs.map +1 -1
  123. package/dist/pages/sent/index.js.map +1 -1
  124. package/dist/pages/settings/notifications/index.cjs.map +1 -1
  125. package/dist/pages/settings/notifications/index.js.map +1 -1
  126. package/dist/pages/templates/categories/index.cjs +1 -1
  127. package/dist/pages/templates/categories/index.cjs.map +1 -1
  128. package/dist/pages/templates/categories/index.js +1 -1
  129. package/dist/pages/templates/categories/index.js.map +1 -1
  130. package/dist/pages/templates/compose/index.cjs +1 -1
  131. package/dist/pages/templates/compose/index.cjs.map +1 -1
  132. package/dist/pages/templates/compose/index.js +1 -1
  133. package/dist/pages/templates/compose/index.js.map +1 -1
  134. package/dist/pages/templates/designer/index.cjs +1 -1
  135. package/dist/pages/templates/designer/index.cjs.map +1 -1
  136. package/dist/pages/templates/designer/index.js +1 -1
  137. package/dist/pages/templates/designer/index.js.map +1 -1
  138. package/dist/pages/templates/index.cjs.map +1 -1
  139. package/dist/pages/templates/index.js.map +1 -1
  140. package/dist/pages/templates/versions/index.cjs.map +1 -1
  141. package/dist/pages/templates/versions/index.js.map +1 -1
  142. package/dist/views/admin/delegations/index.cjs +1 -1
  143. package/dist/views/admin/delegations/index.js +1 -2
  144. package/dist/views/admin/index.cjs +1 -1
  145. package/dist/views/admin/index.js +3 -3
  146. package/dist/views/admin/orgs/index.cjs +1 -1
  147. package/dist/views/admin/orgs/index.js +1 -2
  148. package/dist/views/admin/users/MemberTaskReassignModal.d.ts +13 -0
  149. package/dist/views/admin/users/index.cjs +1 -1
  150. package/dist/views/admin/users/index.js +1 -2
  151. package/dist/views/admin/webhook-endpoints/index.cjs.map +1 -1
  152. package/dist/views/admin/webhook-endpoints/index.js.map +1 -1
  153. package/dist/views/cc/index.cjs.map +1 -1
  154. package/dist/views/cc/index.js.map +1 -1
  155. package/dist/views/dashboard/index.cjs +1 -1
  156. package/dist/views/dashboard/index.cjs.map +1 -1
  157. package/dist/views/dashboard/index.js +1 -1
  158. package/dist/views/dashboard/index.js.map +1 -1
  159. package/dist/views/delegations/index.cjs +1 -1
  160. package/dist/views/delegations/index.js +1 -2
  161. package/dist/views/forms/builder/index.cjs +1 -1
  162. package/dist/views/forms/builder/index.js +1 -2
  163. package/dist/views/forms/renderer/index.cjs +1 -1
  164. package/dist/views/forms/renderer/index.js +1 -2
  165. package/dist/views/inbox/index.cjs +1 -1
  166. package/dist/views/inbox/index.cjs.map +1 -1
  167. package/dist/views/inbox/index.js +1 -5
  168. package/dist/views/inbox/index.js.map +1 -1
  169. package/dist/views/instances/detail/index.cjs +1 -1
  170. package/dist/views/instances/detail/index.js +1 -2
  171. package/dist/views/instances/index.js +1 -2
  172. package/dist/views/instances/new/index.cjs +1 -1
  173. package/dist/views/instances/new/index.cjs.map +1 -1
  174. package/dist/views/instances/new/index.js +4 -11
  175. package/dist/views/instances/new/index.js.map +1 -1
  176. package/dist/views/login/index.js +1 -2
  177. package/dist/views/search/index.cjs.map +1 -1
  178. package/dist/views/search/index.js.map +1 -1
  179. package/dist/views/sent/index.cjs.map +1 -1
  180. package/dist/views/sent/index.js.map +1 -1
  181. package/dist/views/settings/index.js +1 -2
  182. package/dist/views/settings/notifications/index.js +1 -2
  183. package/dist/views/templates/categories/index.cjs +1 -1
  184. package/dist/views/templates/categories/index.js +1 -2
  185. package/dist/views/templates/compose/index.cjs +1 -1
  186. package/dist/views/templates/compose/index.js +1 -2
  187. package/dist/views/templates/designer/index.cjs +1 -1
  188. package/dist/views/templates/designer/index.js +1 -2
  189. package/dist/views/templates/index.cjs +1 -1
  190. package/dist/views/templates/index.cjs.map +1 -1
  191. package/dist/views/templates/index.js +1 -1
  192. package/dist/views/templates/index.js.map +1 -1
  193. package/dist/views/templates/versions/index.cjs.map +1 -1
  194. package/dist/views/templates/versions/index.js.map +1 -1
  195. package/package.json +5 -5
  196. package/dist/chunks/FormBuilderView-6bCbIkz6.cjs +0 -3
  197. package/dist/chunks/FormBuilderView-6bCbIkz6.cjs.map +0 -1
  198. package/dist/chunks/FormBuilderView-CLg-JYmu.js.map +0 -1
  199. package/dist/chunks/FormRendererView-DU9LLrOF.js.map +0 -1
  200. package/dist/chunks/FormRendererView-gctFIqwC.cjs +0 -4
  201. package/dist/chunks/FormRendererView-gctFIqwC.cjs.map +0 -1
  202. package/dist/chunks/admin-pickers-Btvij1at.cjs +0 -2
  203. package/dist/chunks/admin-pickers-Btvij1at.cjs.map +0 -1
  204. package/dist/chunks/admin-pickers-DLlG_1du.js.map +0 -1
  205. package/dist/chunks/categories-DGlxuC6X.js.map +0 -1
  206. package/dist/chunks/categories-Yid05WAX.cjs.map +0 -1
  207. package/dist/chunks/compose-BtQPBd1Q.js.map +0 -1
  208. package/dist/chunks/compose-DZhTQOt4.cjs.map +0 -1
  209. package/dist/chunks/dashboard-page-CeCLV1tp.js.map +0 -1
  210. package/dist/chunks/dashboard-page-DooFJsxM.cjs +0 -2
  211. package/dist/chunks/dashboard-page-DooFJsxM.cjs.map +0 -1
  212. package/dist/chunks/delegations-8LsxcQM4.cjs +0 -2
  213. package/dist/chunks/delegations-8LsxcQM4.cjs.map +0 -1
  214. package/dist/chunks/delegations-B8gLSt0i.js.map +0 -1
  215. package/dist/chunks/delegations-DRupxJ4_.js.map +0 -1
  216. package/dist/chunks/delegations-DlYwMiAW.cjs +0 -2
  217. package/dist/chunks/delegations-DlYwMiAW.cjs.map +0 -1
  218. package/dist/chunks/designer-CrbBRPUU.js.map +0 -1
  219. package/dist/chunks/designer-D3_lWmfa.cjs +0 -65
  220. package/dist/chunks/designer-D3_lWmfa.cjs.map +0 -1
  221. package/dist/chunks/detail-CVXb5Vy8.cjs +0 -2
  222. package/dist/chunks/detail-CVXb5Vy8.cjs.map +0 -1
  223. package/dist/chunks/detail-_c21WQla.js.map +0 -1
  224. package/dist/chunks/orgs-BpqVu5ka.js.map +0 -1
  225. package/dist/chunks/orgs-D0-5BaHT.cjs +0 -2
  226. package/dist/chunks/orgs-D0-5BaHT.cjs.map +0 -1
  227. package/dist/chunks/users-C9LA-Rd7.cjs +0 -2
  228. package/dist/chunks/users-C9LA-Rd7.cjs.map +0 -1
  229. package/dist/chunks/users-FWrb3ehW.js +0 -214
  230. package/dist/chunks/users-FWrb3ehW.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"format-date-time-D-cx5M8z.cjs","names":[],"sources":["../../src/lib/format-date-time.ts"],"sourcesContent":["import moment from 'moment';\n\n/**\n * Default BPM datetime formatter: `YYYY-MM-DD HH:mm:ss`. Returns `'-'` for\n * `null` / `undefined` so list cells render a consistent placeholder.\n */\nexport function formatDateTime(value: string | null | undefined): string {\n return value ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-';\n}\n"],"mappings":"sFAMA,SAAgB,EAAe,EAA0C,CACvE,OAAO,GAAA,EAAQ,EAAA,QAAA,CAAO,CAAK,CAAC,CAAC,OAAO,qBAAqB,EAAI,GAC/D"}
1
+ {"version":3,"file":"format-date-time-D-cx5M8z.cjs","names":["require_rolldown_runtime","moment","formatDateTime","value"],"sources":["../../src/lib/format-date-time.ts"],"sourcesContent":["import moment from 'moment';\n\n/**\n * Default BPM datetime formatter: `YYYY-MM-DD HH:mm:ss`. Returns `'-'` for\n * `null` / `undefined` so list cells render a consistent placeholder.\n */\nexport function formatDateTime(value: string | null | undefined): string {\n return value ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-';\n}\n"],"mappings":"sFAMA,SAAgBE,EAAe,EAA0C,CACvE,OAAOC,GAAA,EAAQF,EAAA,QAAA,CAAOE,CAAK,CAAC,CAAC,OAAO,qBAAqB,EAAI,GAC/D"}
@@ -0,0 +1,13 @@
1
+ //#region src/lib/format-member-label.ts
2
+ function e(e, t, n = "ascii") {
3
+ if (!t?.trim()) return e;
4
+ switch (n) {
5
+ case "fullwidth": return `${e}(${t})`;
6
+ case "dot": return `${e} · ${t}`;
7
+ case "ascii": return `${e} (${t})`;
8
+ }
9
+ }
10
+ //#endregion
11
+ export { e as t };
12
+
13
+ //# sourceMappingURL=format-member-label-DudVI55w.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-member-label-DudVI55w.js","names":["formatMemberLabel","email","name","style"],"sources":["../../src/lib/format-member-label.ts"],"sourcesContent":["/**\n * How the e-mail is attached to the name: `ascii` is `name (email)`,\n * `fullwidth` is `name(email)` and `dot` is `name · email`. Each call site\n * keeps the separator it always had.\n */\nexport type MemberLabelStyle = 'ascii' | 'dot' | 'fullwidth';\n\n/**\n * `name` followed by the e-mail in the given style. A host may resolve a member\n * with `email: ''` (or a blank string), and then the label is just the name —\n * never `name ()` or `name · `.\n */\nexport function formatMemberLabel(\n name: string,\n email: string | null | undefined,\n style: MemberLabelStyle = 'ascii',\n): string {\n if (!email?.trim()) {\n return name;\n }\n\n switch (style) {\n case 'fullwidth':\n return `${name}(${email})`;\n case 'dot':\n return `${name} · ${email}`;\n case 'ascii':\n return `${name} (${email})`;\n }\n}\n"],"mappings":";AAYA,SAAgBA,EACd,GACA,GACA,IAA0B,SAClB;CACR,IAAI,CAACC,GAAO,KAAK,GACf,OAAOC;CAGT,QAAQC,GAAR;EACE,KAAK,aACH,OAAO,GAAGD,EAAK,GAAGD,EAAM;EAC1B,KAAK,OACH,OAAO,GAAGC,EAAK,KAAKD;EACtB,KAAK,SACH,OAAO,GAAGC,EAAK,IAAID,EAAM;CAC7B;AACF"}
@@ -0,0 +1,2 @@
1
+ function e(e,t,n=`ascii`){if(!t?.trim())return e;switch(n){case`fullwidth`:return`${e}(${t})`;case`dot`:return`${e} · ${t}`;case`ascii`:return`${e} (${t})`}}Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return e}});
2
+ //# sourceMappingURL=format-member-label-t7ANTF62.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-member-label-t7ANTF62.cjs","names":["formatMemberLabel","email","name","style"],"sources":["../../src/lib/format-member-label.ts"],"sourcesContent":["/**\n * How the e-mail is attached to the name: `ascii` is `name (email)`,\n * `fullwidth` is `name(email)` and `dot` is `name · email`. Each call site\n * keeps the separator it always had.\n */\nexport type MemberLabelStyle = 'ascii' | 'dot' | 'fullwidth';\n\n/**\n * `name` followed by the e-mail in the given style. A host may resolve a member\n * with `email: ''` (or a blank string), and then the label is just the name —\n * never `name ()` or `name · `.\n */\nexport function formatMemberLabel(\n name: string,\n email: string | null | undefined,\n style: MemberLabelStyle = 'ascii',\n): string {\n if (!email?.trim()) {\n return name;\n }\n\n switch (style) {\n case 'fullwidth':\n return `${name}(${email})`;\n case 'dot':\n return `${name} · ${email}`;\n case 'ascii':\n return `${name} (${email})`;\n }\n}\n"],"mappings":"AAYA,SAAgBA,EACd,EACA,EACA,EAA0B,QAClB,CACR,GAAI,CAACC,GAAO,KAAK,EACf,OAAOC,EAGT,OAAQC,EAAR,CACE,IAAK,YACH,MAAO,GAAGD,EAAK,GAAGD,EAAM,GAC1B,IAAK,MACH,MAAO,GAAGC,EAAK,KAAKD,IACtB,IAAK,QACH,MAAO,GAAGC,EAAK,IAAID,EAAM,EAC7B,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"login-AtVxZBsC.cjs","names":[],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\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":"login-AtVxZBsC.cjs","names":["useRouterAdapter","useAuth","useState","listApiTestMembers","useCallback","styles","Typography","Input","Button","LoginIcon","require_router_adapter","require_auth_provider","react","_mezzanine_ui_react","_rytass_bpm_core_client","react_jsx_runtime","_mezzanine_ui_icons","login_module_default","DEFAULT_LOGO","DEFAULT_IDENTIFIER","DEFAULT_PASSWORD","LoginView","router","testMembers","setTestMembers","error","setError","identifier","setIdentifier","defaultIdentifier","password","setPassword","defaultPassword","submitting","setSubmitting","member","resolveNextPath","defaultNextPath","loading","handleSubmit","event","login","loginError","readErrorMessage","handleIdentifierChange","handlePasswordChange","logoSrc","brandTitle","brandSubtitle","testMember","fallback","next","readBrowserSearchParams"],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '登入失敗';\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"login-C6qlTC6F.js","names":[],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\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":"login-C6qlTC6F.js","names":["styles","useRouterAdapter","useAuth","login_module_default","DEFAULT_LOGO","DEFAULT_IDENTIFIER","DEFAULT_PASSWORD","LoginView","router","testMembers","setTestMembers","useState","error","setError","identifier","setIdentifier","defaultIdentifier","password","setPassword","defaultPassword","submitting","setSubmitting","useEffect","member","resolveNextPath","defaultNextPath","loading","listApiTestMembers","handleSubmit","useCallback","event","login","loginError","readErrorMessage","handleIdentifierChange","handlePasswordChange","logoSrc","Typography","brandTitle","brandSubtitle","Input","Button","LoginIcon","testMember","fallback","next","readBrowserSearchParams"],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '登入失敗';\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"notifications-Bc4mgSds.cjs","names":[],"sources":["../../src/views/settings/notifications/notification-settings.module.scss","../../src/views/settings/notifications/SettingsNotificationsView.tsx"],"sourcesContent":[".preferenceFilter {\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.segmentFilterControl {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n min-width: 0;\n white-space: nowrap;\n\n :global(.mzn-input-check-group--segmented) {\n align-self: flex-start;\n transform: translateY(-7px);\n }\n}\n\n.segmentFilterLabel {\n flex: 0 0 auto;\n color: var(--mzn-color-text-primary, #1f2937);\n font-size: 13px;\n line-height: 16px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n CSSProperties,\n ReactElement,\n useEffect,\n useState,\n} from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n PageHeader,\n RadioGroup,\n Section,\n SectionGroup,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport { useAuth } from '../../../lib/auth-provider';\nimport {\n NotificationDigestMode,\n NotificationPreferenceRecord,\n readNotificationPreference,\n updateNotificationPreference,\n} from '@rytass/bpm-core-client/workflow';\nimport styles from './notification-settings.module.scss';\n\ninterface DigestOption {\n readonly id: NotificationDigestMode;\n readonly name: string;\n}\n\ntype EnabledSegmentValue = 'OFF' | 'ON';\n\ninterface EnabledSegmentOption {\n readonly id: EnabledSegmentValue;\n readonly name: string;\n}\n\nconst DIGEST_OPTIONS: readonly DigestOption[] = [\n { id: 'INSTANT', name: '即時通知' },\n { id: 'DAILY', name: '每日摘要' },\n];\n\nconst ENABLED_SEGMENT_OPTIONS: readonly EnabledSegmentOption[] = [\n { id: 'ON', name: '開' },\n { id: 'OFF', name: '關' },\n];\n\nconst DEFAULT_PREFERENCE: NotificationPreferenceRecord = {\n emailDigestMode: 'INSTANT',\n emailEnabled: true,\n inAppEnabled: true,\n memberId: '',\n quietHoursEnd: null,\n quietHoursStart: null,\n updatedAt: '',\n};\n\n\nexport function SettingsNotificationsView(): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [preference, setPreference] =\n useState<NotificationPreferenceRecord>(DEFAULT_PREFERENCE);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect((): void => {\n if (!currentMemberId) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n readNotificationPreference(currentMemberId)\n .then((nextPreference): void => {\n setPreference(nextPreference);\n })\n .catch((requestError: unknown): void => {\n setError(readErrorMessage(requestError));\n })\n .finally((): void => {\n setLoading(false);\n });\n }, [currentMemberId]);\n\n async function handlePreferenceChange(\n nextPreference: NotificationPreferenceRecord,\n ): Promise<void> {\n if (!currentMemberId || saving) {\n return;\n }\n\n const previousPreference = preference;\n setPreference(nextPreference);\n setSaving(true);\n\n try {\n setPreference(\n await updateNotificationPreference({\n emailDigestMode: nextPreference.emailDigestMode,\n emailEnabled: nextPreference.emailEnabled,\n inAppEnabled: nextPreference.inAppEnabled,\n memberId: currentMemberId,\n quietHoursEnd: nextPreference.quietHoursEnd,\n quietHoursStart: nextPreference.quietHoursStart,\n }),\n );\n } catch (requestError: unknown) {\n setPreference(previousPreference);\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const controlsDisabled = loading || saving;\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"調整站內通知、Email 通知與摘要頻率。\"\n title=\"通知設定\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.preferenceFilter} isDirty={false}>\n <FilterLine>\n <Filter minWidth={160} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"inAppEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n 站內通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"inAppEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n inAppEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.inAppEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={180} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"emailEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.emailEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={280} span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"emailDigestMode\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 頻率\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailDigestMode\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailDigestMode: readDigestMode(\n event.target.value,\n ),\n });\n }}\n options={[...DIGEST_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={preference.emailDigestMode}\n />\n </div>\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 偏好設定會立即生效。\n </Typography>\n )}\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nconst FILTER_FIELD_STYLE = {\n minWidth: 0,\n whiteSpace: 'nowrap',\n} satisfies CSSProperties;\n\nfunction readDigestMode(value: unknown): NotificationDigestMode {\n return value === 'DAILY' ? 'DAILY' : 'INSTANT';\n}\n\nfunction readEnabledSegmentValue(value: unknown): boolean {\n return value !== 'OFF';\n}\n\nfunction readEnabledSegmentValueId(enabled: boolean): EnabledSegmentValue {\n return enabled ? 'ON' : 'OFF';\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":"notifications-Bc4mgSds.cjs","names":["useAuth","useState","updateNotificationPreference","_Fragment","PageHeader","ContentHeader","SectionGroup","Section","FilterArea","styles","FilterLine","Filter","FormField","FormFieldLayout","RadioGroup","Typography","require_rolldown_runtime","require_auth_provider","react","_mezzanine_ui_react","react_jsx_runtime","_rytass_bpm_core_client_workflow","_mezzanine_ui_core_form","_mezzanine_ui_react_ContentHeader","notification_settings_module_default","DIGEST_OPTIONS","ENABLED_SEGMENT_OPTIONS","DEFAULT_PREFERENCE","SettingsNotificationsView","currentMemberId","member","preference","setPreference","loading","setLoading","saving","setSaving","error","setError","nextPreference","readErrorMessage","requestError","handlePreferenceChange","previousPreference","controlsDisabled","FILTER_FIELD_STYLE","readEnabledSegmentValue","event","readEnabledSegmentValueId","readDigestMode","value","enabled"],"sources":["../../src/views/settings/notifications/notification-settings.module.scss","../../src/views/settings/notifications/SettingsNotificationsView.tsx"],"sourcesContent":[".preferenceFilter {\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.segmentFilterControl {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n min-width: 0;\n white-space: nowrap;\n\n :global(.mzn-input-check-group--segmented) {\n align-self: flex-start;\n transform: translateY(-7px);\n }\n}\n\n.segmentFilterLabel {\n flex: 0 0 auto;\n color: var(--mzn-color-text-primary, #1f2937);\n font-size: 13px;\n line-height: 16px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n CSSProperties,\n ReactElement,\n useEffect,\n useState,\n} from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n PageHeader,\n RadioGroup,\n Section,\n SectionGroup,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport { useAuth } from '../../../lib/auth-provider';\nimport {\n NotificationDigestMode,\n NotificationPreferenceRecord,\n readNotificationPreference,\n updateNotificationPreference,\n} from '@rytass/bpm-core-client/workflow';\nimport styles from './notification-settings.module.scss';\n\ninterface DigestOption {\n readonly id: NotificationDigestMode;\n readonly name: string;\n}\n\ntype EnabledSegmentValue = 'OFF' | 'ON';\n\ninterface EnabledSegmentOption {\n readonly id: EnabledSegmentValue;\n readonly name: string;\n}\n\nconst DIGEST_OPTIONS: readonly DigestOption[] = [\n { id: 'INSTANT', name: '即時通知' },\n { id: 'DAILY', name: '每日摘要' },\n];\n\nconst ENABLED_SEGMENT_OPTIONS: readonly EnabledSegmentOption[] = [\n { id: 'ON', name: '開' },\n { id: 'OFF', name: '關' },\n];\n\nconst DEFAULT_PREFERENCE: NotificationPreferenceRecord = {\n emailDigestMode: 'INSTANT',\n emailEnabled: true,\n inAppEnabled: true,\n memberId: '',\n quietHoursEnd: null,\n quietHoursStart: null,\n updatedAt: '',\n};\n\n\nexport function SettingsNotificationsView(): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [preference, setPreference] =\n useState<NotificationPreferenceRecord>(DEFAULT_PREFERENCE);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect((): void => {\n if (!currentMemberId) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n readNotificationPreference(currentMemberId)\n .then((nextPreference): void => {\n setPreference(nextPreference);\n })\n .catch((requestError: unknown): void => {\n setError(readErrorMessage(requestError));\n })\n .finally((): void => {\n setLoading(false);\n });\n }, [currentMemberId]);\n\n async function handlePreferenceChange(\n nextPreference: NotificationPreferenceRecord,\n ): Promise<void> {\n if (!currentMemberId || saving) {\n return;\n }\n\n const previousPreference = preference;\n setPreference(nextPreference);\n setSaving(true);\n\n try {\n setPreference(\n await updateNotificationPreference({\n emailDigestMode: nextPreference.emailDigestMode,\n emailEnabled: nextPreference.emailEnabled,\n inAppEnabled: nextPreference.inAppEnabled,\n memberId: currentMemberId,\n quietHoursEnd: nextPreference.quietHoursEnd,\n quietHoursStart: nextPreference.quietHoursStart,\n }),\n );\n } catch (requestError: unknown) {\n setPreference(previousPreference);\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const controlsDisabled = loading || saving;\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"調整站內通知、Email 通知與摘要頻率。\"\n title=\"通知設定\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.preferenceFilter} isDirty={false}>\n <FilterLine>\n <Filter minWidth={160} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"inAppEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n 站內通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"inAppEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n inAppEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.inAppEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={180} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"emailEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.emailEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={280} span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"emailDigestMode\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 頻率\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailDigestMode\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailDigestMode: readDigestMode(\n event.target.value,\n ),\n });\n }}\n options={[...DIGEST_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={preference.emailDigestMode}\n />\n </div>\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 偏好設定會立即生效。\n </Typography>\n )}\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nconst FILTER_FIELD_STYLE = {\n minWidth: 0,\n whiteSpace: 'nowrap',\n} satisfies CSSProperties;\n\nfunction readDigestMode(value: unknown): NotificationDigestMode {\n return value === 'DAILY' ? 'DAILY' : 'INSTANT';\n}\n\nfunction readEnabledSegmentValue(value: unknown): boolean {\n return value !== 'OFF';\n}\n\nfunction readEnabledSegmentValueId(enabled: boolean): EnabledSegmentValue {\n return enabled ? 'ON' : 'OFF';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"notifications-DJ5wzCye.js","names":[],"sources":["../../src/views/settings/notifications/notification-settings.module.scss","../../src/views/settings/notifications/SettingsNotificationsView.tsx"],"sourcesContent":[".preferenceFilter {\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.segmentFilterControl {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n min-width: 0;\n white-space: nowrap;\n\n :global(.mzn-input-check-group--segmented) {\n align-self: flex-start;\n transform: translateY(-7px);\n }\n}\n\n.segmentFilterLabel {\n flex: 0 0 auto;\n color: var(--mzn-color-text-primary, #1f2937);\n font-size: 13px;\n line-height: 16px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n CSSProperties,\n ReactElement,\n useEffect,\n useState,\n} from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n PageHeader,\n RadioGroup,\n Section,\n SectionGroup,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport { useAuth } from '../../../lib/auth-provider';\nimport {\n NotificationDigestMode,\n NotificationPreferenceRecord,\n readNotificationPreference,\n updateNotificationPreference,\n} from '@rytass/bpm-core-client/workflow';\nimport styles from './notification-settings.module.scss';\n\ninterface DigestOption {\n readonly id: NotificationDigestMode;\n readonly name: string;\n}\n\ntype EnabledSegmentValue = 'OFF' | 'ON';\n\ninterface EnabledSegmentOption {\n readonly id: EnabledSegmentValue;\n readonly name: string;\n}\n\nconst DIGEST_OPTIONS: readonly DigestOption[] = [\n { id: 'INSTANT', name: '即時通知' },\n { id: 'DAILY', name: '每日摘要' },\n];\n\nconst ENABLED_SEGMENT_OPTIONS: readonly EnabledSegmentOption[] = [\n { id: 'ON', name: '開' },\n { id: 'OFF', name: '關' },\n];\n\nconst DEFAULT_PREFERENCE: NotificationPreferenceRecord = {\n emailDigestMode: 'INSTANT',\n emailEnabled: true,\n inAppEnabled: true,\n memberId: '',\n quietHoursEnd: null,\n quietHoursStart: null,\n updatedAt: '',\n};\n\n\nexport function SettingsNotificationsView(): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [preference, setPreference] =\n useState<NotificationPreferenceRecord>(DEFAULT_PREFERENCE);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect((): void => {\n if (!currentMemberId) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n readNotificationPreference(currentMemberId)\n .then((nextPreference): void => {\n setPreference(nextPreference);\n })\n .catch((requestError: unknown): void => {\n setError(readErrorMessage(requestError));\n })\n .finally((): void => {\n setLoading(false);\n });\n }, [currentMemberId]);\n\n async function handlePreferenceChange(\n nextPreference: NotificationPreferenceRecord,\n ): Promise<void> {\n if (!currentMemberId || saving) {\n return;\n }\n\n const previousPreference = preference;\n setPreference(nextPreference);\n setSaving(true);\n\n try {\n setPreference(\n await updateNotificationPreference({\n emailDigestMode: nextPreference.emailDigestMode,\n emailEnabled: nextPreference.emailEnabled,\n inAppEnabled: nextPreference.inAppEnabled,\n memberId: currentMemberId,\n quietHoursEnd: nextPreference.quietHoursEnd,\n quietHoursStart: nextPreference.quietHoursStart,\n }),\n );\n } catch (requestError: unknown) {\n setPreference(previousPreference);\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const controlsDisabled = loading || saving;\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"調整站內通知、Email 通知與摘要頻率。\"\n title=\"通知設定\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.preferenceFilter} isDirty={false}>\n <FilterLine>\n <Filter minWidth={160} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"inAppEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n 站內通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"inAppEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n inAppEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.inAppEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={180} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"emailEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.emailEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={280} span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"emailDigestMode\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 頻率\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailDigestMode\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailDigestMode: readDigestMode(\n event.target.value,\n ),\n });\n }}\n options={[...DIGEST_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={preference.emailDigestMode}\n />\n </div>\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 偏好設定會立即生效。\n </Typography>\n )}\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nconst FILTER_FIELD_STYLE = {\n minWidth: 0,\n whiteSpace: 'nowrap',\n} satisfies CSSProperties;\n\nfunction readDigestMode(value: unknown): NotificationDigestMode {\n return value === 'DAILY' ? 'DAILY' : 'INSTANT';\n}\n\nfunction readEnabledSegmentValue(value: unknown): boolean {\n return value !== 'OFF';\n}\n\nfunction readEnabledSegmentValueId(enabled: boolean): EnabledSegmentValue {\n return enabled ? 'ON' : 'OFF';\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":"notifications-DJ5wzCye.js","names":["_Fragment","styles","useAuth","Fragment$1","ContentHeader","notification_settings_module_default","DIGEST_OPTIONS","ENABLED_SEGMENT_OPTIONS","DEFAULT_PREFERENCE","SettingsNotificationsView","currentMemberId","member","preference","setPreference","useState","loading","setLoading","saving","setSaving","error","setError","readNotificationPreference","nextPreference","readErrorMessage","requestError","handlePreferenceChange","previousPreference","updateNotificationPreference","controlsDisabled","PageHeader","SectionGroup","Section","FilterArea","FilterLine","Filter","FormField","FormFieldLayout","FILTER_FIELD_STYLE","RadioGroup","readEnabledSegmentValue","event","readEnabledSegmentValueId","readDigestMode","Typography","value","enabled"],"sources":["../../src/views/settings/notifications/notification-settings.module.scss","../../src/views/settings/notifications/SettingsNotificationsView.tsx"],"sourcesContent":[".preferenceFilter {\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.segmentFilterControl {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n min-width: 0;\n white-space: nowrap;\n\n :global(.mzn-input-check-group--segmented) {\n align-self: flex-start;\n transform: translateY(-7px);\n }\n}\n\n.segmentFilterLabel {\n flex: 0 0 auto;\n color: var(--mzn-color-text-primary, #1f2937);\n font-size: 13px;\n line-height: 16px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n CSSProperties,\n ReactElement,\n useEffect,\n useState,\n} from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n PageHeader,\n RadioGroup,\n Section,\n SectionGroup,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport { useAuth } from '../../../lib/auth-provider';\nimport {\n NotificationDigestMode,\n NotificationPreferenceRecord,\n readNotificationPreference,\n updateNotificationPreference,\n} from '@rytass/bpm-core-client/workflow';\nimport styles from './notification-settings.module.scss';\n\ninterface DigestOption {\n readonly id: NotificationDigestMode;\n readonly name: string;\n}\n\ntype EnabledSegmentValue = 'OFF' | 'ON';\n\ninterface EnabledSegmentOption {\n readonly id: EnabledSegmentValue;\n readonly name: string;\n}\n\nconst DIGEST_OPTIONS: readonly DigestOption[] = [\n { id: 'INSTANT', name: '即時通知' },\n { id: 'DAILY', name: '每日摘要' },\n];\n\nconst ENABLED_SEGMENT_OPTIONS: readonly EnabledSegmentOption[] = [\n { id: 'ON', name: '開' },\n { id: 'OFF', name: '關' },\n];\n\nconst DEFAULT_PREFERENCE: NotificationPreferenceRecord = {\n emailDigestMode: 'INSTANT',\n emailEnabled: true,\n inAppEnabled: true,\n memberId: '',\n quietHoursEnd: null,\n quietHoursStart: null,\n updatedAt: '',\n};\n\n\nexport function SettingsNotificationsView(): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [preference, setPreference] =\n useState<NotificationPreferenceRecord>(DEFAULT_PREFERENCE);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect((): void => {\n if (!currentMemberId) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n readNotificationPreference(currentMemberId)\n .then((nextPreference): void => {\n setPreference(nextPreference);\n })\n .catch((requestError: unknown): void => {\n setError(readErrorMessage(requestError));\n })\n .finally((): void => {\n setLoading(false);\n });\n }, [currentMemberId]);\n\n async function handlePreferenceChange(\n nextPreference: NotificationPreferenceRecord,\n ): Promise<void> {\n if (!currentMemberId || saving) {\n return;\n }\n\n const previousPreference = preference;\n setPreference(nextPreference);\n setSaving(true);\n\n try {\n setPreference(\n await updateNotificationPreference({\n emailDigestMode: nextPreference.emailDigestMode,\n emailEnabled: nextPreference.emailEnabled,\n inAppEnabled: nextPreference.inAppEnabled,\n memberId: currentMemberId,\n quietHoursEnd: nextPreference.quietHoursEnd,\n quietHoursStart: nextPreference.quietHoursStart,\n }),\n );\n } catch (requestError: unknown) {\n setPreference(previousPreference);\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const controlsDisabled = loading || saving;\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"調整站內通知、Email 通知與摘要頻率。\"\n title=\"通知設定\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.preferenceFilter} isDirty={false}>\n <FilterLine>\n <Filter minWidth={160} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"inAppEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n 站內通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"inAppEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n inAppEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.inAppEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={180} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"emailEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.emailEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={280} span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"emailDigestMode\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 頻率\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailDigestMode\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailDigestMode: readDigestMode(\n event.target.value,\n ),\n });\n }}\n options={[...DIGEST_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={preference.emailDigestMode}\n />\n </div>\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 偏好設定會立即生效。\n </Typography>\n )}\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nconst FILTER_FIELD_STYLE = {\n minWidth: 0,\n whiteSpace: 'nowrap',\n} satisfies CSSProperties;\n\nfunction readDigestMode(value: unknown): NotificationDigestMode {\n return value === 'DAILY' ? 'DAILY' : 'INSTANT';\n}\n\nfunction readEnabledSegmentValue(value: unknown): boolean {\n return value !== 'OFF';\n}\n\nfunction readEnabledSegmentValueId(enabled: boolean): EnabledSegmentValue {\n return enabled ? 'ON' : 'OFF';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}