@rytass/bpm-core-react 0.20.1 → 0.21.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 (176) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +1 -1
  3. package/dist/approval-instance-list-page.css +1 -1
  4. package/dist/chunks/{FormBuilderView-CFRiw04B.js → FormBuilderView-6KdApi-D.js} +667 -584
  5. package/dist/chunks/FormBuilderView-6KdApi-D.js.map +1 -0
  6. package/dist/chunks/FormBuilderView-CGKkuVFi.cjs +4 -0
  7. package/dist/chunks/FormBuilderView-CGKkuVFi.cjs.map +1 -0
  8. package/dist/chunks/{FormRendererView-lYSaR8CQ.cjs → FormRendererView-B-xycC9S.cjs} +3 -3
  9. package/dist/chunks/{FormRendererView-lYSaR8CQ.cjs.map → FormRendererView-B-xycC9S.cjs.map} +1 -1
  10. package/dist/chunks/{FormRendererView-GfbVWEGa.js → FormRendererView-BcQKUi1A.js} +238 -238
  11. package/dist/chunks/{FormRendererView-GfbVWEGa.js.map → FormRendererView-BcQKUi1A.js.map} +1 -1
  12. package/dist/chunks/approval-instance-list-page-BI5fIXc-.js +724 -0
  13. package/dist/chunks/approval-instance-list-page-BI5fIXc-.js.map +1 -0
  14. package/dist/chunks/approval-instance-list-page-Cv5I1YPX.cjs +2 -0
  15. package/dist/chunks/approval-instance-list-page-Cv5I1YPX.cjs.map +1 -0
  16. package/dist/chunks/{categories-DQUtVEiV.cjs → categories-CyyqG6wG.cjs} +2 -2
  17. package/dist/chunks/{categories-DQUtVEiV.cjs.map → categories-CyyqG6wG.cjs.map} +1 -1
  18. package/dist/chunks/{categories-BAdxjNSA.js → categories-f7E-RJEc.js} +9 -9
  19. package/dist/chunks/{categories-BAdxjNSA.js.map → categories-f7E-RJEc.js.map} +1 -1
  20. package/dist/chunks/{compose-egQlRjuZ.js → compose-DFuLPmOA.js} +5 -5
  21. package/dist/chunks/{compose-egQlRjuZ.js.map → compose-DFuLPmOA.js.map} +1 -1
  22. package/dist/chunks/compose-DK3ocWwG.cjs +2 -0
  23. package/dist/chunks/compose-DK3ocWwG.cjs.map +1 -0
  24. package/dist/chunks/{delegations-CVmtpkRn.cjs → delegations-ClRCwD1I.cjs} +2 -2
  25. package/dist/chunks/{delegations-CVmtpkRn.cjs.map → delegations-ClRCwD1I.cjs.map} +1 -1
  26. package/dist/chunks/{delegations-9-yvWWws.js → delegations-CqND-Qtz.js} +5 -5
  27. package/dist/chunks/{delegations-9-yvWWws.js.map → delegations-CqND-Qtz.js.map} +1 -1
  28. package/dist/chunks/{delegations-CrfjsH94.cjs → delegations-D43VDSz1.cjs} +2 -2
  29. package/dist/chunks/{delegations-CrfjsH94.cjs.map → delegations-D43VDSz1.cjs.map} +1 -1
  30. package/dist/chunks/{delegations-CxnNvTCc.js → delegations-zisffLwk.js} +5 -5
  31. package/dist/chunks/{delegations-CxnNvTCc.js.map → delegations-zisffLwk.js.map} +1 -1
  32. package/dist/chunks/designer-CAUWEKJ4.cjs +69 -0
  33. package/dist/chunks/designer-CAUWEKJ4.cjs.map +1 -0
  34. package/dist/chunks/{designer-CZDlQ9Nf.js → designer-CCy5EbOe.js} +2380 -1786
  35. package/dist/chunks/designer-CCy5EbOe.js.map +1 -0
  36. package/dist/chunks/detail-C5KrUC7s.cjs +5 -0
  37. package/dist/chunks/detail-C5KrUC7s.cjs.map +1 -0
  38. package/dist/chunks/{detail-ByZV1XaI.js → detail-Dnc4PPWO.js} +804 -777
  39. package/dist/chunks/detail-Dnc4PPWO.js.map +1 -0
  40. package/dist/chunks/instance-error-message-BEM4O_Kj.js +208 -0
  41. package/dist/chunks/instance-error-message-BEM4O_Kj.js.map +1 -0
  42. package/dist/chunks/instance-error-message-BpCLCOjp.cjs +2 -0
  43. package/dist/chunks/instance-error-message-BpCLCOjp.cjs.map +1 -0
  44. package/dist/chunks/organization-time-zone-provider-B7CXQril.cjs +2 -0
  45. package/dist/chunks/organization-time-zone-provider-B7CXQril.cjs.map +1 -0
  46. package/dist/chunks/organization-time-zone-provider-DNL8yo4-.js +42 -0
  47. package/dist/chunks/organization-time-zone-provider-DNL8yo4-.js.map +1 -0
  48. package/dist/chunks/{providers-BbCs23sk.js → providers-CQWekD7_.js} +197 -196
  49. package/dist/chunks/providers-CQWekD7_.js.map +1 -0
  50. package/dist/chunks/providers-VUOh6i0i.cjs +2 -0
  51. package/dist/chunks/providers-VUOh6i0i.cjs.map +1 -0
  52. package/dist/components/approval-instance-field-filter-controls.d.cts +12 -0
  53. package/dist/components/approval-instance-field-filter-controls.d.ts +12 -0
  54. package/dist/components/approval-instance-field-filters.d.cts +86 -0
  55. package/dist/components/approval-instance-field-filters.d.ts +86 -0
  56. package/dist/components/approval-instance-list-page.d.cts +4 -0
  57. package/dist/components/approval-instance-list-page.d.ts +4 -0
  58. package/dist/components/notification-drawer-options.d.cts +18 -0
  59. package/dist/components/notification-drawer-options.d.ts +18 -0
  60. package/dist/index.cjs +1 -1
  61. package/dist/index.cjs.map +1 -1
  62. package/dist/index.d.cts +1 -0
  63. package/dist/index.d.ts +1 -0
  64. package/dist/index.js +23 -23
  65. package/dist/index.js.map +1 -1
  66. package/dist/lib/organization-time-zone-provider.d.cts +20 -0
  67. package/dist/lib/organization-time-zone-provider.d.ts +20 -0
  68. package/dist/lib/organization-time-zone.d.cts +8 -0
  69. package/dist/lib/organization-time-zone.d.ts +8 -0
  70. package/dist/lib/providers.d.cts +10 -1
  71. package/dist/lib/providers.d.ts +10 -1
  72. package/dist/next/BPMNextProviders.d.cts +8 -1
  73. package/dist/next/BPMNextProviders.d.ts +8 -1
  74. package/dist/next/index.cjs +1 -1
  75. package/dist/next/index.cjs.map +1 -1
  76. package/dist/next/index.js +12 -11
  77. package/dist/next/index.js.map +1 -1
  78. package/dist/next/workflow-chat-route.cjs +2 -2
  79. package/dist/next/workflow-chat-route.cjs.map +1 -1
  80. package/dist/next/workflow-chat-route.js +13 -13
  81. package/dist/pages/admin/delegations/index.cjs +1 -1
  82. package/dist/pages/admin/delegations/index.js +1 -1
  83. package/dist/pages/delegations/index.cjs +1 -1
  84. package/dist/pages/delegations/index.js +1 -1
  85. package/dist/pages/instances/detail/index.cjs +1 -1
  86. package/dist/pages/instances/detail/index.js +1 -1
  87. package/dist/pages/templates/categories/index.cjs +1 -1
  88. package/dist/pages/templates/categories/index.js +1 -1
  89. package/dist/pages/templates/compose/index.cjs +1 -1
  90. package/dist/pages/templates/compose/index.js +1 -1
  91. package/dist/pages/templates/designer/index.cjs +1 -1
  92. package/dist/pages/templates/designer/index.js +1 -1
  93. package/dist/views/admin/delegations/index.cjs +1 -1
  94. package/dist/views/admin/delegations/index.js +1 -1
  95. package/dist/views/admin/index.cjs +1 -1
  96. package/dist/views/admin/index.js +1 -1
  97. package/dist/views/admin/webhook-endpoints/index.cjs +1 -1
  98. package/dist/views/admin/webhook-endpoints/index.cjs.map +1 -1
  99. package/dist/views/admin/webhook-endpoints/index.js +60 -60
  100. package/dist/views/admin/webhook-endpoints/index.js.map +1 -1
  101. package/dist/views/cc/index.cjs +1 -1
  102. package/dist/views/cc/index.js +1 -1
  103. package/dist/views/delegations/index.cjs +1 -1
  104. package/dist/views/delegations/index.js +1 -1
  105. package/dist/views/forms/builder/FormBuilderView.d.cts +8 -1
  106. package/dist/views/forms/builder/FormBuilderView.d.ts +8 -1
  107. package/dist/views/forms/builder/index.cjs +1 -1
  108. package/dist/views/forms/builder/index.js +1 -1
  109. package/dist/views/forms/renderer/index.cjs +1 -1
  110. package/dist/views/forms/renderer/index.js +1 -1
  111. package/dist/views/instances/approver-filled-form.d.cts +8 -5
  112. package/dist/views/instances/approver-filled-form.d.ts +8 -5
  113. package/dist/views/instances/detail/index.cjs +1 -1
  114. package/dist/views/instances/detail/index.js +1 -1
  115. package/dist/views/instances/detail/sections/InstanceTasksSection.d.cts +2 -1
  116. package/dist/views/instances/detail/sections/InstanceTasksSection.d.ts +2 -1
  117. package/dist/views/instances/detail/sections/shared.d.cts +0 -1
  118. package/dist/views/instances/detail/sections/shared.d.ts +0 -1
  119. package/dist/views/instances/new/index.cjs +1 -1
  120. package/dist/views/instances/new/index.cjs.map +1 -1
  121. package/dist/views/instances/new/index.js +8 -8
  122. package/dist/views/instances/new/index.js.map +1 -1
  123. package/dist/views/search/index.cjs +1 -1
  124. package/dist/views/search/index.js +1 -1
  125. package/dist/views/sent/index.cjs +1 -1
  126. package/dist/views/sent/index.js +1 -1
  127. package/dist/views/templates/categories/index.cjs +1 -1
  128. package/dist/views/templates/categories/index.js +1 -1
  129. package/dist/views/templates/compose/index.cjs +1 -1
  130. package/dist/views/templates/compose/index.js +1 -1
  131. package/dist/views/templates/designer/NotifyWebhookTargetsEditor.d.cts +2 -0
  132. package/dist/views/templates/designer/NotifyWebhookTargetsEditor.d.ts +2 -0
  133. package/dist/views/templates/designer/approver-resolver-model.d.cts +105 -1
  134. package/dist/views/templates/designer/approver-resolver-model.d.ts +105 -1
  135. package/dist/views/templates/designer/dynamic-form-resolver-options.d.cts +5 -0
  136. package/dist/views/templates/designer/dynamic-form-resolver-options.d.ts +5 -0
  137. package/dist/views/templates/designer/index.cjs +1 -1
  138. package/dist/views/templates/designer/index.js +1 -1
  139. package/dist/views/templates/designer/node-actor-resolver-options.d.cts +44 -1
  140. package/dist/views/templates/designer/node-actor-resolver-options.d.ts +44 -1
  141. package/dist/views/templates/designer/notify-recipient-options.d.cts +81 -0
  142. package/dist/views/templates/designer/notify-recipient-options.d.ts +81 -0
  143. package/dist/views/templates/designer/position-level-options.d.cts +3 -1
  144. package/dist/views/templates/designer/position-level-options.d.ts +3 -1
  145. package/dist/views/templates/designer/position-scope.d.cts +51 -0
  146. package/dist/views/templates/designer/position-scope.d.ts +51 -0
  147. package/dist/views/templates/index.cjs +1 -1
  148. package/dist/views/templates/index.cjs.map +1 -1
  149. package/dist/views/templates/index.js +7 -7
  150. package/package.json +3 -3
  151. package/dist/chunks/FormBuilderView-CFRiw04B.js.map +0 -1
  152. package/dist/chunks/FormBuilderView-CpUgbSbt.cjs +0 -3
  153. package/dist/chunks/FormBuilderView-CpUgbSbt.cjs.map +0 -1
  154. package/dist/chunks/approval-instance-list-page-BnI4t5mD.js +0 -279
  155. package/dist/chunks/approval-instance-list-page-BnI4t5mD.js.map +0 -1
  156. package/dist/chunks/approval-instance-list-page-fqvmkBlg.cjs +0 -2
  157. package/dist/chunks/approval-instance-list-page-fqvmkBlg.cjs.map +0 -1
  158. package/dist/chunks/compose-Bb5bUVFd.cjs +0 -2
  159. package/dist/chunks/compose-Bb5bUVFd.cjs.map +0 -1
  160. package/dist/chunks/designer-CX9bhYJS.cjs +0 -69
  161. package/dist/chunks/designer-CX9bhYJS.cjs.map +0 -1
  162. package/dist/chunks/designer-CZDlQ9Nf.js.map +0 -1
  163. package/dist/chunks/detail-ByZV1XaI.js.map +0 -1
  164. package/dist/chunks/detail-ChseSF55.cjs +0 -5
  165. package/dist/chunks/detail-ChseSF55.cjs.map +0 -1
  166. package/dist/chunks/instance-error-message-DTARGbGo.cjs +0 -2
  167. package/dist/chunks/instance-error-message-DTARGbGo.cjs.map +0 -1
  168. package/dist/chunks/instance-error-message-q6gy5evy.js +0 -120
  169. package/dist/chunks/instance-error-message-q6gy5evy.js.map +0 -1
  170. package/dist/chunks/language-provider-Bdnx3Gxl.cjs +0 -2
  171. package/dist/chunks/language-provider-Bdnx3Gxl.cjs.map +0 -1
  172. package/dist/chunks/language-provider-phkWOapf.js +0 -17
  173. package/dist/chunks/language-provider-phkWOapf.js.map +0 -1
  174. package/dist/chunks/providers-BbCs23sk.js.map +0 -1
  175. package/dist/chunks/providers-D_KAgOOu.cjs +0 -2
  176. package/dist/chunks/providers-D_KAgOOu.cjs.map +0 -1
@@ -1,2 +0,0 @@
1
- let e=require("@rytass/bpm-core-shared"),t=require("@rytass/bpm-core-client/form"),n=require("@rytass/bpm-core-shared/form");function r(t,n){let r=(0,e.readApproverFilledFieldKeys)(n);return{...t,fields:t.fields.filter(e=>!r.has(e.fieldKey))}}function i(t,n){return(0,e.omitFormDataKeys)(t,(0,e.readApproverFilledFieldKeys)(n))}function a(t,n){let r=(0,e.readNodeFillableFieldKeys)(n),i=o(t,n);return{...i,fields:i.fields.filter(e=>r.has(e.fieldKey))}}function o(t,n){let r=(0,e.readNodeFillableFieldKeys)(n),i=(0,e.readNodeRequiredFieldKeys)(n);return{...t,fields:t.fields.map(e=>r.has(e.fieldKey)?{...e,required:i.has(e.fieldKey),requiredWhen:void 0}:e)}}var s=e=>e.nodes.filter(e=>e.type===`userTask`);function c(t,n){let r=n===null?new Set:(0,e.readNodeFillableFieldKeys)(n);return s(t).reduce((t,n)=>({...Object.fromEntries([...(0,e.readNodeFillableFieldKeys)(n)].map(e=>[e,r.has(e)?`本關補填`:`${n.data.label||n.id}補填`])),...t}),{})}function l(t,n){return Object.fromEntries([...(0,e.readNodeFillableFieldKeys)(n)].filter(e=>Object.prototype.hasOwnProperty.call(t,e)).map(e=>[e,t[e]??null]))}function u({draft:t,node:r,schema:i,stored:a,values:o}){let s=(0,e.readNodeFillableFieldKeys)(r),c=i.fields.filter(e=>{let t=o[e.fieldKey];return(0,n.isTableFieldDefinition)(e)&&s.has(e.fieldKey)&&!Object.prototype.hasOwnProperty.call(a,e.fieldKey)&&Array.isArray(t)&&t.length>0});return{...Object.fromEntries(c.map(e=>[e.fieldKey,o[e.fieldKey]])),...t}}function d(e){return e.includes(`form data changed; refresh and retry`)}var f=/^formData\.([A-Za-z_][A-Za-z0-9_]*(?:\[\d+\]\.[A-Za-z_][A-Za-z0-9_]*)?) (.+)$/,p=`Form data is missing required fields: `;function m(e,t){let[r,i]=e.split(/\[\d+\]\./),a=t.find(e=>e.fieldKey===r);return i===void 0?a:a&&(0,n.isTableFieldDefinition)(a)?a.columns.find(e=>e.fieldKey===i):void 0}function h(e,t,n){let r=/^must include at least (\d+) row\(s\)$/.exec(t),i=/^must include at most (\d+) row\(s\)$/.exec(t),a=/^must be at least (\d+) characters$/.exec(t),o=/^must be at most (\d+) characters$/.exec(t);if(t===`is required`)return`此欄為必填。`;if(r)return`至少需要 ${r[1]} 列。`;if(i)return`最多 ${i[1]} 列。`;if(a)return`至少需要 ${a[1]} 個字。`;if(o)return`最多 ${o[1]} 個字。`;if(t===`does not match the required format`){let t=m(e,n);return(t&&(t.type===`text`||t.type===`textarea`)?t.patternErrorMessage?.trim():void 0)||`格式不正確。`}return t===`could not be checked against the required format in time`?`格式檢查逾時,請縮短內容後再試,或請表單設計者簡化格式。`:e.includes(`[`)?`此欄內容不正確。`:`此欄位內容不正確。`}function g(t,n,r){let i=n.fields.filter(t=>(0,e.readNodeFillableFieldKeys)(r).has(t.fieldKey));if(t.startsWith(p)){let e=`, ${t.slice(38)}, `,n=i.filter(t=>e.includes(`, ${t.label||t.fieldKey}, `));return n.length>0?Object.fromEntries(n.map(e=>[e.fieldKey,`${e.label||e.fieldKey}為必填欄位。`])):null}let a=t.split(`; `).map(e=>f.exec(e));return a.length===0||a.some(e=>e===null)?null:Object.fromEntries(a.flatMap(e=>e===null?[]:[[e[1],h(e[1],e[2],i)]]))}var _=`未知組織`;function v(e,t){return[...new Set(e.fields.filter(n.isOrgUnitFieldDefinition).map(e=>t[e.fieldKey]).filter(e=>typeof e==`string`&&e.trim().length>0))]}function y(r,i,a,o){if((0,n.isTableFieldDefinition)(r)){let e=(0,t.readFormTableRows)(i),n=r.columns.filter(e=>e.type===`number`||e.type===`money`).map(t=>({label:t.label||t.fieldKey,total:e.reduce((e,n)=>typeof n[t.fieldKey]==`number`&&Number.isFinite(n[t.fieldKey])?e+n[t.fieldKey]:e,0)}));return[`${e.length} 列`,...e.length>0?n.map(({label:e,total:t})=>`${e}合計 ${t.toLocaleString(`zh-TW`)}`):[]].join(`,`)}if((0,e.isFormDataValueEmpty)(i))return`未填寫`;if(typeof i==`boolean`)return i?`是`:`否`;if((0,n.isOrgUnitFieldDefinition)(r))return typeof i==`string`?o.get(i)??`未知組織`:_;let s=Array.isArray(i)?i:[i],c=(0,n.isFormStaticOptionFieldDefinition)(r)?new Map(r.options.map(e=>[e.value,e.label])):r.type===`member`?a:new Map;return s.map(e=>typeof e==`string`?c.get(e)??e:String(e)).join(`、`)}function b(t,n,r,i=new Map,a=new Map){let o=(0,e.readNodeFillableFieldKeys)(n);return t.fields.filter(e=>o.has(e.fieldKey)&&Object.prototype.hasOwnProperty.call(r,e.fieldKey)).map(e=>({fieldKey:e.fieldKey,label:e.label||e.fieldKey,text:y(e,r[e.fieldKey],i,a)}))}var x=`表單中有附件無法附加到這個案件(只能附加申請人為本案上傳的檔案),請移除後重新上傳再送出。`;function S(e){let r=(0,t.readFormDataSourceErrorMessage)(e);if(r)return r;let i=e instanceof Error?e.message:null;return i?.includes(n.ATTACHMENT_NOT_BINDABLE_ERROR_CODE)?x:i??`發生未知錯誤`}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,"d",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"f",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return g}});
2
- //# sourceMappingURL=instance-error-message-DTARGbGo.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"instance-error-message-DTARGbGo.cjs","names":["readApproverFilledFieldKeys","omitFormDataKeys","readNodeFillableFieldKeys","readNodeRequiredFieldKeys","isTableFieldDefinition","isOrgUnitFieldDefinition","readFormTableRows","isFormDataValueEmpty","isFormStaticOptionFieldDefinition","readFormDataSourceErrorMessage","ATTACHMENT_NOT_BINDABLE_ERROR_CODE","_rytass_bpm_core_shared","_rytass_bpm_core_client_form","_rytass_bpm_core_shared_form","readApplicantFormSchema","approverFilledKeys","definition","schema","field","readApplicantSubmission","values","readFillValidationSchema","fillableKeys","node","displaySchema","readFillDisplaySchema","requiredKeys","userTaskNodes","readFillLabelMarkers","viewerKeys","viewerFillNode","fieldKey","markers","readFillPayload","readFillChanges","seededTables","seeded","stored","draft","isFillConflictMessage","message","FORM_DATA_PATH_PATTERN","MISSING_FIELDS_PREFIX","readFillPathField","columnKey","path","fields","candidate","column","readFillPathErrorText","atLeast","reason","atMost","shortest","longest","readFillServerErrors","fillableFields","listed","missing","parts","part","UNKNOWN_FILL_ORG_UNIT_TEXT","readFillOrgUnitIds","value","readFillValueText","rows","totals","row","sum","label","total","orgUnitNames","items","optionLabels","option","memberNames","item","readFillSummary","ATTACHMENT_NOT_BINDABLE_MESSAGE","readErrorMessage","dataSourceMessage","error"],"sources":["../../src/views/instances/approver-filled-form.ts","../../src/views/instances/instance-error-message.ts"],"sourcesContent":["import {\n isFormDataValueEmpty,\n omitFormDataKeys,\n readApproverFilledFieldKeys,\n readNodeFillableFieldKeys,\n readNodeRequiredFieldKeys,\n type UserTaskNode,\n type WorkflowDefinition,\n} from '@rytass/bpm-core-shared';\nimport {\n isFormStaticOptionFieldDefinition,\n isOrgUnitFieldDefinition,\n isTableFieldDefinition,\n type FormDefinitionSchema,\n type FormFieldDefinition,\n type FormFieldValue,\n type TableColumnDefinition,\n} from '@rytass/bpm-core-shared/form';\nimport { readFormTableRows } from '@rytass/bpm-core-client/form';\nimport type { WorkflowFormData } from '@rytass/bpm-core-client/workflow';\n\n/**\n * The form as the applicant sees it: without the fields some approver fills in.\n * The server drops those keys from a submission, so rendering one would swallow\n * what the applicant types, and a `minRows` or `required` on it would block a\n * launch nobody can complete. Returns an equal schema when nothing is\n * approver-filled; never mutates its input.\n */\nexport function readApplicantFormSchema(\n schema: FormDefinitionSchema,\n definition: WorkflowDefinition,\n): FormDefinitionSchema {\n const approverFilledKeys = readApproverFilledFieldKeys(definition);\n\n return {\n ...schema,\n fields: schema.fields.filter(\n (field) => !approverFilledKeys.has(field.fieldKey),\n ),\n };\n}\n\n/** The payload the applicant may send: `values` without approver-filled keys. */\nexport function readApplicantSubmission(\n values: WorkflowFormData,\n definition: WorkflowDefinition,\n): WorkflowFormData {\n // Dropping keys cannot change a remaining value's type, which is all the\n // shared helper's `unknown` return forgets.\n return omitFormDataKeys(\n values,\n readApproverFilledFieldKeys(definition),\n ) as WorkflowFormData;\n}\n\n/**\n * The form an approver's fill is validated against: only the fields `node`\n * lets them fill in, each required exactly when the node says `REQUIRED`. A\n * required table needs at least one row, the rule `validateFormRendererValues`\n * already applies, so the result agrees with the server's fill check. Never\n * mutates its input.\n */\nexport function readFillValidationSchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const displaySchema = readFillDisplaySchema(schema, node);\n\n // Derived from the display schema so the check and the asterisk read the\n // same rule.\n return {\n ...displaySchema,\n fields: displaySchema.fields.filter((field) =>\n fillableKeys.has(field.fieldKey),\n ),\n };\n}\n\n/**\n * The schema the fill node's approver sees: every field as the form defines\n * it, except that the node's fillable fields are required exactly when the\n * node says `REQUIRED`, so the asterisk matches what approving will check.\n */\nexport function readFillDisplaySchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const requiredKeys = readNodeRequiredFieldKeys(node);\n\n return {\n ...schema,\n fields: schema.fields.map((field) =>\n fillableKeys.has(field.fieldKey)\n ? {\n ...field,\n required: requiredKeys.has(field.fieldKey),\n // The node alone decides; the server ignores the form's rule.\n requiredWhen: undefined,\n }\n : field,\n ),\n };\n}\n\nconst userTaskNodes = (definition: WorkflowDefinition): readonly UserTaskNode[] =>\n definition.nodes.filter(\n (node): node is UserTaskNode => node.type === 'userTask',\n );\n\n/**\n * The marker shown after each approver-filled field's label: 「本關補填」 on\n * the fields `viewerFillNode` lets the viewer fill, 「<node>補填」 on every\n * other approver-filled field (the first node in the definition that fills\n * it names it).\n */\nexport function readFillLabelMarkers(\n definition: WorkflowDefinition,\n viewerFillNode: UserTaskNode | null,\n): Readonly<Record<string, string>> {\n const viewerKeys =\n viewerFillNode === null\n ? new Set<string>()\n : readNodeFillableFieldKeys(viewerFillNode);\n\n return userTaskNodes(definition).reduce<Readonly<Record<string, string>>>(\n (markers, node) => ({\n ...Object.fromEntries(\n [...readNodeFillableFieldKeys(node)].map((fieldKey) => [\n fieldKey,\n viewerKeys.has(fieldKey)\n ? '本關補填'\n : `${node.data.label || node.id}補填`,\n ]),\n ),\n ...markers,\n }),\n {},\n );\n}\n\n/**\n * What approving sends as `fill.formData`: the keys of `values` that `node`\n * lets its approver fill, and nothing else (the server refuses any other key).\n * `values` is what the approver changed, not the rendered form — a renderer\n * default the approver never touched (a table's empty rows, a boolean's\n * `false`) is not theirs to sign, and would otherwise show up as filled in.\n */\nexport function readFillPayload(\n values: WorkflowFormData,\n node: UserTaskNode,\n): Readonly<Record<string, unknown>> {\n return Object.fromEntries(\n [...readNodeFillableFieldKeys(node)]\n .filter((fieldKey) =>\n Object.prototype.hasOwnProperty.call(values, fieldKey),\n )\n // A cleared field reads `undefined`, which JSON drops, and the server\n // would keep the old value; `null` clears it.\n .map((fieldKey) => [fieldKey, values[fieldKey] ?? null]),\n );\n}\n\n/**\n * What the approver's fill amounts to: the fields they changed (`draft`), plus\n * each fill table the page seeds with `minRows` rows that the case does not\n * store yet. The page shows those rows and validates them, so they are sent\n * too — left out, the server would refuse the approval for missing rows the\n * approver can see on screen.\n */\nexport function readFillChanges({\n draft,\n node,\n schema,\n stored,\n values,\n}: {\n readonly draft: WorkflowFormData;\n readonly node: UserTaskNode;\n readonly schema: FormDefinitionSchema;\n readonly stored: WorkflowFormData;\n readonly values: WorkflowFormData;\n}): WorkflowFormData {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const seededTables = schema.fields.filter((field) => {\n const seeded = values[field.fieldKey];\n\n return (\n isTableFieldDefinition(field) &&\n fillableKeys.has(field.fieldKey) &&\n !Object.prototype.hasOwnProperty.call(stored, field.fieldKey) &&\n Array.isArray(seeded) &&\n seeded.length > 0\n );\n });\n\n return {\n ...Object.fromEntries(\n seededTables.map((field) => [field.fieldKey, values[field.fieldKey]]),\n ),\n ...draft,\n };\n}\n\n/** The server message of a fill against form data someone else changed. */\nexport function isFillConflictMessage(message: string): boolean {\n return message.includes('form data changed; refresh and retry');\n}\n\nconst FORM_DATA_PATH_PATTERN =\n /^formData\\.([A-Za-z_][A-Za-z0-9_]*(?:\\[\\d+\\]\\.[A-Za-z_][A-Za-z0-9_]*)?) (.+)$/;\nconst MISSING_FIELDS_PREFIX = 'Form data is missing required fields: ';\n\n/**\n * The field or table column a server error path such as `note` or\n * `items[0].name` points at, so its own settings can word the error.\n */\nfunction readFillPathField(\n path: string,\n fields: readonly FormFieldDefinition[],\n): FormFieldDefinition | TableColumnDefinition | undefined {\n const [fieldKey, columnKey] = path.split(/\\[\\d+\\]\\./);\n const field = fields.find((candidate) => candidate.fieldKey === fieldKey);\n\n return columnKey === undefined\n ? field\n : field && isTableFieldDefinition(field)\n ? field.columns.find((column) => column.fieldKey === columnKey)\n : undefined;\n}\n\nfunction readFillPathErrorText(\n path: string,\n reason: string,\n fields: readonly FormFieldDefinition[],\n): string {\n const atLeast = /^must include at least (\\d+) row\\(s\\)$/.exec(reason);\n const atMost = /^must include at most (\\d+) row\\(s\\)$/.exec(reason);\n const shortest = /^must be at least (\\d+) characters$/.exec(reason);\n const longest = /^must be at most (\\d+) characters$/.exec(reason);\n\n if (reason === 'is required') {\n return '此欄為必填。';\n }\n\n if (atLeast) {\n return `至少需要 ${atLeast[1]} 列。`;\n }\n\n if (atMost) {\n return `最多 ${atMost[1]} 列。`;\n }\n\n if (shortest) {\n return `至少需要 ${shortest[1]} 個字。`;\n }\n\n if (longest) {\n return `最多 ${longest[1]} 個字。`;\n }\n\n if (reason === 'does not match the required format') {\n const field = readFillPathField(path, fields);\n const message =\n field && (field.type === 'text' || field.type === 'textarea')\n ? field.patternErrorMessage?.trim()\n : undefined;\n\n return message || '格式不正確。';\n }\n\n // The server ran out of time on the format check; the value may be fine.\n if (reason === 'could not be checked against the required format in time') {\n return '格式檢查逾時,請縮短內容後再試,或請表單設計者簡化格式。';\n }\n\n return path.includes('[') ? '此欄內容不正確。' : '此欄位內容不正確。';\n}\n\n/**\n * Turns the server's fill validation message (docs/08 §8.5, error table 2)\n * into field errors keyed the way `FormRenderer` addresses them, so a check\n * the page missed still lands on the field. `null` when any part does not\n * parse — the caller then shows the message as it is.\n */\nexport function readFillServerErrors(\n message: string,\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): Readonly<Record<string, string>> | null {\n const fillableFields = schema.fields.filter((field) =>\n readNodeFillableFieldKeys(node).has(field.fieldKey),\n );\n\n if (message.startsWith(MISSING_FIELDS_PREFIX)) {\n // Labels may hold commas, so match each fillable field's label (or key)\n // against the list rather than splitting it.\n const listed = `, ${message.slice(MISSING_FIELDS_PREFIX.length)}, `;\n const missing = fillableFields.filter((field) =>\n listed.includes(`, ${field.label || field.fieldKey}, `),\n );\n\n return missing.length > 0\n ? Object.fromEntries(\n missing.map((field) => [\n field.fieldKey,\n `${field.label || field.fieldKey}為必填欄位。`,\n ]),\n )\n : null;\n }\n\n const parts = message.split('; ').map((part) => FORM_DATA_PATH_PATTERN.exec(part));\n\n if (parts.length === 0 || parts.some((part) => part === null)) {\n return null;\n }\n\n return Object.fromEntries(\n parts.flatMap((part) =>\n part === null\n ? []\n : [[part[1], readFillPathErrorText(part[1], part[2], fillableFields)]],\n ),\n );\n}\n\nexport interface FillSummaryItem {\n readonly fieldKey: string;\n readonly label: string;\n readonly text: string;\n}\n\nexport const UNKNOWN_FILL_ORG_UNIT_TEXT = '未知組織';\n\n/**\n * The org unit ids among `values`, once each and in form order, so the caller\n * can look up their names before building the summary.\n */\nexport function readFillOrgUnitIds(\n schema: FormDefinitionSchema,\n values: WorkflowFormData,\n): readonly string[] {\n return [\n ...new Set(\n schema.fields\n .filter(isOrgUnitFieldDefinition)\n .map((field) => values[field.fieldKey])\n .filter(\n (value): value is string =>\n typeof value === 'string' && value.trim().length > 0,\n ),\n ),\n ];\n}\n\nfunction readFillValueText(\n field: FormFieldDefinition,\n value: FormFieldValue | undefined,\n memberNames: ReadonlyMap<string, string>,\n orgUnitNames: ReadonlyMap<string, string>,\n): string {\n if (isTableFieldDefinition(field)) {\n const rows = readFormTableRows(value);\n const totals = field.columns\n .filter((column) => column.type === 'number' || column.type === 'money')\n .map((column) => ({\n label: column.label || column.fieldKey,\n total: rows.reduce(\n (sum, row) =>\n typeof row[column.fieldKey] === 'number' &&\n Number.isFinite(row[column.fieldKey])\n ? sum + (row[column.fieldKey] as number)\n : sum,\n 0,\n ),\n }));\n\n return [\n `${rows.length} 列`,\n ...(rows.length > 0\n ? totals.map(\n ({ label, total }) => `${label}合計 ${total.toLocaleString('zh-TW')}`,\n )\n : []),\n ].join(',');\n }\n\n if (isFormDataValueEmpty(value)) {\n return '未填寫';\n }\n\n if (typeof value === 'boolean') {\n return value ? '是' : '否';\n }\n\n if (isOrgUnitFieldDefinition(field)) {\n // `orgUnitNames` (`useOrgUnitNames`) maps an id still being looked up to\n // a loading text; an id the lookup did not name is 「未知組織」. Never the\n // raw id.\n return typeof value === 'string'\n ? (orgUnitNames.get(value) ?? UNKNOWN_FILL_ORG_UNIT_TEXT)\n : UNKNOWN_FILL_ORG_UNIT_TEXT;\n }\n\n const items = Array.isArray(value) ? value : [value];\n const optionLabels = isFormStaticOptionFieldDefinition(field)\n ? new Map(field.options.map((option) => [option.value, option.label]))\n : field.type === 'member'\n ? memberNames\n : new Map<string, string>();\n\n return items\n .map((item) =>\n typeof item === 'string' ? (optionLabels.get(item) ?? item) : String(item),\n )\n .join('、');\n}\n\n/**\n * One line per field `node` lets its approver fill, in form order, for the\n * approve dialog: what will be sent along with the approval. A member field\n * names each member found in `memberNames` and shows the id otherwise; an org\n * unit field names its unit from `orgUnitNames` and shows 「未知組織」 otherwise.\n */\nexport function readFillSummary(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n values: WorkflowFormData,\n memberNames: ReadonlyMap<string, string> = new Map(),\n orgUnitNames: ReadonlyMap<string, string> = new Map(),\n): readonly FillSummaryItem[] {\n const fillableKeys = readNodeFillableFieldKeys(node);\n\n return schema.fields\n .filter(\n (field) =>\n fillableKeys.has(field.fieldKey) &&\n Object.prototype.hasOwnProperty.call(values, field.fieldKey),\n )\n .map((field) => ({\n fieldKey: field.fieldKey,\n label: field.label || field.fieldKey,\n text: readFillValueText(\n field,\n values[field.fieldKey],\n memberNames,\n orgUnitNames,\n ),\n }));\n}\n","import { readFormDataSourceErrorMessage } from '@rytass/bpm-core-client/form';\nimport { ATTACHMENT_NOT_BINDABLE_ERROR_CODE } from '@rytass/bpm-core-shared/form';\n\nconst ATTACHMENT_NOT_BINDABLE_MESSAGE =\n '表單中有附件無法附加到這個案件(只能附加申請人為本案上傳的檔案),請移除後重新上傳再送出。';\n\n/**\n * The message a case page shows for a failed request: a stable server code\n * translated into words, otherwise the server's own message.\n */\nexport function readErrorMessage(error: unknown): string {\n // DataSource failures arrive as stable codes so the backend never leaks\n // upstream transport detail; translate them instead of showing the raw code.\n const dataSourceMessage = readFormDataSourceErrorMessage(error);\n\n if (dataSourceMessage) {\n return dataSourceMessage;\n }\n\n const message = error instanceof Error ? error.message : null;\n\n // Names the problem, not the attachment ids the server lists.\n if (message?.includes(ATTACHMENT_NOT_BINDABLE_ERROR_CODE)) {\n return ATTACHMENT_NOT_BINDABLE_MESSAGE;\n }\n\n return message ?? '發生未知錯誤';\n}\n"],"mappings":"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"}
@@ -1,120 +0,0 @@
1
- import { isFormDataValueEmpty as e, omitFormDataKeys as t, readApproverFilledFieldKeys as n, readNodeFillableFieldKeys as r, readNodeRequiredFieldKeys as i } from "@rytass/bpm-core-shared";
2
- import { readFormDataSourceErrorMessage as a, readFormTableRows as o } from "@rytass/bpm-core-client/form";
3
- import { ATTACHMENT_NOT_BINDABLE_ERROR_CODE as s, isFormStaticOptionFieldDefinition as c, isOrgUnitFieldDefinition as l, isTableFieldDefinition as u } from "@rytass/bpm-core-shared/form";
4
- // src/views/instances/approver-filled-form.ts
5
- function d(e, t) {
6
- let r = n(t);
7
- return {
8
- ...e,
9
- fields: e.fields.filter((e) => !r.has(e.fieldKey))
10
- };
11
- }
12
- function f(e, r) {
13
- return t(e, n(r));
14
- }
15
- function p(e, t) {
16
- let n = r(t), i = m(e, t);
17
- return {
18
- ...i,
19
- fields: i.fields.filter((e) => n.has(e.fieldKey))
20
- };
21
- }
22
- function m(e, t) {
23
- let n = r(t), a = i(t);
24
- return {
25
- ...e,
26
- fields: e.fields.map((e) => n.has(e.fieldKey) ? {
27
- ...e,
28
- required: a.has(e.fieldKey),
29
- requiredWhen: void 0
30
- } : e)
31
- };
32
- }
33
- var h = (e) => e.nodes.filter((e) => e.type === "userTask");
34
- function g(e, t) {
35
- let n = t === null ? /* @__PURE__ */ new Set() : r(t);
36
- return h(e).reduce((e, t) => ({
37
- ...Object.fromEntries([...r(t)].map((e) => [e, n.has(e) ? "本關補填" : `${t.data.label || t.id}補填`])),
38
- ...e
39
- }), {});
40
- }
41
- function _(e, t) {
42
- return Object.fromEntries([...r(t)].filter((t) => Object.prototype.hasOwnProperty.call(e, t)).map((t) => [t, e[t] ?? null]));
43
- }
44
- function v({ draft: e, node: t, schema: n, stored: i, values: a }) {
45
- let o = r(t), s = n.fields.filter((e) => {
46
- let t = a[e.fieldKey];
47
- return u(e) && o.has(e.fieldKey) && !Object.prototype.hasOwnProperty.call(i, e.fieldKey) && Array.isArray(t) && t.length > 0;
48
- });
49
- return {
50
- ...Object.fromEntries(s.map((e) => [e.fieldKey, a[e.fieldKey]])),
51
- ...e
52
- };
53
- }
54
- function y(e) {
55
- return e.includes("form data changed; refresh and retry");
56
- }
57
- var b = /^formData\.([A-Za-z_][A-Za-z0-9_]*(?:\[\d+\]\.[A-Za-z_][A-Za-z0-9_]*)?) (.+)$/, x = "Form data is missing required fields: ";
58
- function S(e, t) {
59
- let [n, r] = e.split(/\[\d+\]\./), i = t.find((e) => e.fieldKey === n);
60
- return r === void 0 ? i : i && u(i) ? i.columns.find((e) => e.fieldKey === r) : void 0;
61
- }
62
- function C(e, t, n) {
63
- let r = /^must include at least (\d+) row\(s\)$/.exec(t), i = /^must include at most (\d+) row\(s\)$/.exec(t), a = /^must be at least (\d+) characters$/.exec(t), o = /^must be at most (\d+) characters$/.exec(t);
64
- if (t === "is required") return "此欄為必填。";
65
- if (r) return `至少需要 ${r[1]} 列。`;
66
- if (i) return `最多 ${i[1]} 列。`;
67
- if (a) return `至少需要 ${a[1]} 個字。`;
68
- if (o) return `最多 ${o[1]} 個字。`;
69
- if (t === "does not match the required format") {
70
- let t = S(e, n);
71
- return (t && (t.type === "text" || t.type === "textarea") ? t.patternErrorMessage?.trim() : void 0) || "格式不正確。";
72
- }
73
- return t === "could not be checked against the required format in time" ? "格式檢查逾時,請縮短內容後再試,或請表單設計者簡化格式。" : e.includes("[") ? "此欄內容不正確。" : "此欄位內容不正確。";
74
- }
75
- function w(e, t, n) {
76
- let i = t.fields.filter((e) => r(n).has(e.fieldKey));
77
- if (e.startsWith(x)) {
78
- let t = `, ${e.slice(38)}, `, n = i.filter((e) => t.includes(`, ${e.label || e.fieldKey}, `));
79
- return n.length > 0 ? Object.fromEntries(n.map((e) => [e.fieldKey, `${e.label || e.fieldKey}為必填欄位。`])) : null;
80
- }
81
- let a = e.split("; ").map((e) => b.exec(e));
82
- return a.length === 0 || a.some((e) => e === null) ? null : Object.fromEntries(a.flatMap((e) => e === null ? [] : [[e[1], C(e[1], e[2], i)]]));
83
- }
84
- var T = "未知組織";
85
- function E(e, t) {
86
- return [...new Set(e.fields.filter(l).map((e) => t[e.fieldKey]).filter((e) => typeof e == "string" && e.trim().length > 0))];
87
- }
88
- function D(t, n, r, i) {
89
- if (u(t)) {
90
- let e = o(n), r = t.columns.filter((e) => e.type === "number" || e.type === "money").map((t) => ({
91
- label: t.label || t.fieldKey,
92
- total: e.reduce((e, n) => typeof n[t.fieldKey] == "number" && Number.isFinite(n[t.fieldKey]) ? e + n[t.fieldKey] : e, 0)
93
- }));
94
- return [`${e.length} 列`, ...e.length > 0 ? r.map(({ label: e, total: t }) => `${e}合計 ${t.toLocaleString("zh-TW")}`) : []].join(",");
95
- }
96
- if (e(n)) return "未填寫";
97
- if (typeof n == "boolean") return n ? "是" : "否";
98
- if (l(t)) return typeof n == "string" ? i.get(n) ?? "未知組織" : T;
99
- let a = Array.isArray(n) ? n : [n], s = c(t) ? new Map(t.options.map((e) => [e.value, e.label])) : t.type === "member" ? r : /* @__PURE__ */ new Map();
100
- return a.map((e) => typeof e == "string" ? s.get(e) ?? e : String(e)).join("、");
101
- }
102
- function O(e, t, n, i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map()) {
103
- let o = r(t);
104
- return e.fields.filter((e) => o.has(e.fieldKey) && Object.prototype.hasOwnProperty.call(n, e.fieldKey)).map((e) => ({
105
- fieldKey: e.fieldKey,
106
- label: e.label || e.fieldKey,
107
- text: D(e, n[e.fieldKey], i, a)
108
- }));
109
- }
110
- // src/views/instances/instance-error-message.ts
111
- var k = "表單中有附件無法附加到這個案件(只能附加申請人為本案上傳的檔案),請移除後重新上傳再送出。";
112
- function A(e) {
113
- let t = a(e);
114
- if (t) return t;
115
- let n = e instanceof Error ? e.message : null;
116
- return n?.includes(s) ? k : n ?? "發生未知錯誤";
117
- }
118
- export { v as a, E as c, O as d, p as f, f as i, _ as l, y as n, m as o, d as r, g as s, A as t, w as u };
119
-
120
- //# sourceMappingURL=instance-error-message-q6gy5evy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"instance-error-message-q6gy5evy.js","names":["readApplicantFormSchema","approverFilledKeys","readApproverFilledFieldKeys","definition","schema","field","readApplicantSubmission","omitFormDataKeys","values","readFillValidationSchema","fillableKeys","readNodeFillableFieldKeys","node","displaySchema","readFillDisplaySchema","requiredKeys","readNodeRequiredFieldKeys","userTaskNodes","readFillLabelMarkers","viewerKeys","viewerFillNode","fieldKey","markers","readFillPayload","readFillChanges","seededTables","seeded","isTableFieldDefinition","stored","draft","isFillConflictMessage","message","FORM_DATA_PATH_PATTERN","MISSING_FIELDS_PREFIX","readFillPathField","columnKey","path","fields","candidate","column","readFillPathErrorText","atLeast","reason","atMost","shortest","longest","readFillServerErrors","fillableFields","listed","missing","parts","part","UNKNOWN_FILL_ORG_UNIT_TEXT","readFillOrgUnitIds","isOrgUnitFieldDefinition","value","readFillValueText","rows","readFormTableRows","totals","row","sum","label","total","isFormDataValueEmpty","orgUnitNames","items","optionLabels","isFormStaticOptionFieldDefinition","option","memberNames","item","readFillSummary","ATTACHMENT_NOT_BINDABLE_MESSAGE","readErrorMessage","dataSourceMessage","readFormDataSourceErrorMessage","error","ATTACHMENT_NOT_BINDABLE_ERROR_CODE"],"sources":["../../src/views/instances/approver-filled-form.ts","../../src/views/instances/instance-error-message.ts"],"sourcesContent":["import {\n isFormDataValueEmpty,\n omitFormDataKeys,\n readApproverFilledFieldKeys,\n readNodeFillableFieldKeys,\n readNodeRequiredFieldKeys,\n type UserTaskNode,\n type WorkflowDefinition,\n} from '@rytass/bpm-core-shared';\nimport {\n isFormStaticOptionFieldDefinition,\n isOrgUnitFieldDefinition,\n isTableFieldDefinition,\n type FormDefinitionSchema,\n type FormFieldDefinition,\n type FormFieldValue,\n type TableColumnDefinition,\n} from '@rytass/bpm-core-shared/form';\nimport { readFormTableRows } from '@rytass/bpm-core-client/form';\nimport type { WorkflowFormData } from '@rytass/bpm-core-client/workflow';\n\n/**\n * The form as the applicant sees it: without the fields some approver fills in.\n * The server drops those keys from a submission, so rendering one would swallow\n * what the applicant types, and a `minRows` or `required` on it would block a\n * launch nobody can complete. Returns an equal schema when nothing is\n * approver-filled; never mutates its input.\n */\nexport function readApplicantFormSchema(\n schema: FormDefinitionSchema,\n definition: WorkflowDefinition,\n): FormDefinitionSchema {\n const approverFilledKeys = readApproverFilledFieldKeys(definition);\n\n return {\n ...schema,\n fields: schema.fields.filter(\n (field) => !approverFilledKeys.has(field.fieldKey),\n ),\n };\n}\n\n/** The payload the applicant may send: `values` without approver-filled keys. */\nexport function readApplicantSubmission(\n values: WorkflowFormData,\n definition: WorkflowDefinition,\n): WorkflowFormData {\n // Dropping keys cannot change a remaining value's type, which is all the\n // shared helper's `unknown` return forgets.\n return omitFormDataKeys(\n values,\n readApproverFilledFieldKeys(definition),\n ) as WorkflowFormData;\n}\n\n/**\n * The form an approver's fill is validated against: only the fields `node`\n * lets them fill in, each required exactly when the node says `REQUIRED`. A\n * required table needs at least one row, the rule `validateFormRendererValues`\n * already applies, so the result agrees with the server's fill check. Never\n * mutates its input.\n */\nexport function readFillValidationSchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const displaySchema = readFillDisplaySchema(schema, node);\n\n // Derived from the display schema so the check and the asterisk read the\n // same rule.\n return {\n ...displaySchema,\n fields: displaySchema.fields.filter((field) =>\n fillableKeys.has(field.fieldKey),\n ),\n };\n}\n\n/**\n * The schema the fill node's approver sees: every field as the form defines\n * it, except that the node's fillable fields are required exactly when the\n * node says `REQUIRED`, so the asterisk matches what approving will check.\n */\nexport function readFillDisplaySchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const requiredKeys = readNodeRequiredFieldKeys(node);\n\n return {\n ...schema,\n fields: schema.fields.map((field) =>\n fillableKeys.has(field.fieldKey)\n ? {\n ...field,\n required: requiredKeys.has(field.fieldKey),\n // The node alone decides; the server ignores the form's rule.\n requiredWhen: undefined,\n }\n : field,\n ),\n };\n}\n\nconst userTaskNodes = (definition: WorkflowDefinition): readonly UserTaskNode[] =>\n definition.nodes.filter(\n (node): node is UserTaskNode => node.type === 'userTask',\n );\n\n/**\n * The marker shown after each approver-filled field's label: 「本關補填」 on\n * the fields `viewerFillNode` lets the viewer fill, 「<node>補填」 on every\n * other approver-filled field (the first node in the definition that fills\n * it names it).\n */\nexport function readFillLabelMarkers(\n definition: WorkflowDefinition,\n viewerFillNode: UserTaskNode | null,\n): Readonly<Record<string, string>> {\n const viewerKeys =\n viewerFillNode === null\n ? new Set<string>()\n : readNodeFillableFieldKeys(viewerFillNode);\n\n return userTaskNodes(definition).reduce<Readonly<Record<string, string>>>(\n (markers, node) => ({\n ...Object.fromEntries(\n [...readNodeFillableFieldKeys(node)].map((fieldKey) => [\n fieldKey,\n viewerKeys.has(fieldKey)\n ? '本關補填'\n : `${node.data.label || node.id}補填`,\n ]),\n ),\n ...markers,\n }),\n {},\n );\n}\n\n/**\n * What approving sends as `fill.formData`: the keys of `values` that `node`\n * lets its approver fill, and nothing else (the server refuses any other key).\n * `values` is what the approver changed, not the rendered form — a renderer\n * default the approver never touched (a table's empty rows, a boolean's\n * `false`) is not theirs to sign, and would otherwise show up as filled in.\n */\nexport function readFillPayload(\n values: WorkflowFormData,\n node: UserTaskNode,\n): Readonly<Record<string, unknown>> {\n return Object.fromEntries(\n [...readNodeFillableFieldKeys(node)]\n .filter((fieldKey) =>\n Object.prototype.hasOwnProperty.call(values, fieldKey),\n )\n // A cleared field reads `undefined`, which JSON drops, and the server\n // would keep the old value; `null` clears it.\n .map((fieldKey) => [fieldKey, values[fieldKey] ?? null]),\n );\n}\n\n/**\n * What the approver's fill amounts to: the fields they changed (`draft`), plus\n * each fill table the page seeds with `minRows` rows that the case does not\n * store yet. The page shows those rows and validates them, so they are sent\n * too — left out, the server would refuse the approval for missing rows the\n * approver can see on screen.\n */\nexport function readFillChanges({\n draft,\n node,\n schema,\n stored,\n values,\n}: {\n readonly draft: WorkflowFormData;\n readonly node: UserTaskNode;\n readonly schema: FormDefinitionSchema;\n readonly stored: WorkflowFormData;\n readonly values: WorkflowFormData;\n}): WorkflowFormData {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const seededTables = schema.fields.filter((field) => {\n const seeded = values[field.fieldKey];\n\n return (\n isTableFieldDefinition(field) &&\n fillableKeys.has(field.fieldKey) &&\n !Object.prototype.hasOwnProperty.call(stored, field.fieldKey) &&\n Array.isArray(seeded) &&\n seeded.length > 0\n );\n });\n\n return {\n ...Object.fromEntries(\n seededTables.map((field) => [field.fieldKey, values[field.fieldKey]]),\n ),\n ...draft,\n };\n}\n\n/** The server message of a fill against form data someone else changed. */\nexport function isFillConflictMessage(message: string): boolean {\n return message.includes('form data changed; refresh and retry');\n}\n\nconst FORM_DATA_PATH_PATTERN =\n /^formData\\.([A-Za-z_][A-Za-z0-9_]*(?:\\[\\d+\\]\\.[A-Za-z_][A-Za-z0-9_]*)?) (.+)$/;\nconst MISSING_FIELDS_PREFIX = 'Form data is missing required fields: ';\n\n/**\n * The field or table column a server error path such as `note` or\n * `items[0].name` points at, so its own settings can word the error.\n */\nfunction readFillPathField(\n path: string,\n fields: readonly FormFieldDefinition[],\n): FormFieldDefinition | TableColumnDefinition | undefined {\n const [fieldKey, columnKey] = path.split(/\\[\\d+\\]\\./);\n const field = fields.find((candidate) => candidate.fieldKey === fieldKey);\n\n return columnKey === undefined\n ? field\n : field && isTableFieldDefinition(field)\n ? field.columns.find((column) => column.fieldKey === columnKey)\n : undefined;\n}\n\nfunction readFillPathErrorText(\n path: string,\n reason: string,\n fields: readonly FormFieldDefinition[],\n): string {\n const atLeast = /^must include at least (\\d+) row\\(s\\)$/.exec(reason);\n const atMost = /^must include at most (\\d+) row\\(s\\)$/.exec(reason);\n const shortest = /^must be at least (\\d+) characters$/.exec(reason);\n const longest = /^must be at most (\\d+) characters$/.exec(reason);\n\n if (reason === 'is required') {\n return '此欄為必填。';\n }\n\n if (atLeast) {\n return `至少需要 ${atLeast[1]} 列。`;\n }\n\n if (atMost) {\n return `最多 ${atMost[1]} 列。`;\n }\n\n if (shortest) {\n return `至少需要 ${shortest[1]} 個字。`;\n }\n\n if (longest) {\n return `最多 ${longest[1]} 個字。`;\n }\n\n if (reason === 'does not match the required format') {\n const field = readFillPathField(path, fields);\n const message =\n field && (field.type === 'text' || field.type === 'textarea')\n ? field.patternErrorMessage?.trim()\n : undefined;\n\n return message || '格式不正確。';\n }\n\n // The server ran out of time on the format check; the value may be fine.\n if (reason === 'could not be checked against the required format in time') {\n return '格式檢查逾時,請縮短內容後再試,或請表單設計者簡化格式。';\n }\n\n return path.includes('[') ? '此欄內容不正確。' : '此欄位內容不正確。';\n}\n\n/**\n * Turns the server's fill validation message (docs/08 §8.5, error table 2)\n * into field errors keyed the way `FormRenderer` addresses them, so a check\n * the page missed still lands on the field. `null` when any part does not\n * parse — the caller then shows the message as it is.\n */\nexport function readFillServerErrors(\n message: string,\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): Readonly<Record<string, string>> | null {\n const fillableFields = schema.fields.filter((field) =>\n readNodeFillableFieldKeys(node).has(field.fieldKey),\n );\n\n if (message.startsWith(MISSING_FIELDS_PREFIX)) {\n // Labels may hold commas, so match each fillable field's label (or key)\n // against the list rather than splitting it.\n const listed = `, ${message.slice(MISSING_FIELDS_PREFIX.length)}, `;\n const missing = fillableFields.filter((field) =>\n listed.includes(`, ${field.label || field.fieldKey}, `),\n );\n\n return missing.length > 0\n ? Object.fromEntries(\n missing.map((field) => [\n field.fieldKey,\n `${field.label || field.fieldKey}為必填欄位。`,\n ]),\n )\n : null;\n }\n\n const parts = message.split('; ').map((part) => FORM_DATA_PATH_PATTERN.exec(part));\n\n if (parts.length === 0 || parts.some((part) => part === null)) {\n return null;\n }\n\n return Object.fromEntries(\n parts.flatMap((part) =>\n part === null\n ? []\n : [[part[1], readFillPathErrorText(part[1], part[2], fillableFields)]],\n ),\n );\n}\n\nexport interface FillSummaryItem {\n readonly fieldKey: string;\n readonly label: string;\n readonly text: string;\n}\n\nexport const UNKNOWN_FILL_ORG_UNIT_TEXT = '未知組織';\n\n/**\n * The org unit ids among `values`, once each and in form order, so the caller\n * can look up their names before building the summary.\n */\nexport function readFillOrgUnitIds(\n schema: FormDefinitionSchema,\n values: WorkflowFormData,\n): readonly string[] {\n return [\n ...new Set(\n schema.fields\n .filter(isOrgUnitFieldDefinition)\n .map((field) => values[field.fieldKey])\n .filter(\n (value): value is string =>\n typeof value === 'string' && value.trim().length > 0,\n ),\n ),\n ];\n}\n\nfunction readFillValueText(\n field: FormFieldDefinition,\n value: FormFieldValue | undefined,\n memberNames: ReadonlyMap<string, string>,\n orgUnitNames: ReadonlyMap<string, string>,\n): string {\n if (isTableFieldDefinition(field)) {\n const rows = readFormTableRows(value);\n const totals = field.columns\n .filter((column) => column.type === 'number' || column.type === 'money')\n .map((column) => ({\n label: column.label || column.fieldKey,\n total: rows.reduce(\n (sum, row) =>\n typeof row[column.fieldKey] === 'number' &&\n Number.isFinite(row[column.fieldKey])\n ? sum + (row[column.fieldKey] as number)\n : sum,\n 0,\n ),\n }));\n\n return [\n `${rows.length} 列`,\n ...(rows.length > 0\n ? totals.map(\n ({ label, total }) => `${label}合計 ${total.toLocaleString('zh-TW')}`,\n )\n : []),\n ].join(',');\n }\n\n if (isFormDataValueEmpty(value)) {\n return '未填寫';\n }\n\n if (typeof value === 'boolean') {\n return value ? '是' : '否';\n }\n\n if (isOrgUnitFieldDefinition(field)) {\n // `orgUnitNames` (`useOrgUnitNames`) maps an id still being looked up to\n // a loading text; an id the lookup did not name is 「未知組織」. Never the\n // raw id.\n return typeof value === 'string'\n ? (orgUnitNames.get(value) ?? UNKNOWN_FILL_ORG_UNIT_TEXT)\n : UNKNOWN_FILL_ORG_UNIT_TEXT;\n }\n\n const items = Array.isArray(value) ? value : [value];\n const optionLabels = isFormStaticOptionFieldDefinition(field)\n ? new Map(field.options.map((option) => [option.value, option.label]))\n : field.type === 'member'\n ? memberNames\n : new Map<string, string>();\n\n return items\n .map((item) =>\n typeof item === 'string' ? (optionLabels.get(item) ?? item) : String(item),\n )\n .join('、');\n}\n\n/**\n * One line per field `node` lets its approver fill, in form order, for the\n * approve dialog: what will be sent along with the approval. A member field\n * names each member found in `memberNames` and shows the id otherwise; an org\n * unit field names its unit from `orgUnitNames` and shows 「未知組織」 otherwise.\n */\nexport function readFillSummary(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n values: WorkflowFormData,\n memberNames: ReadonlyMap<string, string> = new Map(),\n orgUnitNames: ReadonlyMap<string, string> = new Map(),\n): readonly FillSummaryItem[] {\n const fillableKeys = readNodeFillableFieldKeys(node);\n\n return schema.fields\n .filter(\n (field) =>\n fillableKeys.has(field.fieldKey) &&\n Object.prototype.hasOwnProperty.call(values, field.fieldKey),\n )\n .map((field) => ({\n fieldKey: field.fieldKey,\n label: field.label || field.fieldKey,\n text: readFillValueText(\n field,\n values[field.fieldKey],\n memberNames,\n orgUnitNames,\n ),\n }));\n}\n","import { readFormDataSourceErrorMessage } from '@rytass/bpm-core-client/form';\nimport { ATTACHMENT_NOT_BINDABLE_ERROR_CODE } from '@rytass/bpm-core-shared/form';\n\nconst ATTACHMENT_NOT_BINDABLE_MESSAGE =\n '表單中有附件無法附加到這個案件(只能附加申請人為本案上傳的檔案),請移除後重新上傳再送出。';\n\n/**\n * The message a case page shows for a failed request: a stable server code\n * translated into words, otherwise the server's own message.\n */\nexport function readErrorMessage(error: unknown): string {\n // DataSource failures arrive as stable codes so the backend never leaks\n // upstream transport detail; translate them instead of showing the raw code.\n const dataSourceMessage = readFormDataSourceErrorMessage(error);\n\n if (dataSourceMessage) {\n return dataSourceMessage;\n }\n\n const message = error instanceof Error ? error.message : null;\n\n // Names the problem, not the attachment ids the server lists.\n if (message?.includes(ATTACHMENT_NOT_BINDABLE_ERROR_CODE)) {\n return ATTACHMENT_NOT_BINDABLE_MESSAGE;\n }\n\n return message ?? '發生未知錯誤';\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- "use client";let e=require("react"),t=require("react/jsx-runtime");var n=`zh-TW`,r=[`zh-TW`,`en`],i=(0,e.createContext)(n);function a({children:e,language:n}){return(0,t.jsx)(i.Provider,{value:n,children:e})}function o(){return(0,e.useContext)(i)}Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return a}});
2
- //# sourceMappingURL=language-provider-Bdnx3Gxl.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"language-provider-Bdnx3Gxl.cjs","names":["createContext","useContext","react","react_jsx_runtime","DEFAULT_BPM_LANGUAGE","BPM_LANGUAGES","BPMLanguageContext","BPMLanguageProvider","language","useBPMLanguage"],"sources":["../../src/lib/language-provider.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n useContext,\n type ReactElement,\n type ReactNode,\n} from 'react';\n\n/**\n * Languages BPM views can write their messages in. This is separate from the\n * Mezzanine calendar `locale` on `<Providers>`, which only formats dates.\n */\nexport type BPMLanguage = 'en' | 'zh-TW';\n\nexport const DEFAULT_BPM_LANGUAGE: BPMLanguage = 'zh-TW';\n\nexport const BPM_LANGUAGES: readonly BPMLanguage[] = ['zh-TW', 'en'];\n\nconst BPMLanguageContext = createContext<BPMLanguage>(DEFAULT_BPM_LANGUAGE);\n\ninterface BPMLanguageProviderProps {\n readonly children: ReactNode;\n readonly language: BPMLanguage;\n}\n\n/**\n * Sets the language BPM views write their messages in. Hosts that let a user\n * choose pass that choice here as state; a change re-renders every view below.\n *\n * Outside this provider `useBPMLanguage()` answers `'zh-TW'`, so views render\n * in tests and Storybook without it.\n */\nexport function BPMLanguageProvider({\n children,\n language,\n}: BPMLanguageProviderProps): ReactElement {\n return (\n <BPMLanguageContext.Provider value={language}>\n {children}\n </BPMLanguageContext.Provider>\n );\n}\n\nexport function useBPMLanguage(): BPMLanguage {\n return useContext(BPMLanguageContext);\n}\n"],"mappings":"A,mEAeA,IAAaI,EAAoC,QAEpCC,EAAwC,CAAC,QAAS,IAAI,EAE7DC,GAAA,EAAqBN,EAAAA,cAAAA,CAA2BI,CAAoB,EAc1E,SAAgBG,EAAoB,CAClC,WACA,YACyC,CACzC,OACE,EAAAJ,EAAA,IAAA,CAACG,EAAmB,SAApB,CAA6B,MAAOE,EACjC,UAC0B,CAAA,CAEjC,CAEA,SAAgBC,GAA8B,CAC5C,OAAA,EAAOR,EAAAA,WAAAA,CAAWK,CAAkB,CACtC,C"}
@@ -1,17 +0,0 @@
1
- "use client";
2
- import { createContext as e, useContext as t } from "react";
3
- import { jsx as n } from "react/jsx-runtime";
4
- // src/lib/language-provider.tsx
5
- var r = "zh-TW", i = ["zh-TW", "en"], a = e(r);
6
- function o({ children: e, language: t }) {
7
- return /* @__PURE__ */ n(a.Provider, {
8
- value: t,
9
- children: e
10
- });
11
- }
12
- function s() {
13
- return t(a);
14
- }
15
- export { s as i, i as n, r, o as t };
16
-
17
- //# sourceMappingURL=language-provider-phkWOapf.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"language-provider-phkWOapf.js","names":["DEFAULT_BPM_LANGUAGE","BPM_LANGUAGES","BPMLanguageContext","createContext","BPMLanguageProvider","language","useBPMLanguage","useContext"],"sources":["../../src/lib/language-provider.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n useContext,\n type ReactElement,\n type ReactNode,\n} from 'react';\n\n/**\n * Languages BPM views can write their messages in. This is separate from the\n * Mezzanine calendar `locale` on `<Providers>`, which only formats dates.\n */\nexport type BPMLanguage = 'en' | 'zh-TW';\n\nexport const DEFAULT_BPM_LANGUAGE: BPMLanguage = 'zh-TW';\n\nexport const BPM_LANGUAGES: readonly BPMLanguage[] = ['zh-TW', 'en'];\n\nconst BPMLanguageContext = createContext<BPMLanguage>(DEFAULT_BPM_LANGUAGE);\n\ninterface BPMLanguageProviderProps {\n readonly children: ReactNode;\n readonly language: BPMLanguage;\n}\n\n/**\n * Sets the language BPM views write their messages in. Hosts that let a user\n * choose pass that choice here as state; a change re-renders every view below.\n *\n * Outside this provider `useBPMLanguage()` answers `'zh-TW'`, so views render\n * in tests and Storybook without it.\n */\nexport function BPMLanguageProvider({\n children,\n language,\n}: BPMLanguageProviderProps): ReactElement {\n return (\n <BPMLanguageContext.Provider value={language}>\n {children}\n </BPMLanguageContext.Provider>\n );\n}\n\nexport function useBPMLanguage(): BPMLanguage {\n return useContext(BPMLanguageContext);\n}\n"],"mappings":";;;;AAeA,IAAaA,IAAoC,SAEpCC,IAAwC,CAAC,SAAS,IAAI,GAE7DC,IAAqBC,EAA2BH,CAAoB;AAc1E,SAAgBI,EAAoB,EAClC,aACA,eACyC;CACzC,OACE,kBAACF,EAAmB,UAApB;EAA6B,OAAOG;EACjC;CAC0B,CAAA;AAEjC;AAEA,SAAgBC,IAA8B;CAC5C,OAAOC,EAAWL,CAAkB;AACtC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"providers-BbCs23sk.js","names":["_Fragment","Modal","Textarea","useAuth","DEFAULT_BPM_LANGUAGE","BPMLanguageProvider","useRouterAdapter","useBPMRoutes","BPMFormField","Fragment$1","Drawer","Modal$1","NotificationCenter","Textarea$1","NotificationDrawerContext","createContext","NotificationDrawerProvider","isOpen","setIsOpen","useState","open","useCallback","close","toggle","current","value","useMemo","useNotificationDrawer","useContext","NotificationUnreadContext","NotificationUnreadProvider","currentMemberId","member","unreadCount","setUnreadCount","refreshUnreadCount","next","readUnreadNotificationCount","active","useNotificationUnread","TIME_GROUP_ORDER","TIME_GROUP_LABEL","PAGE_SIZE","TOGGLE_ARCHIVED_OPTION_ID","buildNotificationOptions","canReject","readNotificationAllowReject","record","opensToFill","readNotificationHasFillableFields","RESOLVED_TITLE","resolveDisplayTitle","resolveSeverity","toSeverity","NotificationDrawer","router","routes","rows","setRows","totalCount","setTotalCount","page","setPage","loading","setLoading","bulkLoading","setBulkLoading","error","setError","notice","setNotice","filter","setFilter","includeArchived","setIncludeArchived","approveTarget","setApproveTarget","approveComment","setApproveComment","rejectTarget","setRejectTarget","rejectReason","setRejectReason","deciding","setDeciding","trimmedApproveComment","trimmedRejectReason","loadPage","result","listNotifications","nextPage","append","readErrorMessage","handleFilterChange","event","handleMarkAllRead","markAllNotificationsRead","handleLoadMore","handleMarkRead","markNotificationRead","handleArchive","label","unarchiveNotifications","archiveNotifications","handleFilterAreaSelect","option","handleOpenInstance","openApproveModal","closeApproveModal","handleApproveConfirm","target","decideTask","openRejectModal","closeRejectModal","handleRejectConfirm","handleBadgeSelect","action","isCardActivation","card","handleCardActivate","handleCardKeyDown","filteredRows","row","groupedRows","now","buckets","group","accumulator","resolveTimeGroup","items","hasMore","Fragment","openable","itemIndex","Typography","type","notificationDate","nowStartOfDay","notificationStartOfDay","yesterdayStartOfDay","payload","isRecord","Providers","CalendarLocale","CalendarConfigProviderMoment","AuthProvider","children"],"sources":["../../src/lib/notification-drawer-provider.tsx","../../src/lib/notification-unread-provider.tsx","../../src/components/notification-drawer.tsx","../../src/lib/providers.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\n\ninterface NotificationDrawerContextValue {\n readonly close: () => void;\n readonly isOpen: boolean;\n readonly open: () => void;\n readonly toggle: () => void;\n}\n\nconst NotificationDrawerContext =\n createContext<NotificationDrawerContextValue | null>(null);\n\ninterface NotificationDrawerProviderProps {\n readonly children: ReactNode;\n}\n\n/**\n * Controls the open/closed state of the BPM notification drawer. Wraps\n * children with a context that `<NotificationDrawer />` reads to mount /\n * hide itself, and that the host navigation reads (via\n * `<BPMNotificationBellButton />` or `useNotificationDrawer().open`) to\n * open the drawer when the bell icon is clicked.\n *\n * When used outside this provider, the returned hook is a safe no-op so\n * components don't crash in test or storybook environments.\n */\nexport function NotificationDrawerProvider({\n children,\n}: NotificationDrawerProviderProps): ReactElement {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback((): void => {\n setIsOpen(true);\n }, []);\n const close = useCallback((): void => {\n setIsOpen(false);\n }, []);\n const toggle = useCallback((): void => {\n setIsOpen((current) => !current);\n }, []);\n\n const value = useMemo<NotificationDrawerContextValue>(\n () => ({ close, isOpen, open, toggle }),\n [close, isOpen, open, toggle],\n );\n\n return (\n <NotificationDrawerContext.Provider value={value}>\n {children}\n </NotificationDrawerContext.Provider>\n );\n}\n\n/**\n * Read the BPM notification drawer's open state and control helpers.\n * Returns a no-op stub when used outside `<NotificationDrawerProvider>`.\n */\nexport function useNotificationDrawer(): NotificationDrawerContextValue {\n const context = useContext(NotificationDrawerContext);\n if (!context) {\n return {\n close: (): void => undefined,\n isOpen: false,\n open: (): void => undefined,\n toggle: (): void => undefined,\n };\n }\n return context;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { readUnreadNotificationCount } from '@rytass/bpm-core-client/workflow';\nimport { useAuth } from './auth-provider';\n\ninterface NotificationUnreadContextValue {\n readonly refreshUnreadCount: () => Promise<number>;\n readonly unreadCount: number;\n}\n\nconst NotificationUnreadContext =\n createContext<NotificationUnreadContextValue | null>(null);\n\ninterface NotificationUnreadProviderProps {\n readonly children: ReactNode;\n}\n\n/**\n * Polls BPM for the current member's unread notification count via\n * `readUnreadNotificationCount` and exposes it through context for the\n * host navigation (`<BPMNotificationBellButton />` badge or any custom\n * trigger using `useNotificationUnread().unreadCount`) and the BPM\n * `<NotificationDrawer />` (header count). Refresh is triggered on\n * mount and whenever the auth member id changes; consumers can call\n * `refreshUnreadCount()` after acknowledging a notification.\n */\nexport function NotificationUnreadProvider({\n children,\n}: NotificationUnreadProviderProps): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [unreadCount, setUnreadCount] = useState(0);\n\n const refreshUnreadCount = useCallback(async (): Promise<number> => {\n if (!currentMemberId) {\n setUnreadCount(0);\n return 0;\n }\n const next = await readUnreadNotificationCount(currentMemberId);\n setUnreadCount(next);\n return next;\n }, [currentMemberId]);\n\n useEffect((): (() => void) => {\n let active = true;\n void (async () => {\n try {\n const next = await refreshUnreadCount();\n if (active) setUnreadCount(next);\n } catch {\n if (active) setUnreadCount(0);\n }\n })();\n return (): void => {\n active = false;\n };\n }, [refreshUnreadCount]);\n\n const value = useMemo<NotificationUnreadContextValue>(\n () => ({ refreshUnreadCount, unreadCount }),\n [refreshUnreadCount, unreadCount],\n );\n\n return (\n <NotificationUnreadContext.Provider value={value}>\n {children}\n </NotificationUnreadContext.Provider>\n );\n}\n\n/**\n * Read the current unread-notification count and a manual refresh helper.\n * Returns a zero/no-op stub when used outside\n * `<NotificationUnreadProvider>`.\n */\nexport function useNotificationUnread(): NotificationUnreadContextValue {\n const context = useContext(NotificationUnreadContext);\n if (!context) {\n return {\n refreshUnreadCount: async (): Promise<number> => 0,\n unreadCount: 0,\n };\n }\n return context;\n}\n","'use client';\n\nimport {\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n type ChangeEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactElement,\n} from 'react';\nimport Drawer from '@mezzanine-ui/react/Drawer';\nimport Modal from '@mezzanine-ui/react/Modal';\nimport NotificationCenter from '@mezzanine-ui/react/NotificationCenter';\nimport Textarea from '@mezzanine-ui/react/Textarea';\nimport { Typography } from '@mezzanine-ui/react';\nimport type { NotificationSeverity } from '@mezzanine-ui/core/notification-center';\nimport type { DropdownOption } from '@mezzanine-ui/core/dropdown/dropdown';\nimport {\n archiveNotifications,\n decideTask,\n listNotifications,\n markAllNotificationsRead,\n markNotificationRead,\n unarchiveNotifications,\n type NotificationRecord,\n type NotificationResolution,\n type NotificationType,\n} from '@rytass/bpm-core-client/workflow';\nimport { useAuth } from '../lib/auth-provider';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { BPMFormField } from './bpm-form-field';\n\ntype FilterValue = 'all' | 'read' | 'unread';\n\ntype TimeGroup = 'today' | 'yesterday' | 'past7Days' | 'earlier';\n\nconst TIME_GROUP_ORDER: readonly TimeGroup[] = [\n 'today',\n 'yesterday',\n 'past7Days',\n 'earlier',\n];\n\nconst TIME_GROUP_LABEL: Readonly<Record<TimeGroup, string>> = {\n earlier: '更早',\n past7Days: '過去七天',\n today: '今天',\n yesterday: '昨天',\n};\n\nconst PAGE_SIZE = 50;\n\n/** Identifiers for the per-notification `...` dropdown menu entries. */\ntype NotificationAction =\n | 'approve'\n | 'archive'\n | 'reject'\n | 'unarchive'\n | 'view'\n | 'read';\n\n/** Identifier for the filter-bar dropdown entry toggling the archived view. */\nconst TOGGLE_ARCHIVED_OPTION_ID = 'toggleArchived';\n\n/**\n * Build the `...` dropdown options for one notification. Approve / reject are\n * offered only while the server still reports the notification as `actionable`\n * (an unresolved task assignment) — once the task is decided / cancelled the\n * backend flips `actionable` to false, so a stale \"同意\" can never appear.\n * A task that fills in form fields (`hasFillableFields`) cannot be approved\n * from here — the fill has to be entered on the case page — so its \"同意\"\n * becomes \"開啟案件\".\n * \"查看案件\" needs an `instanceId`; \"標為已讀\" only shows while unread.\n * Archiving is independent of read state, so the entry flips between\n * \"封存\" / \"取消封存\" for every notification.\n */\nfunction buildNotificationOptions(\n record: NotificationRecord,\n): readonly DropdownOption[] {\n const canReject = readNotificationAllowReject(record) !== false;\n const opensToFill =\n record.actionable &&\n Boolean(record.instanceId) &&\n readNotificationHasFillableFields(record);\n\n return [\n ...(record.actionable\n ? ([\n opensToFill\n ? { id: 'view', name: '開啟案件' }\n : { id: 'approve', name: '同意' },\n ...(canReject ? [{ id: 'reject', name: '拒絕' }] : []),\n ] satisfies DropdownOption[])\n : []),\n ...(record.instanceId && !opensToFill\n ? ([{ id: 'view', name: '查看案件' }] satisfies DropdownOption[])\n : []),\n ...(record.status !== 'READ'\n ? ([{ id: 'read', name: '標為已讀' }] satisfies DropdownOption[])\n : []),\n record.archivedAt\n ? { id: 'unarchive', name: '取消封存' }\n : { id: 'archive', name: '封存' },\n ];\n}\n\n/**\n * Title shown for a resolved task-assignment notification, replacing the\n * stored \"新的待簽任務\" wording so a decided card no longer reads as pending.\n * The stored `body` is kept as historical context (which case / node).\n */\nconst RESOLVED_TITLE: Readonly<Record<NotificationResolution, string>> = {\n APPROVED: '簽核任務已同意',\n REJECTED: '簽核任務已拒絕',\n RETURNED: '簽核任務已退回',\n SUPERSEDED: '簽核任務已結束',\n TRANSFERRED: '簽核任務已轉派',\n};\n\nfunction resolveDisplayTitle(record: NotificationRecord): string {\n return record.resolution ? RESOLVED_TITLE[record.resolution] : record.title;\n}\n\n/**\n * Severity (icon colour) for a notification. Once resolved, it reflects the\n * outcome — green for approved, red for rejected, neutral otherwise — instead\n * of the original by-type colour.\n */\nfunction resolveSeverity(record: NotificationRecord): NotificationSeverity {\n if (record.resolution === 'APPROVED') return 'success';\n if (record.resolution === 'REJECTED') return 'error';\n if (record.resolution) return 'info';\n return toSeverity(record.type);\n}\n\n/**\n * Right-side notification drawer mounted at the root by `<Providers>`.\n * Opens / closes via `useNotificationDrawer()`, polls\n * `listNotifications()` for the current member, supports filter\n * (`all` / `read` / `unread`), per-row mark-read, per-row archive / unarchive,\n * bulk mark-all-read, and load-more pagination. Archived notifications are\n * hidden until the filter-bar menu turns \"顯示已封存\" on. Clicking a row with an\n * `instanceId` navigates to `/instances/<id>` via the host's router adapter.\n */\nexport function NotificationDrawer(): ReactElement | null {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const { member } = useAuth();\n const { close, isOpen } = useNotificationDrawer();\n const { refreshUnreadCount } = useNotificationUnread();\n const currentMemberId = member?.memberId ?? null;\n const [rows, setRows] = useState<readonly NotificationRecord[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [page, setPage] = useState(1);\n const [loading, setLoading] = useState(false);\n const [bulkLoading, setBulkLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const [filter, setFilter] = useState<FilterValue>('all');\n const [includeArchived, setIncludeArchived] = useState(false);\n const [approveTarget, setApproveTarget] = useState<NotificationRecord | null>(\n null,\n );\n const [approveComment, setApproveComment] = useState('');\n const [rejectTarget, setRejectTarget] = useState<NotificationRecord | null>(\n null,\n );\n const [rejectReason, setRejectReason] = useState('');\n const [deciding, setDeciding] = useState(false);\n\n const trimmedApproveComment = approveComment.trim();\n const trimmedRejectReason = rejectReason.trim();\n\n const loadPage = useCallback(\n async (nextPage: number, append: boolean): Promise<boolean> => {\n if (!currentMemberId) return false;\n setLoading(true);\n setError(null);\n try {\n const result = await listNotifications({\n includeArchived,\n includeRead: true,\n page: nextPage,\n pageSize: PAGE_SIZE,\n recipientMemberId: currentMemberId,\n });\n setRows((current): readonly NotificationRecord[] =>\n append ? [...current, ...result.notifications] : result.notifications,\n );\n setTotalCount(result.totalCount);\n setPage(nextPage);\n await refreshUnreadCount();\n return true;\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n return false;\n } finally {\n setLoading(false);\n }\n },\n [currentMemberId, includeArchived, refreshUnreadCount],\n );\n\n useEffect((): void => {\n if (!isOpen || !currentMemberId) return;\n setError(null);\n setNotice(null);\n void loadPage(1, false);\n }, [isOpen, currentMemberId, loadPage]);\n\n const handleFilterChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>): void => {\n const next = event.target.value;\n if (next === 'all' || next === 'read' || next === 'unread') setFilter(next);\n },\n [],\n );\n\n const handleMarkAllRead = useCallback(async (): Promise<void> => {\n if (!currentMemberId || bulkLoading) return;\n setBulkLoading(true);\n setError(null);\n try {\n await markAllNotificationsRead({ recipientMemberId: currentMemberId });\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setBulkLoading(false);\n }\n }, [bulkLoading, currentMemberId, loadPage]);\n\n const handleLoadMore = useCallback((): void => {\n if (loading) return;\n void loadPage(page + 1, true);\n }, [loading, loadPage, page]);\n\n const handleMarkRead = useCallback(\n async (id: string): Promise<void> => {\n if (!currentMemberId) return;\n try {\n await markNotificationRead({ id, readerMemberId: currentMemberId });\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [currentMemberId, loadPage],\n );\n\n const handleArchive = useCallback(\n async (record: NotificationRecord): Promise<void> => {\n if (!currentMemberId) return;\n setError(null);\n setNotice(null);\n try {\n // Name the notification the way the row does — `title` is the raw\n // stored title, which can differ from the rendered one.\n const label = resolveDisplayTitle(record);\n\n if (record.archivedAt) {\n await unarchiveNotifications({ ids: [record.id] });\n setNotice(`已將「${label}」移出封存。`);\n } else {\n await archiveNotifications({ ids: [record.id] });\n setNotice(`已封存「${label}」。`);\n }\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [currentMemberId, loadPage],\n );\n\n const handleFilterAreaSelect = useCallback((option: DropdownOption): void => {\n if (option.id !== TOGGLE_ARCHIVED_OPTION_ID) return;\n setNotice(null);\n setIncludeArchived((current): boolean => !current);\n }, []);\n\n const handleOpenInstance = useCallback(\n async (record: NotificationRecord): Promise<void> => {\n if (!record.instanceId || !currentMemberId) return;\n try {\n if (record.status !== 'READ') {\n await markNotificationRead({\n id: record.id,\n readerMemberId: currentMemberId,\n });\n await refreshUnreadCount();\n }\n close();\n router.push(routes.caseDetail(record.instanceId));\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [close, currentMemberId, refreshUnreadCount, router, routes],\n );\n\n const openApproveModal = useCallback((record: NotificationRecord): void => {\n setError(null);\n setApproveTarget(record);\n setApproveComment('');\n }, []);\n\n const closeApproveModal = useCallback((): void => {\n if (deciding) return;\n setApproveTarget(null);\n setApproveComment('');\n }, [deciding]);\n\n const handleApproveConfirm = useCallback(async (): Promise<void> => {\n const target = approveTarget;\n if (!target?.taskId || !currentMemberId || deciding) return;\n setDeciding(true);\n setError(null);\n setNotice(null);\n try {\n await decideTask({\n action: 'APPROVED',\n comment: trimmedApproveComment || null,\n decidedByMemberId: currentMemberId,\n taskId: target.taskId,\n });\n const refreshed = await loadPage(1, false);\n if (!refreshed) return;\n setApproveTarget(null);\n setApproveComment('');\n setNotice(`已同意「${target.title}」。`);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setDeciding(false);\n }\n }, [\n approveTarget,\n currentMemberId,\n deciding,\n loadPage,\n trimmedApproveComment,\n ]);\n\n const openRejectModal = useCallback((record: NotificationRecord): void => {\n setRejectTarget(record);\n setRejectReason('');\n }, []);\n\n const closeRejectModal = useCallback((): void => {\n setRejectTarget(null);\n setRejectReason('');\n }, []);\n\n const handleRejectConfirm = useCallback(async (): Promise<void> => {\n const target = rejectTarget;\n if (!target?.taskId || !currentMemberId || !trimmedRejectReason || deciding)\n return;\n setDeciding(true);\n setError(null);\n setNotice(null);\n try {\n await decideTask({\n action: 'REJECTED',\n comment: trimmedRejectReason,\n decidedByMemberId: currentMemberId,\n taskId: target.taskId,\n });\n setRejectTarget(null);\n setRejectReason('');\n setNotice(`已拒絕「${target.title}」。`);\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setDeciding(false);\n }\n }, [currentMemberId, deciding, loadPage, rejectTarget, trimmedRejectReason]);\n\n const handleBadgeSelect = useCallback(\n (record: NotificationRecord, option: DropdownOption): void => {\n const action = option.id as NotificationAction;\n if (action === 'approve') openApproveModal(record);\n else if (action === 'reject') openRejectModal(record);\n else if (action === 'view') void handleOpenInstance(record);\n else if (action === 'read') void handleMarkRead(record.id);\n else if (action === 'archive' || action === 'unarchive')\n void handleArchive(record);\n },\n [\n handleArchive,\n handleMarkRead,\n handleOpenInstance,\n openApproveModal,\n openRejectModal,\n ],\n );\n\n /**\n * Whether an event that reached the card wrapper actually happened inside the\n * card, and not on one of its action controls.\n *\n * Two escape hatches are needed. The `...` menu button (and its `<svg>` icon —\n * hence the `Element` check, SVG nodes are not HTMLElements) sits inside the\n * card and must open the dropdown rather than navigate. The dropdown itself\n * renders into a portal, and React replays events along the component tree\n * rather than the DOM tree, so picking \"封存\" also reaches this handler even\n * though the `<li role=\"option\">` lives outside the card's DOM — and it is not\n * a `<button>`, so the first guard alone misses it.\n */\n const isCardActivation = useCallback(\n (target: EventTarget | null, card: HTMLDivElement): boolean => {\n if (!(target instanceof Node) || !card.contains(target)) return false;\n\n return !(target instanceof Element && target.closest('button'));\n },\n [],\n );\n\n const handleCardActivate = useCallback(\n (record: NotificationRecord, event: ReactMouseEvent<HTMLDivElement>): void => {\n if (!isCardActivation(event.target, event.currentTarget)) return;\n void handleOpenInstance(record);\n },\n [handleOpenInstance, isCardActivation],\n );\n\n const handleCardKeyDown = useCallback(\n (\n record: NotificationRecord,\n event: ReactKeyboardEvent<HTMLDivElement>,\n ): void => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n if (!isCardActivation(event.target, event.currentTarget)) return;\n event.preventDefault();\n void handleOpenInstance(record);\n },\n [handleOpenInstance, isCardActivation],\n );\n\n const filteredRows = useMemo(\n (): readonly NotificationRecord[] =>\n rows.filter((row): boolean => {\n if (filter === 'all') return true;\n if (filter === 'read') return row.status === 'READ';\n return row.status !== 'READ';\n }),\n [filter, rows],\n );\n\n const groupedRows = useMemo(\n (): ReadonlyArray<readonly [TimeGroup, readonly NotificationRecord[]]> => {\n const now = new Date();\n const buckets = TIME_GROUP_ORDER.reduce<\n Record<TimeGroup, NotificationRecord[]>\n >(\n (accumulator, group) => {\n accumulator[group] = [];\n return accumulator;\n },\n { earlier: [], past7Days: [], today: [], yesterday: [] },\n );\n filteredRows.forEach((row): void => {\n buckets[resolveTimeGroup(row.createdAt, now)].push(row);\n });\n return TIME_GROUP_ORDER.map(\n (group) => [group, buckets[group]] as const,\n ).filter(([, items]) => items.length > 0);\n },\n [filteredRows],\n );\n\n const hasMore = rows.length < totalCount;\n\n if (!currentMemberId) return null;\n\n return (\n <>\n <Drawer\n bottomGhostActionDisabled={bulkLoading || loading}\n bottomGhostActionLoading={bulkLoading}\n bottomGhostActionText=\"全部標為已讀\"\n bottomOnGhostActionClick={(): void => {\n void handleMarkAllRead();\n }}\n bottomOnPrimaryActionClick={(): void => {\n handleLoadMore();\n }}\n bottomPrimaryActionDisabled={!hasMore || loading}\n bottomPrimaryActionLoading={loading && hasMore}\n bottomPrimaryActionText={hasMore ? '載入更多' : '已顯示全部'}\n contentKey={`${filter}:${includeArchived}:${rows.length}`}\n filterAreaAllRadioLabel=\"全部\"\n filterAreaOnRadioChange={handleFilterChange}\n filterAreaOnSelect={handleFilterAreaSelect}\n filterAreaOptions={[\n {\n id: TOGGLE_ARCHIVED_OPTION_ID,\n name: includeArchived ? '隱藏已封存' : '顯示已封存',\n },\n ]}\n filterAreaReadRadioLabel=\"已讀\"\n filterAreaShow\n filterAreaUnreadRadioLabel=\"未讀\"\n filterAreaValue={filter}\n headerTitle=\"通知中心\"\n isBottomDisplay\n isHeaderDisplay\n onClose={close}\n open={isOpen}\n size=\"medium\"\n >\n <div role=\"list\">\n {error ? (\n <p\n role=\"alert\"\n style={{\n color: 'var(--mzn-color-text-error, #d92d20)',\n padding: '12px 16px',\n }}\n >\n {error}\n </p>\n ) : null}\n {notice ? (\n <p\n role=\"status\"\n style={{\n color: 'var(--mzn-color-text-success, #079455)',\n padding: '12px 16px',\n }}\n >\n {notice}\n </p>\n ) : null}\n {groupedRows.length === 0 ? (\n <p\n style={{\n color: 'var(--mzn-color-text-secondary, #6b7280)',\n padding: '24px 16px',\n textAlign: 'center',\n }}\n >\n {loading ? '載入中…' : '目前沒有通知'}\n </p>\n ) : null}\n {groupedRows.map(([group, items]) => (\n <Fragment key={group}>\n {items.map((record, itemIndex) => {\n const openable = record.instanceId !== null;\n\n return (\n <div\n key={record.id}\n onClick={\n openable\n ? (event: ReactMouseEvent<HTMLDivElement>): void => {\n handleCardActivate(record, event);\n }\n : undefined\n }\n onKeyDown={\n openable\n ? (event: ReactKeyboardEvent<HTMLDivElement>): void => {\n handleCardKeyDown(record, event);\n }\n : undefined\n }\n role={openable ? 'button' : undefined}\n style={openable ? { cursor: 'pointer' } : undefined}\n tabIndex={openable ? 0 : undefined}\n >\n <NotificationCenter\n appendTips={record.archivedAt ? '已封存' : undefined}\n description={record.body}\n onBadgeSelect={(option: DropdownOption): void => {\n handleBadgeSelect(record, option);\n }}\n options={[...buildNotificationOptions(record)]}\n prependTips={\n itemIndex === 0 ? TIME_GROUP_LABEL[group] : undefined\n }\n reference={record.id}\n severity={resolveSeverity(record)}\n showBadge={record.status !== 'READ'}\n timeStamp={record.createdAt}\n title={resolveDisplayTitle(record)}\n type=\"drawer\"\n />\n </div>\n );\n })}\n </Fragment>\n ))}\n </div>\n </Drawer>\n <Modal\n cancelText=\"取消\"\n confirmText=\"送出同意\"\n loading={deciding}\n modalType=\"standard\"\n onCancel={closeApproveModal}\n onClose={closeApproveModal}\n onConfirm={(): void => {\n void handleApproveConfirm();\n }}\n open={approveTarget !== null}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"可以留下同意說明,內容會記錄在簽核歷程;不填也可以直接送出。\"\n title=\"簽核意見\"\n >\n {error ? (\n <Typography color=\"text-error\" role=\"alert\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <BPMFormField label=\"簽核意見\" name=\"notificationApproveComment\">\n <Textarea\n autoFocus\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setApproveComment(event.target.value);\n }}\n placeholder=\"選填,例如補充核准條件或提醒事項\"\n rows={4}\n value={approveComment}\n />\n </BPMFormField>\n </Modal>\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{\n disabled: !trimmedRejectReason,\n variant: 'destructive-primary',\n }}\n confirmText=\"送出拒絕\"\n loading={deciding}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={closeRejectModal}\n onClose={closeRejectModal}\n onConfirm={(): void => {\n void handleRejectConfirm();\n }}\n open={rejectTarget !== null}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"拒絕案件時必須留下原因,供發起人與後續追蹤查看。\"\n title=\"拒絕原因\"\n >\n <Textarea\n autoFocus\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setRejectReason(event.target.value);\n }}\n placeholder=\"請輸入拒絕原因\"\n rows={4}\n value={rejectReason}\n />\n </Modal>\n </>\n );\n}\n\nfunction toSeverity(type: NotificationType): NotificationSeverity {\n if (type === 'SLA_OVERDUE') return 'error';\n if (type === 'SLA_WARNING') return 'warning';\n if (type === 'INSTANCE_COMPLETED') return 'success';\n return 'info';\n}\n\nfunction resolveTimeGroup(value: string, now: Date): TimeGroup {\n const notificationDate = new Date(value);\n if (Number.isNaN(notificationDate.getTime())) return 'earlier';\n const nowStartOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n const notificationStartOfDay = new Date(\n notificationDate.getFullYear(),\n notificationDate.getMonth(),\n notificationDate.getDate(),\n );\n if (notificationStartOfDay.getTime() === nowStartOfDay.getTime()) return 'today';\n const yesterdayStartOfDay = new Date(nowStartOfDay);\n yesterdayStartOfDay.setDate(yesterdayStartOfDay.getDate() - 1);\n if (notificationStartOfDay.getTime() === yesterdayStartOfDay.getTime())\n return 'yesterday';\n const diffInDays =\n (now.getTime() - notificationDate.getTime()) / (1000 * 60 * 60 * 24);\n if (diffInDays <= 7) return 'past7Days';\n return 'earlier';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n\nfunction readNotificationAllowReject(\n record: NotificationRecord,\n): boolean | null {\n try {\n const payload: unknown = JSON.parse(record.payloadJson);\n\n if (isRecord(payload) && typeof payload.allowReject === 'boolean') {\n return payload.allowReject;\n }\n\n return record.allowReject;\n } catch {\n return record.allowReject;\n }\n}\n\n/**\n * `TASK_ASSIGNED` / `TASK_TRANSFERRED` payloads carry `hasFillableFields`;\n * older notifications do not, which reads as `false`.\n */\nfunction readNotificationHasFillableFields(\n record: NotificationRecord,\n): boolean {\n try {\n const payload: unknown = JSON.parse(record.payloadJson);\n\n return isRecord(payload) && payload.hasFillableFields === true;\n } catch {\n return false;\n }\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","'use client';\n\nimport type { ReactElement, ReactNode } from 'react';\nimport {\n CalendarConfigProviderMoment,\n CalendarLocale,\n} from '@mezzanine-ui/react/moment';\nimport { AuthProvider } from './auth-provider';\nimport {\n BPMLanguageProvider,\n DEFAULT_BPM_LANGUAGE,\n type BPMLanguage,\n} from './language-provider';\nimport { NotificationDrawer } from '../components/notification-drawer';\nimport { NotificationDrawerProvider } from './notification-drawer-provider';\nimport { NotificationUnreadProvider } from './notification-unread-provider';\n\ninterface ProvidersProps {\n readonly children: ReactNode;\n /** Override Mezzanine calendar locale. Defaults to `CalendarLocale.ZH_TW`. */\n readonly locale?: CalendarLocale;\n /**\n * Language BPM views write their messages in, such as publish-lint errors.\n * Defaults to `'zh-TW'`. Pass the user's choice as state to let them switch.\n * Independent of `locale`, which only formats calendar dates.\n */\n readonly language?: BPMLanguage;\n /**\n * Public paths that should not trigger redirect to `/login` when there\n * is no session. Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n */\n readonly publicPaths?: readonly string[];\n /** Where to send unauthenticated users. Defaults to `'/login'`. */\n readonly loginPath?: string;\n}\n\n/**\n * One-stop BPM admin provider stack. Wires:\n *\n * - Mezzanine UI calendar locale (moment-based, `ZH_TW` by default)\n * - `<BPMLanguageProvider>` (message language, `'zh-TW'` by default)\n * - `<AuthProvider>` (BPM session via REST `/auth/*`)\n * - `<NotificationUnreadProvider>` (polls unread count)\n * - `<NotificationDrawerProvider>` (controls drawer open/close state)\n * - `<NotificationDrawer />` mounted at the root so the host navigation\n * bell (`<BPMNotificationBellButton />` or any custom trigger calling\n * `useNotificationDrawer().open`) can open it.\n *\n * Consumer hosts wrap this **inside** a `<RouterAdapterProvider>` (provided\n * by the `pages/*` subpath shims when consuming via Next.js, or wired by\n * hand for other frameworks).\n */\nexport function Providers({\n children,\n locale = CalendarLocale.ZH_TW,\n language = DEFAULT_BPM_LANGUAGE,\n publicPaths,\n loginPath,\n}: ProvidersProps): ReactElement {\n return (\n <CalendarConfigProviderMoment locale={locale}>\n <BPMLanguageProvider language={language}>\n <AuthProvider publicPaths={publicPaths} loginPath={loginPath}>\n <NotificationUnreadProvider>\n <NotificationDrawerProvider>\n {children}\n <NotificationDrawer />\n </NotificationDrawerProvider>\n </NotificationUnreadProvider>\n </AuthProvider>\n </BPMLanguageProvider>\n </CalendarConfigProviderMoment>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- "use client";const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./auth-provider-4BeCw7cI.cjs"),n=require("./language-provider-Bdnx3Gxl.cjs"),r=require("./router-adapter--gYs13E8.cjs"),i=require("./routes-config-C1j_vFKo.cjs"),a=require("./bpm-form-field-Bc6k4ZEO.cjs");let o=require("react"),s=require("@mezzanine-ui/react"),c=require("react/jsx-runtime"),l=require("@rytass/bpm-core-client/workflow"),u=require("@mezzanine-ui/react/moment"),d=require("@mezzanine-ui/react/Drawer");d=e.t(d,1);let f=require("@mezzanine-ui/react/Modal");f=e.t(f,1);let p=require("@mezzanine-ui/react/NotificationCenter");p=e.t(p,1);let m=require("@mezzanine-ui/react/Textarea");m=e.t(m,1);var h=(0,o.createContext)(null);function g({children:e}){let[t,n]=(0,o.useState)(!1),r=(0,o.useCallback)(()=>{n(!0)},[]),i=(0,o.useCallback)(()=>{n(!1)},[]),a=(0,o.useCallback)(()=>{n(e=>!e)},[]),s=(0,o.useMemo)(()=>({close:i,isOpen:t,open:r,toggle:a}),[i,t,r,a]);return(0,c.jsx)(h.Provider,{value:s,children:e})}function _(){return(0,o.useContext)(h)||{close:()=>void 0,isOpen:!1,open:()=>void 0,toggle:()=>void 0}}var v=(0,o.createContext)(null);function y({children:e}){let{member:n}=t.n(),r=n?.memberId??null,[i,a]=(0,o.useState)(0),s=(0,o.useCallback)(async()=>{if(!r)return a(0),0;let e=await(0,l.readUnreadNotificationCount)(r);return a(e),e},[r]);(0,o.useEffect)(()=>{let e=!0;return(async()=>{try{let t=await s();e&&a(t)}catch{e&&a(0)}})(),()=>{e=!1}},[s]);let u=(0,o.useMemo)(()=>({refreshUnreadCount:s,unreadCount:i}),[s,i]);return(0,c.jsx)(v.Provider,{value:u,children:e})}function ee(){return(0,o.useContext)(v)||{refreshUnreadCount:async()=>0,unreadCount:0}}var b=[`today`,`yesterday`,`past7Days`,`earlier`],te={earlier:`更早`,past7Days:`過去七天`,today:`今天`,yesterday:`昨天`},ne=50,x=`toggleArchived`;function re(e){let t=E(e)!==!1,n=e.actionable&&!!e.instanceId&&D(e);return[...e.actionable?[n?{id:`view`,name:`開啟案件`}:{id:`approve`,name:`同意`},...t?[{id:`reject`,name:`拒絕`}]:[]]:[],...e.instanceId&&!n?[{id:`view`,name:`查看案件`}]:[],...e.status===`READ`?[]:[{id:`read`,name:`標為已讀`}],e.archivedAt?{id:`unarchive`,name:`取消封存`}:{id:`archive`,name:`封存`}]}var S={APPROVED:`簽核任務已同意`,REJECTED:`簽核任務已拒絕`,RETURNED:`簽核任務已退回`,SUPERSEDED:`簽核任務已結束`,TRANSFERRED:`簽核任務已轉派`};function ie(e){return e.resolution?S[e.resolution]:e.title}function ae(e){return e.resolution===`APPROVED`?`success`:e.resolution===`REJECTED`?`error`:e.resolution?`info`:w(e.type)}function C(){let e=r.r(),n=i.r(),{member:u}=t.n(),{close:h,isOpen:g}=_(),{refreshUnreadCount:v}=ee(),y=u?.memberId??null,[S,C]=(0,o.useState)([]),[w,E]=(0,o.useState)(0),[D,O]=(0,o.useState)(1),[k,A]=(0,o.useState)(!1),[j,M]=(0,o.useState)(!1),[N,P]=(0,o.useState)(null),[F,I]=(0,o.useState)(null),[L,se]=(0,o.useState)(`all`),[R,ce]=(0,o.useState)(!1),[z,B]=(0,o.useState)(null),[V,H]=(0,o.useState)(``),[U,W]=(0,o.useState)(null),[G,K]=(0,o.useState)(``),[q,J]=(0,o.useState)(!1),le=V.trim(),Y=G.trim(),X=(0,o.useCallback)(async(e,t)=>{if(!y)return!1;A(!0),P(null);try{let n=await(0,l.listNotifications)({includeArchived:R,includeRead:!0,page:e,pageSize:ne,recipientMemberId:y});return C(e=>t?[...e,...n.notifications]:n.notifications),E(n.totalCount),O(e),await v(),!0}catch(e){return P(T(e)),!1}finally{A(!1)}},[y,R,v]);(0,o.useEffect)(()=>{g&&y&&(P(null),I(null),X(1,!1))},[g,y,X]);let ue=(0,o.useCallback)(e=>{let t=e.target.value;(t===`all`||t===`read`||t===`unread`)&&se(t)},[]),de=(0,o.useCallback)(async()=>{if(y&&!j){M(!0),P(null);try{await(0,l.markAllNotificationsRead)({recipientMemberId:y}),await X(1,!1)}catch(e){P(T(e))}finally{M(!1)}}},[j,y,X]),fe=(0,o.useCallback)(()=>{k||X(D+1,!0)},[k,X,D]),pe=(0,o.useCallback)(async e=>{if(y)try{await(0,l.markNotificationRead)({id:e,readerMemberId:y}),await X(1,!1)}catch(e){P(T(e))}},[y,X]),me=(0,o.useCallback)(async e=>{if(y){P(null),I(null);try{let t=ie(e);e.archivedAt?(await(0,l.unarchiveNotifications)({ids:[e.id]}),I(`已將「${t}」移出封存。`)):(await(0,l.archiveNotifications)({ids:[e.id]}),I(`已封存「${t}」。`)),await X(1,!1)}catch(e){P(T(e))}}},[y,X]),he=(0,o.useCallback)(e=>{e.id===x&&(I(null),ce(e=>!e))},[]),Z=(0,o.useCallback)(async t=>{if(t.instanceId&&y)try{t.status!==`READ`&&(await(0,l.markNotificationRead)({id:t.id,readerMemberId:y}),await v()),h(),e.push(n.caseDetail(t.instanceId))}catch(e){P(T(e))}},[h,y,v,e,n]),ge=(0,o.useCallback)(e=>{P(null),B(e),H(``)},[]),_e=(0,o.useCallback)(()=>{q||(B(null),H(``))},[q]),ve=(0,o.useCallback)(async()=>{let e=z;if(e?.taskId&&y&&!q){J(!0),P(null),I(null);try{if(await(0,l.decideTask)({action:`APPROVED`,comment:le||null,decidedByMemberId:y,taskId:e.taskId}),!await X(1,!1))return;B(null),H(``),I(`已同意「${e.title}」。`)}catch(e){P(T(e))}finally{J(!1)}}},[z,y,q,X,le]),ye=(0,o.useCallback)(e=>{W(e),K(``)},[]),be=(0,o.useCallback)(()=>{W(null),K(``)},[]),xe=(0,o.useCallback)(async()=>{let e=U;if(e?.taskId&&y&&Y&&!q){J(!0),P(null),I(null);try{await(0,l.decideTask)({action:`REJECTED`,comment:Y,decidedByMemberId:y,taskId:e.taskId}),W(null),K(``),I(`已拒絕「${e.title}」。`),await X(1,!1)}catch(e){P(T(e))}finally{J(!1)}}},[y,q,X,U,Y]),Se=(0,o.useCallback)((e,t)=>{let n=t.id;n===`approve`?ge(e):n===`reject`?ye(e):n===`view`?Z(e):n===`read`?pe(e.id):(n===`archive`||n===`unarchive`)&&me(e)},[me,pe,Z,ge,ye]),Q=(0,o.useCallback)((e,t)=>!(e instanceof Node)||!t.contains(e)?!1:!(e instanceof Element&&e.closest(`button`)),[]),Ce=(0,o.useCallback)((e,t)=>{Q(t.target,t.currentTarget)&&Z(e)},[Z,Q]),we=(0,o.useCallback)((e,t)=>{(t.key===`Enter`||t.key===` `)&&Q(t.target,t.currentTarget)&&(t.preventDefault(),Z(e))},[Z,Q]),Te=(0,o.useMemo)(()=>S.filter(e=>L===`all`?!0:L===`read`?e.status===`READ`:e.status!==`READ`),[L,S]),Ee=(0,o.useMemo)(()=>{let e=new Date,t=b.reduce((e,t)=>(e[t]=[],e),{earlier:[],past7Days:[],today:[],yesterday:[]});return Te.forEach(n=>{t[oe(n.createdAt,e)].push(n)}),b.map(e=>[e,t[e]]).filter(([,e])=>e.length>0)},[Te]),$=S.length<w;return y?(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(d.default,{bottomGhostActionDisabled:j||k,bottomGhostActionLoading:j,bottomGhostActionText:`全部標為已讀`,bottomOnGhostActionClick:()=>{de()},bottomOnPrimaryActionClick:()=>{fe()},bottomPrimaryActionDisabled:!$||k,bottomPrimaryActionLoading:k&&$,bottomPrimaryActionText:$?`載入更多`:`已顯示全部`,contentKey:`${L}:${R}:${S.length}`,filterAreaAllRadioLabel:`全部`,filterAreaOnRadioChange:ue,filterAreaOnSelect:he,filterAreaOptions:[{id:x,name:R?`隱藏已封存`:`顯示已封存`}],filterAreaReadRadioLabel:`已讀`,filterAreaShow:!0,filterAreaUnreadRadioLabel:`未讀`,filterAreaValue:L,headerTitle:`通知中心`,isBottomDisplay:!0,isHeaderDisplay:!0,onClose:h,open:g,size:`medium`,children:(0,c.jsxs)(`div`,{role:`list`,children:[N?(0,c.jsx)(`p`,{role:`alert`,style:{color:`var(--mzn-color-text-error, #d92d20)`,padding:`12px 16px`},children:N}):null,F?(0,c.jsx)(`p`,{role:`status`,style:{color:`var(--mzn-color-text-success, #079455)`,padding:`12px 16px`},children:F}):null,Ee.length===0?(0,c.jsx)(`p`,{style:{color:`var(--mzn-color-text-secondary, #6b7280)`,padding:`24px 16px`,textAlign:`center`},children:k?`載入中…`:`目前沒有通知`}):null,Ee.map(([e,t])=>(0,c.jsx)(o.Fragment,{children:t.map((t,n)=>{let r=t.instanceId!==null;return(0,c.jsx)(`div`,{onClick:r?e=>{Ce(t,e)}:void 0,onKeyDown:r?e=>{we(t,e)}:void 0,role:r?`button`:void 0,style:r?{cursor:`pointer`}:void 0,tabIndex:r?0:void 0,children:(0,c.jsx)(p.default,{appendTips:t.archivedAt?`已封存`:void 0,description:t.body,onBadgeSelect:e=>{Se(t,e)},options:[...re(t)],prependTips:n===0?te[e]:void 0,reference:t.id,severity:ae(t),showBadge:t.status!==`READ`,timeStamp:t.createdAt,title:ie(t),type:`drawer`})},t.id)})},e))]})}),(0,c.jsxs)(f.default,{cancelText:`取消`,confirmText:`送出同意`,loading:q,modalType:`standard`,onCancel:_e,onClose:_e,onConfirm:()=>{ve()},open:z!==null,showModalFooter:!0,showModalHeader:!0,size:`regular`,supportingText:`可以留下同意說明,內容會記錄在簽核歷程;不填也可以直接送出。`,title:`簽核意見`,children:[N?(0,c.jsx)(s.Typography,{color:`text-error`,role:`alert`,variant:`body`,children:N}):null,(0,c.jsx)(a.t,{label:`簽核意見`,name:`notificationApproveComment`,children:(0,c.jsx)(m.default,{autoFocus:!0,onChange:e=>{H(e.target.value)},placeholder:`選填,例如補充核准條件或提醒事項`,rows:4,value:V})})]}),(0,c.jsx)(f.default,{cancelText:`取消`,confirmButtonProps:{disabled:!Y,variant:`destructive-primary`},confirmText:`送出拒絕`,loading:q,modalStatusType:`error`,modalType:`standard`,onCancel:be,onClose:be,onConfirm:()=>{xe()},open:U!==null,showModalFooter:!0,showModalHeader:!0,size:`regular`,supportingText:`拒絕案件時必須留下原因,供發起人與後續追蹤查看。`,title:`拒絕原因`,children:(0,c.jsx)(m.default,{autoFocus:!0,onChange:e=>{K(e.target.value)},placeholder:`請輸入拒絕原因`,rows:4,value:G})})]}):null}function w(e){return e===`SLA_OVERDUE`?`error`:e===`SLA_WARNING`?`warning`:e===`INSTANCE_COMPLETED`?`success`:`info`}function oe(e,t){let n=new Date(e);if(Number.isNaN(n.getTime()))return`earlier`;let r=new Date(t.getFullYear(),t.getMonth(),t.getDate()),i=new Date(n.getFullYear(),n.getMonth(),n.getDate());if(i.getTime()===r.getTime())return`today`;let a=new Date(r);return a.setDate(a.getDate()-1),i.getTime()===a.getTime()?`yesterday`:(t.getTime()-n.getTime())/864e5<=7?`past7Days`:`earlier`}function T(e){return e instanceof Error?e.message:`發生未知錯誤`}function E(e){try{let t=JSON.parse(e.payloadJson);return O(t)&&typeof t.allowReject==`boolean`?t.allowReject:e.allowReject}catch{return e.allowReject}}function D(e){try{let t=JSON.parse(e.payloadJson);return O(t)&&t.hasFillableFields===!0}catch{return!1}}function O(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function k({children:e,locale:r=u.CalendarLocale.ZH_TW,language:i=n.r,publicPaths:a,loginPath:o}){return(0,c.jsx)(u.CalendarConfigProviderMoment,{locale:r,children:(0,c.jsx)(n.t,{language:i,children:(0,c.jsx)(t.t,{publicPaths:a,loginPath:o,children:(0,c.jsx)(y,{children:(0,c.jsxs)(g,{children:[e,(0,c.jsx)(C,{})]})})})})})}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return ee}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return k}});
2
- //# sourceMappingURL=providers-D_KAgOOu.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"providers-D_KAgOOu.cjs","names":["createContext","useState","useCallback","useMemo","useContext","createContext","useAuth","useState","useCallback","readUnreadNotificationCount","useMemo","useContext","useRouterAdapter","useBPMRoutes","useAuth","useState","useCallback","listNotifications","markAllNotificationsRead","markNotificationRead","unarchiveNotifications","archiveNotifications","decideTask","useMemo","_Fragment","Drawer","Fragment","NotificationCenter","Modal","Typography","BPMFormField","Textarea","CalendarLocale","DEFAULT_BPM_LANGUAGE","CalendarConfigProviderMoment","BPMLanguageProvider","AuthProvider","require_rolldown_runtime","require_auth_provider","require_language_provider","require_router_adapter","require_routes_config","require_bpm_form_field","react","_mezzanine_ui_react","react_jsx_runtime","_rytass_bpm_core_client_workflow","_mezzanine_ui_react_moment","_mezzanine_ui_react_Drawer","_mezzanine_ui_react_Modal","_mezzanine_ui_react_NotificationCenter","_mezzanine_ui_react_Textarea","NotificationDrawerContext","NotificationDrawerProvider","isOpen","setIsOpen","open","close","toggle","current","value","useNotificationDrawer","NotificationUnreadContext","NotificationUnreadProvider","currentMemberId","member","unreadCount","setUnreadCount","refreshUnreadCount","next","active","useNotificationUnread","TIME_GROUP_ORDER","TIME_GROUP_LABEL","PAGE_SIZE","TOGGLE_ARCHIVED_OPTION_ID","buildNotificationOptions","canReject","readNotificationAllowReject","record","opensToFill","readNotificationHasFillableFields","RESOLVED_TITLE","resolveDisplayTitle","resolveSeverity","toSeverity","NotificationDrawer","router","routes","rows","setRows","totalCount","setTotalCount","page","setPage","loading","setLoading","bulkLoading","setBulkLoading","error","setError","notice","setNotice","filter","setFilter","includeArchived","setIncludeArchived","approveTarget","setApproveTarget","approveComment","setApproveComment","rejectTarget","setRejectTarget","rejectReason","setRejectReason","deciding","setDeciding","trimmedApproveComment","trimmedRejectReason","loadPage","result","nextPage","append","readErrorMessage","handleFilterChange","event","handleMarkAllRead","handleLoadMore","handleMarkRead","handleArchive","label","handleFilterAreaSelect","option","handleOpenInstance","openApproveModal","closeApproveModal","handleApproveConfirm","target","openRejectModal","closeRejectModal","handleRejectConfirm","handleBadgeSelect","action","isCardActivation","card","handleCardActivate","handleCardKeyDown","filteredRows","row","groupedRows","now","buckets","group","accumulator","resolveTimeGroup","items","hasMore","openable","itemIndex","type","notificationDate","nowStartOfDay","notificationStartOfDay","yesterdayStartOfDay","payload","isRecord","Providers","children"],"sources":["../../src/lib/notification-drawer-provider.tsx","../../src/lib/notification-unread-provider.tsx","../../src/components/notification-drawer.tsx","../../src/lib/providers.tsx"],"sourcesContent":["'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\n\ninterface NotificationDrawerContextValue {\n readonly close: () => void;\n readonly isOpen: boolean;\n readonly open: () => void;\n readonly toggle: () => void;\n}\n\nconst NotificationDrawerContext =\n createContext<NotificationDrawerContextValue | null>(null);\n\ninterface NotificationDrawerProviderProps {\n readonly children: ReactNode;\n}\n\n/**\n * Controls the open/closed state of the BPM notification drawer. Wraps\n * children with a context that `<NotificationDrawer />` reads to mount /\n * hide itself, and that the host navigation reads (via\n * `<BPMNotificationBellButton />` or `useNotificationDrawer().open`) to\n * open the drawer when the bell icon is clicked.\n *\n * When used outside this provider, the returned hook is a safe no-op so\n * components don't crash in test or storybook environments.\n */\nexport function NotificationDrawerProvider({\n children,\n}: NotificationDrawerProviderProps): ReactElement {\n const [isOpen, setIsOpen] = useState(false);\n\n const open = useCallback((): void => {\n setIsOpen(true);\n }, []);\n const close = useCallback((): void => {\n setIsOpen(false);\n }, []);\n const toggle = useCallback((): void => {\n setIsOpen((current) => !current);\n }, []);\n\n const value = useMemo<NotificationDrawerContextValue>(\n () => ({ close, isOpen, open, toggle }),\n [close, isOpen, open, toggle],\n );\n\n return (\n <NotificationDrawerContext.Provider value={value}>\n {children}\n </NotificationDrawerContext.Provider>\n );\n}\n\n/**\n * Read the BPM notification drawer's open state and control helpers.\n * Returns a no-op stub when used outside `<NotificationDrawerProvider>`.\n */\nexport function useNotificationDrawer(): NotificationDrawerContextValue {\n const context = useContext(NotificationDrawerContext);\n if (!context) {\n return {\n close: (): void => undefined,\n isOpen: false,\n open: (): void => undefined,\n toggle: (): void => undefined,\n };\n }\n return context;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { readUnreadNotificationCount } from '@rytass/bpm-core-client/workflow';\nimport { useAuth } from './auth-provider';\n\ninterface NotificationUnreadContextValue {\n readonly refreshUnreadCount: () => Promise<number>;\n readonly unreadCount: number;\n}\n\nconst NotificationUnreadContext =\n createContext<NotificationUnreadContextValue | null>(null);\n\ninterface NotificationUnreadProviderProps {\n readonly children: ReactNode;\n}\n\n/**\n * Polls BPM for the current member's unread notification count via\n * `readUnreadNotificationCount` and exposes it through context for the\n * host navigation (`<BPMNotificationBellButton />` badge or any custom\n * trigger using `useNotificationUnread().unreadCount`) and the BPM\n * `<NotificationDrawer />` (header count). Refresh is triggered on\n * mount and whenever the auth member id changes; consumers can call\n * `refreshUnreadCount()` after acknowledging a notification.\n */\nexport function NotificationUnreadProvider({\n children,\n}: NotificationUnreadProviderProps): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [unreadCount, setUnreadCount] = useState(0);\n\n const refreshUnreadCount = useCallback(async (): Promise<number> => {\n if (!currentMemberId) {\n setUnreadCount(0);\n return 0;\n }\n const next = await readUnreadNotificationCount(currentMemberId);\n setUnreadCount(next);\n return next;\n }, [currentMemberId]);\n\n useEffect((): (() => void) => {\n let active = true;\n void (async () => {\n try {\n const next = await refreshUnreadCount();\n if (active) setUnreadCount(next);\n } catch {\n if (active) setUnreadCount(0);\n }\n })();\n return (): void => {\n active = false;\n };\n }, [refreshUnreadCount]);\n\n const value = useMemo<NotificationUnreadContextValue>(\n () => ({ refreshUnreadCount, unreadCount }),\n [refreshUnreadCount, unreadCount],\n );\n\n return (\n <NotificationUnreadContext.Provider value={value}>\n {children}\n </NotificationUnreadContext.Provider>\n );\n}\n\n/**\n * Read the current unread-notification count and a manual refresh helper.\n * Returns a zero/no-op stub when used outside\n * `<NotificationUnreadProvider>`.\n */\nexport function useNotificationUnread(): NotificationUnreadContextValue {\n const context = useContext(NotificationUnreadContext);\n if (!context) {\n return {\n refreshUnreadCount: async (): Promise<number> => 0,\n unreadCount: 0,\n };\n }\n return context;\n}\n","'use client';\n\nimport {\n Fragment,\n useCallback,\n useEffect,\n useMemo,\n useState,\n type ChangeEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactElement,\n} from 'react';\nimport Drawer from '@mezzanine-ui/react/Drawer';\nimport Modal from '@mezzanine-ui/react/Modal';\nimport NotificationCenter from '@mezzanine-ui/react/NotificationCenter';\nimport Textarea from '@mezzanine-ui/react/Textarea';\nimport { Typography } from '@mezzanine-ui/react';\nimport type { NotificationSeverity } from '@mezzanine-ui/core/notification-center';\nimport type { DropdownOption } from '@mezzanine-ui/core/dropdown/dropdown';\nimport {\n archiveNotifications,\n decideTask,\n listNotifications,\n markAllNotificationsRead,\n markNotificationRead,\n unarchiveNotifications,\n type NotificationRecord,\n type NotificationResolution,\n type NotificationType,\n} from '@rytass/bpm-core-client/workflow';\nimport { useAuth } from '../lib/auth-provider';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { BPMFormField } from './bpm-form-field';\n\ntype FilterValue = 'all' | 'read' | 'unread';\n\ntype TimeGroup = 'today' | 'yesterday' | 'past7Days' | 'earlier';\n\nconst TIME_GROUP_ORDER: readonly TimeGroup[] = [\n 'today',\n 'yesterday',\n 'past7Days',\n 'earlier',\n];\n\nconst TIME_GROUP_LABEL: Readonly<Record<TimeGroup, string>> = {\n earlier: '更早',\n past7Days: '過去七天',\n today: '今天',\n yesterday: '昨天',\n};\n\nconst PAGE_SIZE = 50;\n\n/** Identifiers for the per-notification `...` dropdown menu entries. */\ntype NotificationAction =\n | 'approve'\n | 'archive'\n | 'reject'\n | 'unarchive'\n | 'view'\n | 'read';\n\n/** Identifier for the filter-bar dropdown entry toggling the archived view. */\nconst TOGGLE_ARCHIVED_OPTION_ID = 'toggleArchived';\n\n/**\n * Build the `...` dropdown options for one notification. Approve / reject are\n * offered only while the server still reports the notification as `actionable`\n * (an unresolved task assignment) — once the task is decided / cancelled the\n * backend flips `actionable` to false, so a stale \"同意\" can never appear.\n * A task that fills in form fields (`hasFillableFields`) cannot be approved\n * from here — the fill has to be entered on the case page — so its \"同意\"\n * becomes \"開啟案件\".\n * \"查看案件\" needs an `instanceId`; \"標為已讀\" only shows while unread.\n * Archiving is independent of read state, so the entry flips between\n * \"封存\" / \"取消封存\" for every notification.\n */\nfunction buildNotificationOptions(\n record: NotificationRecord,\n): readonly DropdownOption[] {\n const canReject = readNotificationAllowReject(record) !== false;\n const opensToFill =\n record.actionable &&\n Boolean(record.instanceId) &&\n readNotificationHasFillableFields(record);\n\n return [\n ...(record.actionable\n ? ([\n opensToFill\n ? { id: 'view', name: '開啟案件' }\n : { id: 'approve', name: '同意' },\n ...(canReject ? [{ id: 'reject', name: '拒絕' }] : []),\n ] satisfies DropdownOption[])\n : []),\n ...(record.instanceId && !opensToFill\n ? ([{ id: 'view', name: '查看案件' }] satisfies DropdownOption[])\n : []),\n ...(record.status !== 'READ'\n ? ([{ id: 'read', name: '標為已讀' }] satisfies DropdownOption[])\n : []),\n record.archivedAt\n ? { id: 'unarchive', name: '取消封存' }\n : { id: 'archive', name: '封存' },\n ];\n}\n\n/**\n * Title shown for a resolved task-assignment notification, replacing the\n * stored \"新的待簽任務\" wording so a decided card no longer reads as pending.\n * The stored `body` is kept as historical context (which case / node).\n */\nconst RESOLVED_TITLE: Readonly<Record<NotificationResolution, string>> = {\n APPROVED: '簽核任務已同意',\n REJECTED: '簽核任務已拒絕',\n RETURNED: '簽核任務已退回',\n SUPERSEDED: '簽核任務已結束',\n TRANSFERRED: '簽核任務已轉派',\n};\n\nfunction resolveDisplayTitle(record: NotificationRecord): string {\n return record.resolution ? RESOLVED_TITLE[record.resolution] : record.title;\n}\n\n/**\n * Severity (icon colour) for a notification. Once resolved, it reflects the\n * outcome — green for approved, red for rejected, neutral otherwise — instead\n * of the original by-type colour.\n */\nfunction resolveSeverity(record: NotificationRecord): NotificationSeverity {\n if (record.resolution === 'APPROVED') return 'success';\n if (record.resolution === 'REJECTED') return 'error';\n if (record.resolution) return 'info';\n return toSeverity(record.type);\n}\n\n/**\n * Right-side notification drawer mounted at the root by `<Providers>`.\n * Opens / closes via `useNotificationDrawer()`, polls\n * `listNotifications()` for the current member, supports filter\n * (`all` / `read` / `unread`), per-row mark-read, per-row archive / unarchive,\n * bulk mark-all-read, and load-more pagination. Archived notifications are\n * hidden until the filter-bar menu turns \"顯示已封存\" on. Clicking a row with an\n * `instanceId` navigates to `/instances/<id>` via the host's router adapter.\n */\nexport function NotificationDrawer(): ReactElement | null {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const { member } = useAuth();\n const { close, isOpen } = useNotificationDrawer();\n const { refreshUnreadCount } = useNotificationUnread();\n const currentMemberId = member?.memberId ?? null;\n const [rows, setRows] = useState<readonly NotificationRecord[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [page, setPage] = useState(1);\n const [loading, setLoading] = useState(false);\n const [bulkLoading, setBulkLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const [filter, setFilter] = useState<FilterValue>('all');\n const [includeArchived, setIncludeArchived] = useState(false);\n const [approveTarget, setApproveTarget] = useState<NotificationRecord | null>(\n null,\n );\n const [approveComment, setApproveComment] = useState('');\n const [rejectTarget, setRejectTarget] = useState<NotificationRecord | null>(\n null,\n );\n const [rejectReason, setRejectReason] = useState('');\n const [deciding, setDeciding] = useState(false);\n\n const trimmedApproveComment = approveComment.trim();\n const trimmedRejectReason = rejectReason.trim();\n\n const loadPage = useCallback(\n async (nextPage: number, append: boolean): Promise<boolean> => {\n if (!currentMemberId) return false;\n setLoading(true);\n setError(null);\n try {\n const result = await listNotifications({\n includeArchived,\n includeRead: true,\n page: nextPage,\n pageSize: PAGE_SIZE,\n recipientMemberId: currentMemberId,\n });\n setRows((current): readonly NotificationRecord[] =>\n append ? [...current, ...result.notifications] : result.notifications,\n );\n setTotalCount(result.totalCount);\n setPage(nextPage);\n await refreshUnreadCount();\n return true;\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n return false;\n } finally {\n setLoading(false);\n }\n },\n [currentMemberId, includeArchived, refreshUnreadCount],\n );\n\n useEffect((): void => {\n if (!isOpen || !currentMemberId) return;\n setError(null);\n setNotice(null);\n void loadPage(1, false);\n }, [isOpen, currentMemberId, loadPage]);\n\n const handleFilterChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>): void => {\n const next = event.target.value;\n if (next === 'all' || next === 'read' || next === 'unread') setFilter(next);\n },\n [],\n );\n\n const handleMarkAllRead = useCallback(async (): Promise<void> => {\n if (!currentMemberId || bulkLoading) return;\n setBulkLoading(true);\n setError(null);\n try {\n await markAllNotificationsRead({ recipientMemberId: currentMemberId });\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setBulkLoading(false);\n }\n }, [bulkLoading, currentMemberId, loadPage]);\n\n const handleLoadMore = useCallback((): void => {\n if (loading) return;\n void loadPage(page + 1, true);\n }, [loading, loadPage, page]);\n\n const handleMarkRead = useCallback(\n async (id: string): Promise<void> => {\n if (!currentMemberId) return;\n try {\n await markNotificationRead({ id, readerMemberId: currentMemberId });\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [currentMemberId, loadPage],\n );\n\n const handleArchive = useCallback(\n async (record: NotificationRecord): Promise<void> => {\n if (!currentMemberId) return;\n setError(null);\n setNotice(null);\n try {\n // Name the notification the way the row does — `title` is the raw\n // stored title, which can differ from the rendered one.\n const label = resolveDisplayTitle(record);\n\n if (record.archivedAt) {\n await unarchiveNotifications({ ids: [record.id] });\n setNotice(`已將「${label}」移出封存。`);\n } else {\n await archiveNotifications({ ids: [record.id] });\n setNotice(`已封存「${label}」。`);\n }\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [currentMemberId, loadPage],\n );\n\n const handleFilterAreaSelect = useCallback((option: DropdownOption): void => {\n if (option.id !== TOGGLE_ARCHIVED_OPTION_ID) return;\n setNotice(null);\n setIncludeArchived((current): boolean => !current);\n }, []);\n\n const handleOpenInstance = useCallback(\n async (record: NotificationRecord): Promise<void> => {\n if (!record.instanceId || !currentMemberId) return;\n try {\n if (record.status !== 'READ') {\n await markNotificationRead({\n id: record.id,\n readerMemberId: currentMemberId,\n });\n await refreshUnreadCount();\n }\n close();\n router.push(routes.caseDetail(record.instanceId));\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n }\n },\n [close, currentMemberId, refreshUnreadCount, router, routes],\n );\n\n const openApproveModal = useCallback((record: NotificationRecord): void => {\n setError(null);\n setApproveTarget(record);\n setApproveComment('');\n }, []);\n\n const closeApproveModal = useCallback((): void => {\n if (deciding) return;\n setApproveTarget(null);\n setApproveComment('');\n }, [deciding]);\n\n const handleApproveConfirm = useCallback(async (): Promise<void> => {\n const target = approveTarget;\n if (!target?.taskId || !currentMemberId || deciding) return;\n setDeciding(true);\n setError(null);\n setNotice(null);\n try {\n await decideTask({\n action: 'APPROVED',\n comment: trimmedApproveComment || null,\n decidedByMemberId: currentMemberId,\n taskId: target.taskId,\n });\n const refreshed = await loadPage(1, false);\n if (!refreshed) return;\n setApproveTarget(null);\n setApproveComment('');\n setNotice(`已同意「${target.title}」。`);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setDeciding(false);\n }\n }, [\n approveTarget,\n currentMemberId,\n deciding,\n loadPage,\n trimmedApproveComment,\n ]);\n\n const openRejectModal = useCallback((record: NotificationRecord): void => {\n setRejectTarget(record);\n setRejectReason('');\n }, []);\n\n const closeRejectModal = useCallback((): void => {\n setRejectTarget(null);\n setRejectReason('');\n }, []);\n\n const handleRejectConfirm = useCallback(async (): Promise<void> => {\n const target = rejectTarget;\n if (!target?.taskId || !currentMemberId || !trimmedRejectReason || deciding)\n return;\n setDeciding(true);\n setError(null);\n setNotice(null);\n try {\n await decideTask({\n action: 'REJECTED',\n comment: trimmedRejectReason,\n decidedByMemberId: currentMemberId,\n taskId: target.taskId,\n });\n setRejectTarget(null);\n setRejectReason('');\n setNotice(`已拒絕「${target.title}」。`);\n await loadPage(1, false);\n } catch (e: unknown) {\n setError(readErrorMessage(e));\n } finally {\n setDeciding(false);\n }\n }, [currentMemberId, deciding, loadPage, rejectTarget, trimmedRejectReason]);\n\n const handleBadgeSelect = useCallback(\n (record: NotificationRecord, option: DropdownOption): void => {\n const action = option.id as NotificationAction;\n if (action === 'approve') openApproveModal(record);\n else if (action === 'reject') openRejectModal(record);\n else if (action === 'view') void handleOpenInstance(record);\n else if (action === 'read') void handleMarkRead(record.id);\n else if (action === 'archive' || action === 'unarchive')\n void handleArchive(record);\n },\n [\n handleArchive,\n handleMarkRead,\n handleOpenInstance,\n openApproveModal,\n openRejectModal,\n ],\n );\n\n /**\n * Whether an event that reached the card wrapper actually happened inside the\n * card, and not on one of its action controls.\n *\n * Two escape hatches are needed. The `...` menu button (and its `<svg>` icon —\n * hence the `Element` check, SVG nodes are not HTMLElements) sits inside the\n * card and must open the dropdown rather than navigate. The dropdown itself\n * renders into a portal, and React replays events along the component tree\n * rather than the DOM tree, so picking \"封存\" also reaches this handler even\n * though the `<li role=\"option\">` lives outside the card's DOM — and it is not\n * a `<button>`, so the first guard alone misses it.\n */\n const isCardActivation = useCallback(\n (target: EventTarget | null, card: HTMLDivElement): boolean => {\n if (!(target instanceof Node) || !card.contains(target)) return false;\n\n return !(target instanceof Element && target.closest('button'));\n },\n [],\n );\n\n const handleCardActivate = useCallback(\n (record: NotificationRecord, event: ReactMouseEvent<HTMLDivElement>): void => {\n if (!isCardActivation(event.target, event.currentTarget)) return;\n void handleOpenInstance(record);\n },\n [handleOpenInstance, isCardActivation],\n );\n\n const handleCardKeyDown = useCallback(\n (\n record: NotificationRecord,\n event: ReactKeyboardEvent<HTMLDivElement>,\n ): void => {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n if (!isCardActivation(event.target, event.currentTarget)) return;\n event.preventDefault();\n void handleOpenInstance(record);\n },\n [handleOpenInstance, isCardActivation],\n );\n\n const filteredRows = useMemo(\n (): readonly NotificationRecord[] =>\n rows.filter((row): boolean => {\n if (filter === 'all') return true;\n if (filter === 'read') return row.status === 'READ';\n return row.status !== 'READ';\n }),\n [filter, rows],\n );\n\n const groupedRows = useMemo(\n (): ReadonlyArray<readonly [TimeGroup, readonly NotificationRecord[]]> => {\n const now = new Date();\n const buckets = TIME_GROUP_ORDER.reduce<\n Record<TimeGroup, NotificationRecord[]>\n >(\n (accumulator, group) => {\n accumulator[group] = [];\n return accumulator;\n },\n { earlier: [], past7Days: [], today: [], yesterday: [] },\n );\n filteredRows.forEach((row): void => {\n buckets[resolveTimeGroup(row.createdAt, now)].push(row);\n });\n return TIME_GROUP_ORDER.map(\n (group) => [group, buckets[group]] as const,\n ).filter(([, items]) => items.length > 0);\n },\n [filteredRows],\n );\n\n const hasMore = rows.length < totalCount;\n\n if (!currentMemberId) return null;\n\n return (\n <>\n <Drawer\n bottomGhostActionDisabled={bulkLoading || loading}\n bottomGhostActionLoading={bulkLoading}\n bottomGhostActionText=\"全部標為已讀\"\n bottomOnGhostActionClick={(): void => {\n void handleMarkAllRead();\n }}\n bottomOnPrimaryActionClick={(): void => {\n handleLoadMore();\n }}\n bottomPrimaryActionDisabled={!hasMore || loading}\n bottomPrimaryActionLoading={loading && hasMore}\n bottomPrimaryActionText={hasMore ? '載入更多' : '已顯示全部'}\n contentKey={`${filter}:${includeArchived}:${rows.length}`}\n filterAreaAllRadioLabel=\"全部\"\n filterAreaOnRadioChange={handleFilterChange}\n filterAreaOnSelect={handleFilterAreaSelect}\n filterAreaOptions={[\n {\n id: TOGGLE_ARCHIVED_OPTION_ID,\n name: includeArchived ? '隱藏已封存' : '顯示已封存',\n },\n ]}\n filterAreaReadRadioLabel=\"已讀\"\n filterAreaShow\n filterAreaUnreadRadioLabel=\"未讀\"\n filterAreaValue={filter}\n headerTitle=\"通知中心\"\n isBottomDisplay\n isHeaderDisplay\n onClose={close}\n open={isOpen}\n size=\"medium\"\n >\n <div role=\"list\">\n {error ? (\n <p\n role=\"alert\"\n style={{\n color: 'var(--mzn-color-text-error, #d92d20)',\n padding: '12px 16px',\n }}\n >\n {error}\n </p>\n ) : null}\n {notice ? (\n <p\n role=\"status\"\n style={{\n color: 'var(--mzn-color-text-success, #079455)',\n padding: '12px 16px',\n }}\n >\n {notice}\n </p>\n ) : null}\n {groupedRows.length === 0 ? (\n <p\n style={{\n color: 'var(--mzn-color-text-secondary, #6b7280)',\n padding: '24px 16px',\n textAlign: 'center',\n }}\n >\n {loading ? '載入中…' : '目前沒有通知'}\n </p>\n ) : null}\n {groupedRows.map(([group, items]) => (\n <Fragment key={group}>\n {items.map((record, itemIndex) => {\n const openable = record.instanceId !== null;\n\n return (\n <div\n key={record.id}\n onClick={\n openable\n ? (event: ReactMouseEvent<HTMLDivElement>): void => {\n handleCardActivate(record, event);\n }\n : undefined\n }\n onKeyDown={\n openable\n ? (event: ReactKeyboardEvent<HTMLDivElement>): void => {\n handleCardKeyDown(record, event);\n }\n : undefined\n }\n role={openable ? 'button' : undefined}\n style={openable ? { cursor: 'pointer' } : undefined}\n tabIndex={openable ? 0 : undefined}\n >\n <NotificationCenter\n appendTips={record.archivedAt ? '已封存' : undefined}\n description={record.body}\n onBadgeSelect={(option: DropdownOption): void => {\n handleBadgeSelect(record, option);\n }}\n options={[...buildNotificationOptions(record)]}\n prependTips={\n itemIndex === 0 ? TIME_GROUP_LABEL[group] : undefined\n }\n reference={record.id}\n severity={resolveSeverity(record)}\n showBadge={record.status !== 'READ'}\n timeStamp={record.createdAt}\n title={resolveDisplayTitle(record)}\n type=\"drawer\"\n />\n </div>\n );\n })}\n </Fragment>\n ))}\n </div>\n </Drawer>\n <Modal\n cancelText=\"取消\"\n confirmText=\"送出同意\"\n loading={deciding}\n modalType=\"standard\"\n onCancel={closeApproveModal}\n onClose={closeApproveModal}\n onConfirm={(): void => {\n void handleApproveConfirm();\n }}\n open={approveTarget !== null}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"可以留下同意說明,內容會記錄在簽核歷程;不填也可以直接送出。\"\n title=\"簽核意見\"\n >\n {error ? (\n <Typography color=\"text-error\" role=\"alert\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <BPMFormField label=\"簽核意見\" name=\"notificationApproveComment\">\n <Textarea\n autoFocus\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setApproveComment(event.target.value);\n }}\n placeholder=\"選填,例如補充核准條件或提醒事項\"\n rows={4}\n value={approveComment}\n />\n </BPMFormField>\n </Modal>\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{\n disabled: !trimmedRejectReason,\n variant: 'destructive-primary',\n }}\n confirmText=\"送出拒絕\"\n loading={deciding}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={closeRejectModal}\n onClose={closeRejectModal}\n onConfirm={(): void => {\n void handleRejectConfirm();\n }}\n open={rejectTarget !== null}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"拒絕案件時必須留下原因,供發起人與後續追蹤查看。\"\n title=\"拒絕原因\"\n >\n <Textarea\n autoFocus\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setRejectReason(event.target.value);\n }}\n placeholder=\"請輸入拒絕原因\"\n rows={4}\n value={rejectReason}\n />\n </Modal>\n </>\n );\n}\n\nfunction toSeverity(type: NotificationType): NotificationSeverity {\n if (type === 'SLA_OVERDUE') return 'error';\n if (type === 'SLA_WARNING') return 'warning';\n if (type === 'INSTANCE_COMPLETED') return 'success';\n return 'info';\n}\n\nfunction resolveTimeGroup(value: string, now: Date): TimeGroup {\n const notificationDate = new Date(value);\n if (Number.isNaN(notificationDate.getTime())) return 'earlier';\n const nowStartOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n const notificationStartOfDay = new Date(\n notificationDate.getFullYear(),\n notificationDate.getMonth(),\n notificationDate.getDate(),\n );\n if (notificationStartOfDay.getTime() === nowStartOfDay.getTime()) return 'today';\n const yesterdayStartOfDay = new Date(nowStartOfDay);\n yesterdayStartOfDay.setDate(yesterdayStartOfDay.getDate() - 1);\n if (notificationStartOfDay.getTime() === yesterdayStartOfDay.getTime())\n return 'yesterday';\n const diffInDays =\n (now.getTime() - notificationDate.getTime()) / (1000 * 60 * 60 * 24);\n if (diffInDays <= 7) return 'past7Days';\n return 'earlier';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n\nfunction readNotificationAllowReject(\n record: NotificationRecord,\n): boolean | null {\n try {\n const payload: unknown = JSON.parse(record.payloadJson);\n\n if (isRecord(payload) && typeof payload.allowReject === 'boolean') {\n return payload.allowReject;\n }\n\n return record.allowReject;\n } catch {\n return record.allowReject;\n }\n}\n\n/**\n * `TASK_ASSIGNED` / `TASK_TRANSFERRED` payloads carry `hasFillableFields`;\n * older notifications do not, which reads as `false`.\n */\nfunction readNotificationHasFillableFields(\n record: NotificationRecord,\n): boolean {\n try {\n const payload: unknown = JSON.parse(record.payloadJson);\n\n return isRecord(payload) && payload.hasFillableFields === true;\n } catch {\n return false;\n }\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","'use client';\n\nimport type { ReactElement, ReactNode } from 'react';\nimport {\n CalendarConfigProviderMoment,\n CalendarLocale,\n} from '@mezzanine-ui/react/moment';\nimport { AuthProvider } from './auth-provider';\nimport {\n BPMLanguageProvider,\n DEFAULT_BPM_LANGUAGE,\n type BPMLanguage,\n} from './language-provider';\nimport { NotificationDrawer } from '../components/notification-drawer';\nimport { NotificationDrawerProvider } from './notification-drawer-provider';\nimport { NotificationUnreadProvider } from './notification-unread-provider';\n\ninterface ProvidersProps {\n readonly children: ReactNode;\n /** Override Mezzanine calendar locale. Defaults to `CalendarLocale.ZH_TW`. */\n readonly locale?: CalendarLocale;\n /**\n * Language BPM views write their messages in, such as publish-lint errors.\n * Defaults to `'zh-TW'`. Pass the user's choice as state to let them switch.\n * Independent of `locale`, which only formats calendar dates.\n */\n readonly language?: BPMLanguage;\n /**\n * Public paths that should not trigger redirect to `/login` when there\n * is no session. Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n */\n readonly publicPaths?: readonly string[];\n /** Where to send unauthenticated users. Defaults to `'/login'`. */\n readonly loginPath?: string;\n}\n\n/**\n * One-stop BPM admin provider stack. Wires:\n *\n * - Mezzanine UI calendar locale (moment-based, `ZH_TW` by default)\n * - `<BPMLanguageProvider>` (message language, `'zh-TW'` by default)\n * - `<AuthProvider>` (BPM session via REST `/auth/*`)\n * - `<NotificationUnreadProvider>` (polls unread count)\n * - `<NotificationDrawerProvider>` (controls drawer open/close state)\n * - `<NotificationDrawer />` mounted at the root so the host navigation\n * bell (`<BPMNotificationBellButton />` or any custom trigger calling\n * `useNotificationDrawer().open`) can open it.\n *\n * Consumer hosts wrap this **inside** a `<RouterAdapterProvider>` (provided\n * by the `pages/*` subpath shims when consuming via Next.js, or wired by\n * hand for other frameworks).\n */\nexport function Providers({\n children,\n locale = CalendarLocale.ZH_TW,\n language = DEFAULT_BPM_LANGUAGE,\n publicPaths,\n loginPath,\n}: ProvidersProps): ReactElement {\n return (\n <CalendarConfigProviderMoment locale={locale}>\n <BPMLanguageProvider language={language}>\n <AuthProvider publicPaths={publicPaths} loginPath={loginPath}>\n <NotificationUnreadProvider>\n <NotificationDrawerProvider>\n {children}\n <NotificationDrawer />\n </NotificationDrawerProvider>\n </NotificationUnreadProvider>\n </AuthProvider>\n </BPMLanguageProvider>\n </CalendarConfigProviderMoment>\n );\n}\n"],"mappings":"A,0qBAmBA,IAAMoD,GAAA,EACJpD,EAAAA,cAAAA,CAAqD,IAAI,EAgB3D,SAAgBqD,EAA2B,CACzC,YACgD,CAChD,GAAM,CAACC,EAAQC,IAAA,EAAatD,EAAAA,SAAAA,CAAS,EAAK,EAEpCuD,GAAA,EAAOtD,EAAAA,YAAAA,KAAwB,CACnCqD,EAAU,EAAI,CAChB,EAAG,CAAC,CAAC,EACCE,GAAA,EAAQvD,EAAAA,YAAAA,KAAwB,CACpCqD,EAAU,EAAK,CACjB,EAAG,CAAC,CAAC,EACCG,GAAA,EAASxD,EAAAA,YAAAA,KAAwB,CACrCqD,EAAW,GAAY,CAACI,CAAO,CACjC,EAAG,CAAC,CAAC,EAECC,GAAA,EAAQzD,EAAAA,QAAAA,MACL,CAAE,QAAO,SAAQ,OAAM,QAAO,GACrC,CAACsD,EAAOH,EAAQE,EAAME,CAAM,CAC9B,EAEA,OACE,EAAAb,EAAA,IAAA,CAACO,EAA0B,SAA3B,CAA2C,QACxC,UACiC,CAAA,CAExC,CAMA,SAAgBS,GAAwD,CAUtE,OATM,EAAUzD,EAAAA,WAAAA,CAAWgD,CACtB,GACI,CACL,UAAmB,IAAA,GACnB,OAAQ,GACR,SAAkB,IAAA,GAClB,WAAoB,IAAA,EACtB,CAGJ,CC1DA,IAAMU,GAAA,EACJzD,EAAAA,cAAAA,CAAqD,IAAI,EAe3D,SAAgB0D,EAA2B,CACzC,YACgD,CAChD,GAAM,CAAE,UAAWzD,EAAAA,EAAQ,EACrB0D,EAAkBC,GAAQ,UAAY,KACtC,CAACC,EAAaC,IAAA,EAAkB5D,EAAAA,SAAAA,CAAS,CAAC,EAE1C6D,GAAA,EAAqB5D,EAAAA,YAAAA,CAAY,SAA6B,CAClE,GAAI,CAACwD,EAEH,OADAG,EAAe,CAAC,EACT,EAET,IAAME,EAAO,MAAA,EAAM5D,EAAAA,4BAAAA,CAA4BuD,CAAe,EAE9D,OADAG,EAAeE,CAAI,EACZA,CACT,EAAG,CAACL,CAAe,CAAC,GAEpB,EAAArB,EAAA,UAAA,KAA8B,CAC5B,IAAI2B,EAAS,GASb,OARM,SAAY,CAChB,GAAI,CACF,IAAMD,EAAO,MAAMD,EAAmB,EAClCE,GAAQH,EAAeE,CAAI,CACjC,MAAQ,CACFC,GAAQH,EAAe,CAAC,CAC9B,CACF,EAAA,CAAG,MACgB,CACjB,EAAS,EACX,CACF,EAAG,CAACC,CAAkB,CAAC,EAEvB,IAAMR,GAAA,EAAQlD,EAAAA,QAAAA,MACL,CAAE,qBAAoB,aAAY,GACzC,CAAC0D,EAAoBF,CAAW,CAClC,EAEA,OACE,EAAArB,EAAA,IAAA,CAACiB,EAA0B,SAA3B,CAA2C,QACxC,UACiC,CAAA,CAExC,CAOA,SAAgBS,IAAwD,CAQtE,OAPM,EAAU5D,EAAAA,WAAAA,CAAWmD,CACtB,GACI,CACL,mBAAoB,SAA6B,EACjD,YAAa,CACf,CAGJ,CCpDA,IAAMU,EAAyC,CAC7C,QACA,YACA,YACA,SACF,EAEMC,GAAwD,CAC5D,QAAS,KACT,UAAW,OACX,MAAO,KACP,UAAW,IACb,EAEMC,GAAY,GAYZC,EAA4B,iBAclC,SAASC,GACP,EAC2B,CAC3B,IAAMC,EAAYC,EAA4BC,CAAM,IAAM,GACpDC,EACJD,EAAO,YACP,EAAQA,EAAO,YACfE,EAAkCF,CAAM,EAE1C,MAAO,CACL,GAAIA,EAAO,WACN,CACCC,EACI,CAAE,GAAI,OAAQ,KAAM,MAAO,EAC3B,CAAE,GAAI,UAAW,KAAM,IAAK,EAChC,GAAIH,EAAY,CAAC,CAAE,GAAI,SAAU,KAAM,IAAK,CAAC,EAAI,CAAC,CACpD,EACA,CAAC,EACL,GAAIE,EAAO,YAAc,CAACC,EACrB,CAAC,CAAE,GAAI,OAAQ,KAAM,MAAO,CAAC,EAC9B,CAAC,EACL,GAAID,EAAO,SAAW,OAElB,CAAC,EADA,CAAC,CAAE,GAAI,OAAQ,KAAM,MAAO,CAAC,EAElCA,EAAO,WACH,CAAE,GAAI,YAAa,KAAM,MAAO,EAChC,CAAE,GAAI,UAAW,KAAM,IAAK,CAClC,CACF,CAOA,IAAMG,EAAmE,CACvE,SAAU,UACV,SAAU,UACV,SAAU,UACV,WAAY,UACZ,YAAa,SACf,EAEA,SAASC,GAAoB,EAAoC,CAC/D,OAAOJ,EAAO,WAAaG,EAAeH,EAAO,YAAcA,EAAO,KACxE,CAOA,SAASK,GAAgB,EAAkD,CAIzE,OAHIL,EAAO,aAAe,WAAmB,UACzCA,EAAO,aAAe,WAAmB,QACzCA,EAAO,WAAmB,OACvBM,EAAWN,EAAO,IAAI,CAC/B,CAWA,SAAgBO,GAA0C,CACxD,IAAMC,EAAS3E,EAAAA,EAAiB,EAC1B4E,EAAS3E,EAAAA,EAAa,EACtB,CAAE,UAAWC,EAAAA,EAAQ,EACrB,CAAE,QAAO,UAAW+C,EAAsB,EAC1C,CAAE,sBAAuBU,GAAsB,EAC/CP,EAAkBC,GAAQ,UAAY,KACtC,CAACwB,EAAMC,IAAA,EAAW3E,EAAAA,SAAAA,CAAwC,CAAC,CAAC,EAC5D,CAAC4E,EAAYC,IAAA,EAAiB7E,EAAAA,SAAAA,CAAS,CAAC,EACxC,CAAC8E,EAAMC,IAAA,EAAW/E,EAAAA,SAAAA,CAAS,CAAC,EAC5B,CAACgF,EAASC,IAAA,EAAcjF,EAAAA,SAAAA,CAAS,EAAK,EACtC,CAACkF,EAAaC,IAAA,EAAkBnF,EAAAA,SAAAA,CAAS,EAAK,EAC9C,CAACoF,EAAOC,IAAA,EAAYrF,EAAAA,SAAAA,CAAwB,IAAI,EAChD,CAACsF,EAAQC,IAAA,EAAavF,EAAAA,SAAAA,CAAwB,IAAI,EAClD,CAACwF,EAAQC,KAAA,EAAazF,EAAAA,SAAAA,CAAsB,KAAK,EACjD,CAAC0F,EAAiBC,KAAA,EAAsB3F,EAAAA,SAAAA,CAAS,EAAK,EACtD,CAAC4F,EAAeC,IAAA,EAAoB7F,EAAAA,SAAAA,CACxC,IACF,EACM,CAAC8F,EAAgBC,IAAA,EAAqB/F,EAAAA,SAAAA,CAAS,EAAE,EACjD,CAACgG,EAAcC,IAAA,EAAmBjG,EAAAA,SAAAA,CACtC,IACF,EACM,CAACkG,EAAcC,IAAA,EAAmBnG,EAAAA,SAAAA,CAAS,EAAE,EAC7C,CAACoG,EAAUC,IAAA,EAAerG,EAAAA,SAAAA,CAAS,EAAK,EAExCsG,GAAwBR,EAAe,KAAK,EAC5CS,EAAsBL,EAAa,KAAK,EAExCM,GAAA,EAAWvG,EAAAA,YAAAA,CACf,MAAO,EAAkB,IAAsC,CAC7D,GAAI,CAACgD,EAAiB,MAAO,GAC7BgC,EAAW,EAAI,EACfI,EAAS,IAAI,EACb,GAAI,CACF,IAAMoB,EAAS,MAAA,EAAMvG,EAAAA,kBAAAA,CAAkB,CACrC,kBACA,YAAa,GACb,KAAMwG,EACN,SAAU/C,GACV,kBAAmBV,CACrB,CAAC,EAOD,OANA0B,EAAS,GACPgC,EAAS,CAAC,GAAG/D,EAAS,GAAG6D,EAAO,aAAa,EAAIA,EAAO,aAC1D,EACA5B,EAAc4B,EAAO,UAAU,EAC/B1B,EAAQ2B,CAAQ,EAChB,MAAMrD,EAAmB,EAClB,EACT,OAAS,EAAY,CAEnB,OADAgC,EAASuB,EAAiB,CAAC,CAAC,EACrB,EACT,QAAU,CACR3B,EAAW,EAAK,CAClB,CACF,EACA,CAAChC,EAAiByC,EAAiBrC,CAAkB,CACvD,GAEA,EAAAzB,EAAA,UAAA,KAAsB,CACfW,GAAWU,IAChBoC,EAAS,IAAI,EACbE,EAAU,IAAI,EACdiB,EAAc,EAAG,EAAK,EACxB,EAAG,CAACjE,EAAQU,EAAiBuD,CAAQ,CAAC,EAEtC,IAAMK,IAAA,EAAqB5G,EAAAA,YAAAA,CACxB,GAA+C,CAC9C,IAAMqD,EAAOwD,EAAM,OAAO,OACtBxD,IAAS,OAASA,IAAS,QAAUA,IAAS,WAAUmC,GAAUnC,CAAI,CAC5E,EACA,CAAC,CACH,EAEMyD,IAAA,EAAoB9G,EAAAA,YAAAA,CAAY,SAA2B,CAC/D,GAAKgD,GAAmB,GACxB,GAAe,EAAI,EACnBoC,EAAS,IAAI,EACb,GAAI,CACF,MAAA,EAAMlF,EAAAA,yBAAAA,CAAyB,CAAE,kBAAmB8C,CAAgB,CAAC,EACrE,MAAMuD,EAAS,EAAG,EAAK,CACzB,OAAS,EAAY,CACnBnB,EAASuB,EAAiB,CAAC,CAAC,CAC9B,QAAU,CACRzB,EAAe,EAAK,CACtB,CATmB,CAUrB,EAAG,CAACD,EAAajC,EAAiBuD,CAAQ,CAAC,EAErCQ,IAAA,EAAiB/G,EAAAA,YAAAA,KAAwB,CACzC+E,GACJwB,EAAc1B,EAAO,EAAG,EAAI,CAC9B,EAAG,CAACE,EAASwB,EAAU1B,CAAI,CAAC,EAEtBmC,IAAA,EAAiBhH,EAAAA,YAAAA,CACrB,KAAO,IAA8B,CACnC,GAAKgD,EACL,GAAI,CACF,MAAA,EAAM7C,EAAAA,qBAAAA,CAAqB,CAAE,KAAI,eAAgB6C,CAAgB,CAAC,EAClE,MAAMuD,EAAS,EAAG,EAAK,CACzB,OAAS,EAAY,CACnBnB,EAASuB,EAAiB,CAAC,CAAC,CAC9B,CACF,EACA,CAAC3D,EAAiBuD,CAAQ,CAC5B,EAEMU,IAAA,EAAgBjH,EAAAA,YAAAA,CACpB,KAAO,IAA8C,CACnD,GAAKgD,EACL,GAAS,IAAI,EACbsC,EAAU,IAAI,EACd,GAAI,CAGF,IAAM4B,EAAQ/C,GAAoBJ,CAAM,EAEpCA,EAAO,YACT,MAAA,EAAM3D,EAAAA,uBAAAA,CAAuB,CAAE,IAAK,CAAC2D,EAAO,EAAE,CAAE,CAAC,EACjDuB,EAAU,MAAM4B,EAAM,OAAO,IAE7B,MAAA,EAAM7G,EAAAA,qBAAAA,CAAqB,CAAE,IAAK,CAAC0D,EAAO,EAAE,CAAE,CAAC,EAC/CuB,EAAU,OAAO4B,EAAM,GAAG,GAE5B,MAAMX,EAAS,EAAG,EAAK,CACzB,OAAS,EAAY,CACnBnB,EAASuB,EAAiB,CAAC,CAAC,CAC9B,CAjBa,CAkBf,EACA,CAAC3D,EAAiBuD,CAAQ,CAC5B,EAEMY,IAAA,EAAyBnH,EAAAA,YAAAA,CAAa,GAAiC,CACvEoH,EAAO,KAAOzD,IAClB2B,EAAU,IAAI,EACdI,GAAoB,GAAqB,CAAC/C,CAAO,EACnD,EAAG,CAAC,CAAC,EAEC0E,GAAA,EAAqBrH,EAAAA,YAAAA,CACzB,KAAO,IAA8C,CACnD,GAAK+D,EAAO,YAAef,EAC3B,GAAI,CACEe,EAAO,SAAW,SACpB,MAAA,EAAM5D,EAAAA,qBAAAA,CAAqB,CACzB,GAAI4D,EAAO,GACX,eAAgBf,CAClB,CAAC,EACD,MAAMI,EAAmB,GAE3BX,EAAM,EACN8B,EAAO,KAAKC,EAAO,WAAWT,EAAO,UAAU,CAAC,CAClD,OAAS,EAAY,CACnBqB,EAASuB,EAAiB,CAAC,CAAC,CAC9B,CACF,EACA,CAAClE,EAAOO,EAAiBI,EAAoBmB,EAAQC,CAAM,CAC7D,EAEM8C,IAAA,EAAmBtH,EAAAA,YAAAA,CAAa,GAAqC,CACzEoF,EAAS,IAAI,EACbQ,EAAiB7B,CAAM,EACvB+B,EAAkB,EAAE,CACtB,EAAG,CAAC,CAAC,EAECyB,IAAA,EAAoBvH,EAAAA,YAAAA,KAAwB,CAC5CmG,IACJP,EAAiB,IAAI,EACrBE,EAAkB,EAAE,EACtB,EAAG,CAACK,CAAQ,CAAC,EAEPqB,IAAA,EAAuBxH,EAAAA,YAAAA,CAAY,SAA2B,CAClE,IAAMyH,EAAS9B,EACf,GAAK8B,GAAQ,QAAWzE,GAAmB,GAC3C,GAAY,EAAI,EAChBoC,EAAS,IAAI,EACbE,EAAU,IAAI,EACd,GAAI,CAQF,GAPA,MAAA,EAAMhF,EAAAA,WAAAA,CAAW,CACf,OAAQ,WACR,QAAS+F,IAAyB,KAClC,kBAAmBrD,EACnB,OAAQyE,EAAO,MACjB,CAAC,EAEG,CAAC,MADmBlB,EAAS,EAAG,EAAK,EACzB,OAChBX,EAAiB,IAAI,EACrBE,EAAkB,EAAE,EACpBR,EAAU,OAAOmC,EAAO,MAAM,GAAG,CACnC,OAAS,EAAY,CACnBrC,EAASuB,EAAiB,CAAC,CAAC,CAC9B,QAAU,CACRP,EAAY,EAAK,CACnB,CAnBgB,CAoBlB,EAAG,CACDT,EACA3C,EACAmD,EACAI,EACAF,EACF,CAAC,EAEKqB,IAAA,EAAkB1H,EAAAA,YAAAA,CAAa,GAAqC,CACxEgG,EAAgBjC,CAAM,EACtBmC,EAAgB,EAAE,CACpB,EAAG,CAAC,CAAC,EAECyB,IAAA,EAAmB3H,EAAAA,YAAAA,KAAwB,CAC/CgG,EAAgB,IAAI,EACpBE,EAAgB,EAAE,CACpB,EAAG,CAAC,CAAC,EAEC0B,IAAA,EAAsB5H,EAAAA,YAAAA,CAAY,SAA2B,CACjE,IAAMyH,EAAS1B,EACf,GAAK0B,GAAQ,QAAWzE,GAAoBsD,GAAuB,GAEnE,GAAY,EAAI,EAChBlB,EAAS,IAAI,EACbE,EAAU,IAAI,EACd,GAAI,CACF,MAAA,EAAMhF,EAAAA,WAAAA,CAAW,CACf,OAAQ,WACR,QAASgG,EACT,kBAAmBtD,EACnB,OAAQyE,EAAO,MACjB,CAAC,EACDzB,EAAgB,IAAI,EACpBE,EAAgB,EAAE,EAClBZ,EAAU,OAAOmC,EAAO,MAAM,GAAG,EACjC,MAAMlB,EAAS,EAAG,EAAK,CACzB,OAAS,EAAY,CACnBnB,EAASuB,EAAiB,CAAC,CAAC,CAC9B,QAAU,CACRP,EAAY,EAAK,CACnB,CAlBgB,CAmBlB,EAAG,CAACpD,EAAiBmD,EAAUI,EAAUR,EAAcO,CAAmB,CAAC,EAErEuB,IAAA,EAAoB7H,EAAAA,YAAAA,EACvB,EAA4B,IAAiC,CAC5D,IAAM8H,EAASV,EAAO,GAClBU,IAAW,UAAWR,GAAiBvD,CAAM,EACxC+D,IAAW,SAAUJ,GAAgB3D,CAAM,EAC3C+D,IAAW,OAAQT,EAAwBtD,CAAM,EACjD+D,IAAW,OAAQd,GAAoBjD,EAAO,EAAE,GAChD+D,IAAW,WAAaA,IAAW,cAC1Cb,GAAmBlD,CAAM,CAC7B,EACA,CACEkD,GACAD,GACAK,EACAC,GACAI,EACF,CACF,EAcMK,GAAA,EAAmB/H,EAAAA,YAAAA,EACtB,EAA4B,IACvB,EAAEyH,aAAkB,OAAS,CAACO,EAAK,SAASP,CAAM,EAAU,GAEzD,EAAEA,aAAkB,SAAWA,EAAO,QAAQ,QAAQ,GAE/D,CAAC,CACH,EAEMQ,IAAA,EAAqBjI,EAAAA,YAAAA,EACxB,EAA4B,IAAiD,CACvE+H,EAAiBlB,EAAM,OAAQA,EAAM,aAAa,GACvDQ,EAAwBtD,CAAM,CAChC,EACA,CAACsD,EAAoBU,CAAgB,CACvC,EAEMG,IAAA,EAAoBlI,EAAAA,YAAAA,EAEtB,EACA,IACS,EACL6G,EAAM,MAAQ,SAAWA,EAAM,MAAQ,MACtCkB,EAAiBlB,EAAM,OAAQA,EAAM,aAAa,IACvDA,EAAM,eAAe,EACrBQ,EAAwBtD,CAAM,EAChC,EACA,CAACsD,EAAoBU,CAAgB,CACvC,EAEMI,IAAA,EAAe5H,EAAAA,QAAAA,KAEjBkE,EAAK,OAAQ,GACPc,IAAW,MAAc,GACzBA,IAAW,OAAe6C,EAAI,SAAW,OACtCA,EAAI,SAAW,MACvB,EACH,CAAC7C,EAAQd,CAAI,CACf,EAEM4D,IAAA,EAAc9H,EAAAA,QAAAA,KACwD,CAC1E,IAAM+H,EAAM,IAAI,KACVC,EAAU/E,EAAiB,QAG9B,EAAa,KACZ,EAAYgF,GAAS,CAAC,EACfC,GAET,CAAE,QAAS,CAAC,EAAG,UAAW,CAAC,EAAG,MAAO,CAAC,EAAG,UAAW,CAAC,CAAE,CACzD,EAIA,OAHAN,GAAa,QAAS,GAAc,CAClCI,EAAQG,GAAiBN,EAAI,UAAWE,CAAG,EAAE,CAAC,KAAKF,CAAG,CACxD,CAAC,EACM5E,EAAiB,IACrB,GAAU,CAACgF,EAAOD,EAAQC,EAAM,CACnC,CAAC,CAAC,QAAQ,EAAGG,KAAWA,EAAM,OAAS,CAAC,CACxC,EACA,CAACR,EAAY,CACf,EAEMS,EAAUnE,EAAK,OAASE,EAI9B,OAFK3B,GAGH,EAAAnB,EAAA,KAAA,CAAArB,EAAAA,SAAA,CAAA,SAAA,EACE,EAAAqB,EAAA,IAAA,CAACpB,EAAAA,QAAD,CACE,0BAA2BwE,GAAeF,EAC1C,yBAA0BE,EAC1B,sBAAsB,SACtB,6BAAsC,CACpC6B,GAAuB,CACzB,EACA,+BAAwC,CACtCC,GAAe,CACjB,EACA,4BAA6B,CAAC6B,GAAW7D,EACzC,2BAA4BA,GAAW6D,EACvC,wBAAyBA,EAAU,OAAS,QAC5C,WAAY,GAAGrD,EAAO,GAAGE,EAAgB,GAAGhB,EAAK,SACjD,wBAAwB,KACxB,wBAAyBmC,GACzB,mBAAoBO,GACpB,kBAAmB,CACjB,CACE,GAAIxD,EACJ,KAAM8B,EAAkB,QAAU,OACpC,CACF,EACA,yBAAyB,KACzB,eAAA,GACA,2BAA2B,KAC3B,gBAAiBF,EACjB,YAAY,OACZ,gBAAA,GACA,gBAAA,GACA,QAAS9C,EACT,KAAMH,EACN,KAAK,SAEL,UAAA,EAAAT,EAAA,KAAA,CAAC,MAAD,CAAK,KAAK,OAAV,SAAA,CACGsD,GACC,EAAAtD,EAAA,IAAA,CAAC,IAAD,CACE,KAAK,QACL,MAAO,CACL,MAAO,uCACP,QAAS,WACX,EAEC,SAAAsD,CACA,CAAA,EACD,KACHE,GACC,EAAAxD,EAAA,IAAA,CAAC,IAAD,CACE,KAAK,SACL,MAAO,CACL,MAAO,yCACP,QAAS,WACX,EAEC,SAAAwD,CACA,CAAA,EACD,KACHgD,GAAY,SAAW,GACtB,EAAAxG,EAAA,IAAA,CAAC,IAAD,CACE,MAAO,CACL,MAAO,2CACP,QAAS,YACT,UAAW,QACb,EAEC,SAAAkD,EAAU,OAAS,QACnB,CAAA,EACD,KACHsD,GAAY,KAAK,CAACG,EAAOG,MACxB,EAAA9G,EAAA,IAAA,CAACnB,EAAAA,SAAD,CAAA,SACGiI,EAAM,KAAK,EAAQ,IAAc,CAChC,IAAME,EAAW9E,EAAO,aAAe,KAEvC,OACE,EAAAlC,EAAA,IAAA,CAAC,MAAD,CAEE,QACEgH,EACK,GAAiD,CAChDZ,GAAmBlE,EAAQ8C,CAAK,CAClC,EACA,IAAA,GAEN,UACEgC,EACK,GAAoD,CACnDX,GAAkBnE,EAAQ8C,CAAK,CACjC,EACA,IAAA,GAEN,KAAMgC,EAAW,SAAW,IAAA,GAC5B,MAAOA,EAAW,CAAE,OAAQ,SAAU,EAAI,IAAA,GAC1C,SAAUA,EAAW,EAAI,IAAA,GAEzB,UAAA,EAAAhH,EAAA,IAAA,CAAClB,EAAAA,QAAD,CACE,WAAYoD,EAAO,WAAa,MAAQ,IAAA,GACxC,YAAaA,EAAO,KACpB,cAAgB,GAAiC,CAC/C8D,GAAkB9D,EAAQqD,CAAM,CAClC,EACA,QAAS,CAAC,GAAGxD,GAAyBG,CAAM,CAAC,EAC7C,YACE+E,IAAc,EAAIrF,GAAiB+E,GAAS,IAAA,GAE9C,UAAWzE,EAAO,GAClB,SAAUK,GAAgBL,CAAM,EAChC,UAAWA,EAAO,SAAW,OAC7B,UAAWA,EAAO,UAClB,MAAOI,GAAoBJ,CAAM,EACjC,KAAK,QACN,CAAA,CACE,EApCEA,EAAO,EAoCT,CAET,CAAC,CACO,EA7CKyE,CA6CL,CACX,CACE,C,EACC,CAAA,GACR,EAAA3G,EAAA,KAAA,CAACjB,EAAAA,QAAD,CACE,WAAW,KACX,YAAY,OACZ,QAASuF,EACT,UAAU,WACV,SAAUoB,GACV,QAASA,GACT,cAAuB,CACrBC,GAA0B,CAC5B,EACA,KAAM7B,IAAkB,KACxB,gBAAA,GACA,gBAAA,GACA,KAAK,UACL,eAAe,iCACf,MAAM,OAfR,SAAA,CAiBGR,GACC,EAAAtD,EAAA,IAAA,CAAChB,EAAAA,WAAD,CAAY,MAAM,aAAa,KAAK,QAAQ,QAAQ,OACjD,SAAAsE,CACS,CAAA,EACV,MACJ,EAAAtD,EAAA,IAAA,CAACf,EAAAA,EAAD,CAAc,MAAM,OAAO,KAAK,6BAC9B,UAAA,EAAAe,EAAA,IAAA,CAACd,EAAAA,QAAD,CACE,UAAA,GACA,SAAW,GAAkD,CAC3D+E,EAAkBe,EAAM,OAAO,KAAK,CACtC,EACA,YAAY,mBACZ,KAAM,EACN,MAAOhB,CACR,CAAA,CACW,CAAA,CACT,C,IACP,EAAAhE,EAAA,IAAA,CAACjB,EAAAA,QAAD,CACE,WAAW,KACX,mBAAoB,CAClB,SAAU,CAAC0F,EACX,QAAS,qBACX,EACA,YAAY,OACZ,QAASH,EACT,gBAAgB,QAChB,UAAU,WACV,SAAUwB,GACV,QAASA,GACT,cAAuB,CACrBC,GAAyB,CAC3B,EACA,KAAM7B,IAAiB,KACvB,gBAAA,GACA,gBAAA,GACA,KAAK,UACL,eAAe,2BACf,MAAM,OAEN,UAAA,EAAAlE,EAAA,IAAA,CAACd,EAAAA,QAAD,CACE,UAAA,GACA,SAAW,GAAkD,CAC3DmF,EAAgBW,EAAM,OAAO,KAAK,CACpC,EACA,YAAY,UACZ,KAAM,EACN,MAAOZ,CACR,CAAA,CACI,CAAA,CACP,CAAA,CAAA,EA5LyB,IA8L/B,CAEA,SAAS5B,EAAW,EAA8C,CAIhE,OAHI0E,IAAS,cAAsB,QAC/BA,IAAS,cAAsB,UAC/BA,IAAS,qBAA6B,UACnC,MACT,CAEA,SAASL,GAAiB,EAAe,EAAsB,CAC7D,IAAMM,EAAmB,IAAI,KAAKpG,CAAK,EACvC,GAAI,OAAO,MAAMoG,EAAiB,QAAQ,CAAC,EAAG,MAAO,UACrD,IAAMC,EAAgB,IAAI,KAAKX,EAAI,YAAY,EAAGA,EAAI,SAAS,EAAGA,EAAI,QAAQ,CAAC,EACzEY,EAAyB,IAAI,KACjCF,EAAiB,YAAY,EAC7BA,EAAiB,SAAS,EAC1BA,EAAiB,QAAQ,CAC3B,EACA,GAAIE,EAAuB,QAAQ,IAAMD,EAAc,QAAQ,EAAG,MAAO,QACzE,IAAME,EAAsB,IAAI,KAAKF,CAAa,EAOlD,OANAE,EAAoB,QAAQA,EAAoB,QAAQ,EAAI,CAAC,EACzDD,EAAuB,QAAQ,IAAMC,EAAoB,QAAQ,EAC5D,aAENb,EAAI,QAAQ,EAAIU,EAAiB,QAAQ,GAAM,OAChC,EAAU,YACrB,SACT,CAEA,SAASrC,EAAiB,EAAwB,CAChD,OAAOxB,aAAiB,MAAQA,EAAM,QAAU,QAClD,CAEA,SAASrB,EACP,EACgB,CAChB,GAAI,CACF,IAAMsF,EAAmB,KAAK,MAAMrF,EAAO,WAAW,EAMtD,OAJIsF,EAASD,CAAO,GAAK,OAAOA,EAAQ,aAAgB,UAC/CA,EAAQ,YAGVrF,EAAO,WAChB,MAAQ,CACN,OAAOA,EAAO,WAChB,CACF,CAMA,SAASE,EACP,EACS,CACT,GAAI,CACF,IAAMmF,EAAmB,KAAK,MAAMrF,EAAO,WAAW,EAEtD,OAAOsF,EAASD,CAAO,GAAKA,EAAQ,oBAAsB,EAC5D,MAAQ,CACN,MAAO,EACT,CACF,CAEA,SAASC,EAAS,EAA4D,CAC5E,OAAO,OAAOzG,GAAU,YAAYA,GAAkB,CAAC,MAAM,QAAQA,CAAK,CAC5E,CC7qBA,SAAgB0G,EAAU,CACxB,WACA,SAAStI,EAAAA,eAAe,MACxB,WAAWC,EAAAA,EACX,cACA,aAC+B,CAC/B,OACE,EAAAY,EAAA,IAAA,CAACX,EAAAA,6BAAD,CAAsC,SACpC,UAAA,EAAAW,EAAA,IAAA,CAACV,EAAAA,EAAD,CAA+B,WAC7B,UAAA,EAAAU,EAAA,IAAA,CAACT,EAAAA,EAAD,CAA2B,cAAwB,YACjD,UAAA,EAAAS,EAAA,IAAA,CAACkB,EAAD,CAAA,UACE,EAAAlB,EAAA,KAAA,CAACQ,EAAD,CAAA,SAAA,CACGkH,GACD,EAAA1H,EAAA,IAAA,CAACyC,EAAD,CAAqB,CAAA,CACK,CAAA,CAAA,CACF,CAAA,CAChB,CAAA,CACK,CAAA,CACO,CAAA,CAElC,C"}