@rytass/bpm-core-react 0.17.9 → 0.18.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.
- package/CHANGELOG.md +61 -0
- package/README.md +8 -0
- package/dist/chunks/FormBuilderView-CHaAQL5v.cjs +3 -0
- package/dist/chunks/FormBuilderView-CHaAQL5v.cjs.map +1 -0
- package/dist/chunks/{FormBuilderView-ClLnQ1e_.js → FormBuilderView-vQfVc08Z.js} +493 -437
- package/dist/chunks/FormBuilderView-vQfVc08Z.js.map +1 -0
- package/dist/chunks/{FormRendererView-BsTwZkqU.js → FormRendererView-CTLlG13b.js} +300 -189
- package/dist/chunks/FormRendererView-CTLlG13b.js.map +1 -0
- package/dist/chunks/FormRendererView-ChmM_4Yc.cjs +4 -0
- package/dist/chunks/FormRendererView-ChmM_4Yc.cjs.map +1 -0
- package/dist/chunks/admin-pickers-BjbkBpSJ.cjs +2 -0
- package/dist/chunks/admin-pickers-BjbkBpSJ.cjs.map +1 -0
- package/dist/chunks/{admin-pickers-Djq3h-UR.js → admin-pickers-DfGxsjpp.js} +42 -42
- package/dist/chunks/admin-pickers-DfGxsjpp.js.map +1 -0
- package/dist/chunks/{approval-instance-list-page-CPXMP3PL.js → approval-instance-list-page-BnI4t5mD.js} +7 -8
- package/dist/chunks/{approval-instance-list-page-CPXMP3PL.js.map → approval-instance-list-page-BnI4t5mD.js.map} +1 -1
- package/dist/chunks/{approval-instance-list-page-ax0pm8bt.cjs → approval-instance-list-page-fqvmkBlg.cjs} +2 -2
- package/dist/chunks/{approval-instance-list-page-ax0pm8bt.cjs.map → approval-instance-list-page-fqvmkBlg.cjs.map} +1 -1
- package/dist/chunks/approver-filled-form-B4zPg9kz.cjs +2 -0
- package/dist/chunks/approver-filled-form-B4zPg9kz.cjs.map +1 -0
- package/dist/chunks/approver-filled-form-Bgazhbvd.js +112 -0
- package/dist/chunks/approver-filled-form-Bgazhbvd.js.map +1 -0
- package/dist/chunks/auth-provider-4BeCw7cI.cjs.map +1 -1
- package/dist/chunks/{auth-provider-B5oPmvk2.js → auth-provider-C2b-5mGt.js} +2 -3
- package/dist/chunks/{auth-provider-B5oPmvk2.js.map → auth-provider-C2b-5mGt.js.map} +1 -1
- package/dist/chunks/{bpm-form-field-Cao0rMol.js → bpm-form-field-9B-Det0u.js} +2 -3
- package/dist/chunks/{bpm-form-field-Cao0rMol.js.map → bpm-form-field-9B-Det0u.js.map} +1 -1
- package/dist/chunks/bpm-form-field-Bc6k4ZEO.cjs.map +1 -1
- package/dist/chunks/{categories-riC33J_j.js → categories-BAdxjNSA.js} +14 -15
- package/dist/chunks/{categories-riC33J_j.js.map → categories-BAdxjNSA.js.map} +1 -1
- package/dist/chunks/{categories-AxxhqjLZ.cjs → categories-DQUtVEiV.cjs} +2 -2
- package/dist/chunks/{categories-AxxhqjLZ.cjs.map → categories-DQUtVEiV.cjs.map} +1 -1
- package/dist/chunks/{compose-jnvnnVfN.js → compose-D9ipJpF_.js} +131 -148
- package/dist/chunks/compose-D9ipJpF_.js.map +1 -0
- package/dist/chunks/compose-DtfX8DQ9.cjs +2 -0
- package/dist/chunks/compose-DtfX8DQ9.cjs.map +1 -0
- package/dist/chunks/{dashboard-page-DdBvV6YI.js → dashboard-page-BoqNhjT_.js} +5 -6
- package/dist/chunks/{dashboard-page-DdBvV6YI.js.map → dashboard-page-BoqNhjT_.js.map} +1 -1
- package/dist/chunks/{dashboard-page-BWQBSGDq.cjs → dashboard-page-D7YJ3udk.cjs} +2 -2
- package/dist/chunks/{dashboard-page-BWQBSGDq.cjs.map → dashboard-page-D7YJ3udk.cjs.map} +1 -1
- package/dist/chunks/{delegations-MxF3uJGW.js → delegations-BfhCDz5e.js} +15 -16
- package/dist/chunks/{delegations-MxF3uJGW.js.map → delegations-BfhCDz5e.js.map} +1 -1
- package/dist/chunks/{delegations-Ckf03icM.cjs → delegations-BmFGcuhA.cjs} +2 -2
- package/dist/chunks/{delegations-Ckf03icM.cjs.map → delegations-BmFGcuhA.cjs.map} +1 -1
- package/dist/chunks/{delegations-V9HIe3_P.cjs → delegations-CcS0SHbf.cjs} +2 -2
- package/dist/chunks/{delegations-V9HIe3_P.cjs.map → delegations-CcS0SHbf.cjs.map} +1 -1
- package/dist/chunks/{delegations-xUt1tj8d.js → delegations-DBh5TT6G.js} +16 -17
- package/dist/chunks/{delegations-xUt1tj8d.js.map → delegations-DBh5TT6G.js.map} +1 -1
- package/dist/chunks/designer-Df79YzUn.cjs +65 -0
- package/dist/chunks/designer-Df79YzUn.cjs.map +1 -0
- package/dist/chunks/{designer-B8Gvew_k.js → designer-ubnZbP-A.js} +1779 -1360
- package/dist/chunks/designer-ubnZbP-A.js.map +1 -0
- package/dist/chunks/detail-BBC9kqXd.cjs +5 -0
- package/dist/chunks/detail-BBC9kqXd.cjs.map +1 -0
- package/dist/chunks/detail-C6XKZjlF.js +2665 -0
- package/dist/chunks/detail-C6XKZjlF.js.map +1 -0
- package/dist/chunks/{format-date-time-CB-LxzqT.js → format-date-time-B0LdmWg4.js} +2 -3
- package/dist/chunks/{format-date-time-CB-LxzqT.js.map → format-date-time-B0LdmWg4.js.map} +1 -1
- package/dist/chunks/format-date-time-D-cx5M8z.cjs.map +1 -1
- package/dist/chunks/{format-member-label-DudVI55w.js → format-member-label-C73FuP-d.js} +2 -3
- package/dist/chunks/{format-member-label-DudVI55w.js.map → format-member-label-C73FuP-d.js.map} +1 -1
- package/dist/chunks/format-member-label-t7ANTF62.cjs.map +1 -1
- package/dist/chunks/language-provider-Bdnx3Gxl.cjs.map +1 -1
- package/dist/chunks/{language-provider-B3cEbLe4.js → language-provider-phkWOapf.js} +2 -3
- package/dist/chunks/{language-provider-B3cEbLe4.js.map → language-provider-phkWOapf.js.map} +1 -1
- package/dist/chunks/{login-C6qlTC6F.js → login-D6cc3bo7.js} +4 -5
- package/dist/chunks/{login-C6qlTC6F.js.map → login-D6cc3bo7.js.map} +1 -1
- package/dist/chunks/{login-AtVxZBsC.cjs → login-uvVcltrS.cjs} +2 -2
- package/dist/chunks/{login-AtVxZBsC.cjs.map → login-uvVcltrS.cjs.map} +1 -1
- package/dist/chunks/notifications-Bc4mgSds.cjs.map +1 -1
- package/dist/chunks/{notifications-DJ5wzCye.js → notifications-Cvme5Qbl.js} +2 -3
- package/dist/chunks/{notifications-DJ5wzCye.js.map → notifications-Cvme5Qbl.js.map} +1 -1
- package/dist/chunks/orgs-B_2J2tLX.cjs +2 -0
- package/dist/chunks/{orgs-BeeDDWL1.cjs.map → orgs-B_2J2tLX.cjs.map} +1 -1
- package/dist/chunks/{orgs-8hje_l2n.js → orgs-DqxMds26.js} +48 -49
- package/dist/chunks/{orgs-8hje_l2n.js.map → orgs-DqxMds26.js.map} +1 -1
- package/dist/chunks/{providers-B_d5tbCL.js → providers-BbCs23sk.js} +21 -25
- package/dist/chunks/{providers-B_d5tbCL.js.map → providers-BbCs23sk.js.map} +1 -1
- package/dist/chunks/{providers-chuJvMgv.cjs → providers-D_KAgOOu.cjs} +2 -2
- package/dist/chunks/{providers-chuJvMgv.cjs.map → providers-D_KAgOOu.cjs.map} +1 -1
- package/dist/chunks/router-adapter--gYs13E8.cjs.map +1 -1
- package/dist/chunks/{router-adapter-DftlFTOd.js → router-adapter-CkxCyzf5.js} +2 -3
- package/dist/chunks/{router-adapter-DftlFTOd.js.map → router-adapter-CkxCyzf5.js.map} +1 -1
- package/dist/chunks/{routes-config-yZbESPlw.js → routes-config-BcNaWJWJ.js} +2 -3
- package/dist/chunks/{routes-config-yZbESPlw.js.map → routes-config-BcNaWJWJ.js.map} +1 -1
- package/dist/chunks/routes-config-C1j_vFKo.cjs.map +1 -1
- package/dist/chunks/task-decision-3d17ZvZE.js +29 -0
- package/dist/chunks/task-decision-3d17ZvZE.js.map +1 -0
- package/dist/chunks/task-decision-CqZ4WQOC.cjs +2 -0
- package/dist/chunks/task-decision-CqZ4WQOC.cjs.map +1 -0
- package/dist/chunks/{use-bpm-member-D57LKe_u.js → use-bpm-member-BPzcNU1g.js} +3 -4
- package/dist/chunks/{use-bpm-member-D57LKe_u.js.map → use-bpm-member-BPzcNU1g.js.map} +1 -1
- package/dist/chunks/use-bpm-member-DpNAQtNC.cjs.map +1 -1
- package/dist/chunks/{users-Pl6hY5Y5.js → users-BBtiZ9QQ.js} +22 -25
- package/dist/chunks/{users-Pl6hY5Y5.js.map → users-BBtiZ9QQ.js.map} +1 -1
- package/dist/chunks/{users-O2L_M3BK.cjs → users-EqSzDhuj.cjs} +2 -2
- package/dist/chunks/{users-O2L_M3BK.cjs.map → users-EqSzDhuj.cjs.map} +1 -1
- package/dist/components/admin-picker-options.d.cts +11 -0
- package/dist/components/admin-picker-options.d.ts +11 -0
- package/dist/components/admin-pickers.d.cts +2 -11
- package/dist/components/admin-pickers.d.ts +2 -11
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +17 -19
- package/dist/index.js.map +1 -1
- package/dist/lib/task-decision.d.cts +16 -0
- package/dist/lib/task-decision.d.ts +16 -0
- package/dist/lib/use-org-unit-names.d.cts +10 -0
- package/dist/lib/use-org-unit-names.d.ts +10 -0
- package/dist/next/index.cjs +1 -1
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.js +7 -8
- package/dist/next/index.js.map +1 -1
- package/dist/next/workflow-chat-route.cjs +19 -1
- package/dist/next/workflow-chat-route.cjs.map +1 -1
- package/dist/next/workflow-chat-route.js +2 -3
- package/dist/next/workflow-chat-route.js.map +1 -1
- package/dist/pages/admin/delegations/index.cjs +1 -1
- package/dist/pages/admin/delegations/index.cjs.map +1 -1
- package/dist/pages/admin/delegations/index.js +2 -3
- package/dist/pages/admin/orgs/index.cjs +1 -1
- package/dist/pages/admin/orgs/index.cjs.map +1 -1
- package/dist/pages/admin/orgs/index.js +2 -3
- package/dist/pages/admin/users/index.cjs +1 -1
- package/dist/pages/admin/users/index.cjs.map +1 -1
- package/dist/pages/admin/users/index.js +2 -3
- package/dist/pages/admin/webhook-endpoints/index.cjs.map +1 -1
- package/dist/pages/admin/webhook-endpoints/index.js +1 -2
- package/dist/pages/cc/index.cjs.map +1 -1
- package/dist/pages/cc/index.js +1 -2
- package/dist/pages/dashboard/index.cjs.map +1 -1
- package/dist/pages/dashboard/index.js +1 -2
- package/dist/pages/delegations/index.cjs +1 -1
- package/dist/pages/delegations/index.cjs.map +1 -1
- package/dist/pages/delegations/index.js +2 -3
- package/dist/pages/inbox/index.cjs.map +1 -1
- package/dist/pages/inbox/index.js +1 -2
- package/dist/pages/instances/detail/index.cjs +1 -1
- package/dist/pages/instances/detail/index.cjs.map +1 -1
- package/dist/pages/instances/detail/index.js +2 -3
- package/dist/pages/instances/new/index.cjs.map +1 -1
- package/dist/pages/instances/new/index.js +1 -2
- package/dist/pages/login/index.cjs +1 -1
- package/dist/pages/login/index.cjs.map +1 -1
- package/dist/pages/login/index.js +2 -3
- package/dist/pages/root/index.cjs.map +1 -1
- package/dist/pages/root/index.js +1 -2
- package/dist/pages/search/index.cjs.map +1 -1
- package/dist/pages/search/index.js +1 -2
- package/dist/pages/sent/index.cjs.map +1 -1
- package/dist/pages/sent/index.js +1 -2
- package/dist/pages/settings/notifications/index.cjs.map +1 -1
- package/dist/pages/settings/notifications/index.js +2 -3
- package/dist/pages/templates/categories/index.cjs +1 -1
- package/dist/pages/templates/categories/index.cjs.map +1 -1
- package/dist/pages/templates/categories/index.js +2 -3
- package/dist/pages/templates/compose/index.cjs +1 -1
- package/dist/pages/templates/compose/index.cjs.map +1 -1
- package/dist/pages/templates/compose/index.js +2 -3
- package/dist/pages/templates/designer/index.cjs +1 -1
- package/dist/pages/templates/designer/index.cjs.map +1 -1
- package/dist/pages/templates/designer/index.js +2 -3
- package/dist/pages/templates/index.cjs.map +1 -1
- package/dist/pages/templates/index.js +1 -2
- package/dist/pages/templates/versions/index.cjs.map +1 -1
- package/dist/pages/templates/versions/index.js +1 -2
- package/dist/views/admin/delegations/index.cjs +1 -1
- package/dist/views/admin/delegations/index.js +1 -1
- package/dist/views/admin/index.cjs +1 -1
- package/dist/views/admin/index.js +3 -3
- package/dist/views/admin/orgs/index.cjs +1 -1
- package/dist/views/admin/orgs/index.js +1 -1
- package/dist/views/admin/users/index.cjs +1 -1
- package/dist/views/admin/users/index.js +1 -1
- package/dist/views/admin/webhook-endpoints/index.cjs +1 -1
- package/dist/views/admin/webhook-endpoints/index.cjs.map +1 -1
- package/dist/views/admin/webhook-endpoints/index.js +52 -53
- package/dist/views/admin/webhook-endpoints/index.js.map +1 -1
- package/dist/views/cc/index.cjs +1 -1
- package/dist/views/cc/index.cjs.map +1 -1
- package/dist/views/cc/index.js +2 -3
- package/dist/views/dashboard/index.cjs +1 -1
- package/dist/views/dashboard/index.cjs.map +1 -1
- package/dist/views/dashboard/index.js +2 -3
- package/dist/views/delegations/index.cjs +1 -1
- package/dist/views/delegations/index.js +1 -1
- package/dist/views/forms/builder/index.cjs +1 -1
- package/dist/views/forms/builder/index.js +1 -1
- package/dist/views/forms/renderer/index.cjs +1 -1
- package/dist/views/forms/renderer/index.js +1 -1
- package/dist/views/forms/renderer/org-unit-field-options.d.cts +44 -0
- package/dist/views/forms/renderer/org-unit-field-options.d.ts +44 -0
- package/dist/views/forms/renderer/org-unit-field.d.cts +23 -0
- package/dist/views/forms/renderer/org-unit-field.d.ts +23 -0
- package/dist/views/inbox/index.cjs +1 -1
- package/dist/views/inbox/index.cjs.map +1 -1
- package/dist/views/inbox/index.js +133 -137
- package/dist/views/inbox/index.js.map +1 -1
- package/dist/views/instances/approver-filled-form.d.cts +9 -2
- package/dist/views/instances/approver-filled-form.d.ts +9 -2
- package/dist/views/instances/detail/index.cjs +1 -1
- package/dist/views/instances/detail/index.js +1 -1
- package/dist/views/instances/detail/sections/InstanceHistorySection.d.cts +8 -1
- package/dist/views/instances/detail/sections/InstanceHistorySection.d.ts +8 -1
- package/dist/views/instances/detail/sections/container-helpers.d.cts +0 -1
- package/dist/views/instances/detail/sections/container-helpers.d.ts +0 -1
- package/dist/views/instances/detail/sections/shared.d.cts +2 -2
- package/dist/views/instances/detail/sections/shared.d.ts +2 -2
- package/dist/views/instances/new/index.cjs +1 -1
- package/dist/views/instances/new/index.cjs.map +1 -1
- package/dist/views/instances/new/index.js +24 -21
- package/dist/views/instances/new/index.js.map +1 -1
- package/dist/views/login/index.cjs +1 -1
- package/dist/views/login/index.js +1 -1
- package/dist/views/search/index.cjs +1 -1
- package/dist/views/search/index.cjs.map +1 -1
- package/dist/views/search/index.js +2 -3
- package/dist/views/sent/index.cjs +1 -1
- package/dist/views/sent/index.cjs.map +1 -1
- package/dist/views/sent/index.js +2 -3
- package/dist/views/settings/index.js +1 -1
- package/dist/views/settings/notifications/index.js +1 -1
- package/dist/views/templates/categories/index.cjs +1 -1
- package/dist/views/templates/categories/index.js +1 -1
- package/dist/views/templates/compose/index.cjs +1 -1
- package/dist/views/templates/compose/index.js +1 -1
- package/dist/views/templates/designer/approver-resolver-model.d.cts +211 -0
- package/dist/views/templates/designer/approver-resolver-model.d.ts +211 -0
- package/dist/views/templates/designer/dry-run-assignee-label.d.cts +2 -0
- package/dist/views/templates/designer/dry-run-assignee-label.d.ts +2 -0
- package/dist/views/templates/designer/dry-run-sample-data.d.cts +6 -0
- package/dist/views/templates/designer/dry-run-sample-data.d.ts +6 -0
- package/dist/views/templates/designer/dynamic-form-resolver-options.d.cts +25 -0
- package/dist/views/templates/designer/dynamic-form-resolver-options.d.ts +25 -0
- package/dist/views/templates/designer/index.cjs +1 -1
- package/dist/views/templates/designer/index.js +1 -1
- package/dist/views/templates/designer/node-actor-resolver-options.d.cts +38 -0
- package/dist/views/templates/designer/node-actor-resolver-options.d.ts +38 -0
- package/dist/views/templates/index.cjs +1 -1
- package/dist/views/templates/index.cjs.map +1 -1
- package/dist/views/templates/index.js +50 -51
- package/dist/views/templates/index.js.map +1 -1
- package/dist/views/templates/versions/index.cjs +1 -1
- package/dist/views/templates/versions/index.cjs.map +1 -1
- package/dist/views/templates/versions/index.js +7 -8
- package/dist/views/templates/versions/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunks/FormBuilderView-CUOyS07f.cjs +0 -3
- package/dist/chunks/FormBuilderView-CUOyS07f.cjs.map +0 -1
- package/dist/chunks/FormBuilderView-ClLnQ1e_.js.map +0 -1
- package/dist/chunks/FormRendererView-BsTwZkqU.js.map +0 -1
- package/dist/chunks/FormRendererView-GpsE03np.cjs +0 -4
- package/dist/chunks/FormRendererView-GpsE03np.cjs.map +0 -1
- package/dist/chunks/admin-pickers-BRHk3qKq.cjs +0 -2
- package/dist/chunks/admin-pickers-BRHk3qKq.cjs.map +0 -1
- package/dist/chunks/admin-pickers-Djq3h-UR.js.map +0 -1
- package/dist/chunks/approver-filled-form-D1DmqDVP.cjs +0 -2
- package/dist/chunks/approver-filled-form-D1DmqDVP.cjs.map +0 -1
- package/dist/chunks/approver-filled-form-DuQGPtNB.js +0 -95
- package/dist/chunks/approver-filled-form-DuQGPtNB.js.map +0 -1
- package/dist/chunks/compose-Cock_B2Q.cjs +0 -2
- package/dist/chunks/compose-Cock_B2Q.cjs.map +0 -1
- package/dist/chunks/compose-jnvnnVfN.js.map +0 -1
- package/dist/chunks/designer-B8Gvew_k.js.map +0 -1
- package/dist/chunks/designer-CAEFV1ve.cjs +0 -65
- package/dist/chunks/designer-CAEFV1ve.cjs.map +0 -1
- package/dist/chunks/detail-CExpbazF.js +0 -2629
- package/dist/chunks/detail-CExpbazF.js.map +0 -1
- package/dist/chunks/detail-DIY-L4i-.cjs +0 -2
- package/dist/chunks/detail-DIY-L4i-.cjs.map +0 -1
- package/dist/chunks/orgs-BeeDDWL1.cjs +0 -2
- package/dist/chunks/task-node-label-BF6UllxP.cjs +0 -2
- package/dist/chunks/task-node-label-BF6UllxP.cjs.map +0 -1
- package/dist/chunks/task-node-label-CWZgOBj0.js +0 -9
- package/dist/chunks/task-node-label-CWZgOBj0.js.map +0 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import e from "moment";
|
|
2
|
-
|
|
2
|
+
// src/lib/format-date-time.ts
|
|
3
3
|
function t(t) {
|
|
4
4
|
return t ? e(t).format("YYYY-MM-DD HH:mm:ss") : "-";
|
|
5
5
|
}
|
|
6
|
-
//#endregion
|
|
7
6
|
export { t };
|
|
8
7
|
|
|
9
|
-
//# sourceMappingURL=format-date-time-
|
|
8
|
+
//# sourceMappingURL=format-date-time-B0LdmWg4.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-date-time-
|
|
1
|
+
{"version":3,"file":"format-date-time-B0LdmWg4.js","names":["moment","formatDateTime","value"],"sources":["../../src/lib/format-date-time.ts"],"sourcesContent":["import moment from 'moment';\n\n/**\n * Default BPM datetime formatter: `YYYY-MM-DD HH:mm:ss`. Returns `'-'` for\n * `null` / `undefined` so list cells render a consistent placeholder.\n */\nexport function formatDateTime(value: string | null | undefined): string {\n return value ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-';\n}\n"],"mappings":";;AAMA,SAAgBC,EAAe,GAA0C;CACvE,OAAOC,IAAQF,EAAOE,CAAK,CAAC,CAAC,OAAO,qBAAqB,IAAI;AAC/D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-date-time-D-cx5M8z.cjs","names":["require_rolldown_runtime","moment","formatDateTime","value"],"sources":["../../src/lib/format-date-time.ts"],"sourcesContent":["import moment from 'moment';\n\n/**\n * Default BPM datetime formatter: `YYYY-MM-DD HH:mm:ss`. Returns `'-'` for\n * `null` / `undefined` so list cells render a consistent placeholder.\n */\nexport function formatDateTime(value: string | null | undefined): string {\n return value ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-';\n}\n"],"mappings":"sFAMA,SAAgBE,EAAe,EAA0C,CACvE,OAAOC,GAAA,EAAQF,EAAA,QAAA,CAAOE,CAAK,CAAC,CAAC,OAAO,qBAAqB,EAAI,GAC/D"}
|
|
1
|
+
{"version":3,"file":"format-date-time-D-cx5M8z.cjs","names":["require_rolldown_runtime","moment","formatDateTime","value"],"sources":["../../src/lib/format-date-time.ts"],"sourcesContent":["import moment from 'moment';\n\n/**\n * Default BPM datetime formatter: `YYYY-MM-DD HH:mm:ss`. Returns `'-'` for\n * `null` / `undefined` so list cells render a consistent placeholder.\n */\nexport function formatDateTime(value: string | null | undefined): string {\n return value ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-';\n}\n"],"mappings":"A,sFAMA,SAAgBE,EAAe,EAA0C,CACvE,OAAOC,GAAA,EAAQF,EAAA,QAAA,CAAOE,CAAK,CAAC,CAAC,OAAO,qBAAqB,EAAI,GAC/D,C"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/lib/format-member-label.ts
|
|
2
2
|
function e(e, t, n = "ascii") {
|
|
3
3
|
if (!t?.trim()) return e;
|
|
4
4
|
switch (n) {
|
|
@@ -7,7 +7,6 @@ function e(e, t, n = "ascii") {
|
|
|
7
7
|
case "ascii": return `${e} (${t})`;
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
-
//#endregion
|
|
11
10
|
export { e as t };
|
|
12
11
|
|
|
13
|
-
//# sourceMappingURL=format-member-label-
|
|
12
|
+
//# sourceMappingURL=format-member-label-C73FuP-d.js.map
|
package/dist/chunks/{format-member-label-DudVI55w.js.map → format-member-label-C73FuP-d.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-member-label-
|
|
1
|
+
{"version":3,"file":"format-member-label-C73FuP-d.js","names":["formatMemberLabel","email","name","style"],"sources":["../../src/lib/format-member-label.ts"],"sourcesContent":["/**\n * How the e-mail is attached to the name: `ascii` is `name (email)`,\n * `fullwidth` is `name(email)` and `dot` is `name · email`. Each call site\n * keeps the separator it always had.\n */\nexport type MemberLabelStyle = 'ascii' | 'dot' | 'fullwidth';\n\n/**\n * `name` followed by the e-mail in the given style. A host may resolve a member\n * with `email: ''` (or a blank string), and then the label is just the name —\n * never `name ()` or `name · `.\n */\nexport function formatMemberLabel(\n name: string,\n email: string | null | undefined,\n style: MemberLabelStyle = 'ascii',\n): string {\n if (!email?.trim()) {\n return name;\n }\n\n switch (style) {\n case 'fullwidth':\n return `${name}(${email})`;\n case 'dot':\n return `${name} · ${email}`;\n case 'ascii':\n return `${name} (${email})`;\n }\n}\n"],"mappings":";AAYA,SAAgBA,EACd,GACA,GACA,IAA0B,SAClB;CACR,IAAI,CAACC,GAAO,KAAK,GACf,OAAOC;CAGT,QAAQC,GAAR;EACE,KAAK,aACH,OAAO,GAAGD,EAAK,GAAGD,EAAM;EAC1B,KAAK,OACH,OAAO,GAAGC,EAAK,KAAKD;EACtB,KAAK,SACH,OAAO,GAAGC,EAAK,IAAID,EAAM;CAC7B;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format-member-label-t7ANTF62.cjs","names":["formatMemberLabel","email","name","style"],"sources":["../../src/lib/format-member-label.ts"],"sourcesContent":["/**\n * How the e-mail is attached to the name: `ascii` is `name (email)`,\n * `fullwidth` is `name(email)` and `dot` is `name · email`. Each call site\n * keeps the separator it always had.\n */\nexport type MemberLabelStyle = 'ascii' | 'dot' | 'fullwidth';\n\n/**\n * `name` followed by the e-mail in the given style. A host may resolve a member\n * with `email: ''` (or a blank string), and then the label is just the name —\n * never `name ()` or `name · `.\n */\nexport function formatMemberLabel(\n name: string,\n email: string | null | undefined,\n style: MemberLabelStyle = 'ascii',\n): string {\n if (!email?.trim()) {\n return name;\n }\n\n switch (style) {\n case 'fullwidth':\n return `${name}(${email})`;\n case 'dot':\n return `${name} · ${email}`;\n case 'ascii':\n return `${name} (${email})`;\n }\n}\n"],"mappings":"AAYA,SAAgBA,EACd,EACA,EACA,EAA0B,QAClB,CACR,GAAI,CAACC,GAAO,KAAK,EACf,OAAOC,EAGT,OAAQC,EAAR,CACE,IAAK,YACH,MAAO,GAAGD,EAAK,GAAGD,EAAM,GAC1B,IAAK,MACH,MAAO,GAAGC,EAAK,KAAKD,IACtB,IAAK,QACH,MAAO,GAAGC,EAAK,IAAID,EAAM,EAC7B,CACF"}
|
|
1
|
+
{"version":3,"file":"format-member-label-t7ANTF62.cjs","names":["formatMemberLabel","email","name","style"],"sources":["../../src/lib/format-member-label.ts"],"sourcesContent":["/**\n * How the e-mail is attached to the name: `ascii` is `name (email)`,\n * `fullwidth` is `name(email)` and `dot` is `name · email`. Each call site\n * keeps the separator it always had.\n */\nexport type MemberLabelStyle = 'ascii' | 'dot' | 'fullwidth';\n\n/**\n * `name` followed by the e-mail in the given style. A host may resolve a member\n * with `email: ''` (or a blank string), and then the label is just the name —\n * never `name ()` or `name · `.\n */\nexport function formatMemberLabel(\n name: string,\n email: string | null | undefined,\n style: MemberLabelStyle = 'ascii',\n): string {\n if (!email?.trim()) {\n return name;\n }\n\n switch (style) {\n case 'fullwidth':\n return `${name}(${email})`;\n case 'dot':\n return `${name} · ${email}`;\n case 'ascii':\n return `${name} (${email})`;\n }\n}\n"],"mappings":"AAYA,SAAgBA,EACd,EACA,EACA,EAA0B,QAClB,CACR,GAAI,CAACC,GAAO,KAAK,EACf,OAAOC,EAGT,OAAQC,EAAR,CACE,IAAK,YACH,MAAO,GAAGD,EAAK,GAAGD,EAAM,GAC1B,IAAK,MACH,MAAO,GAAGC,EAAK,KAAKD,IACtB,IAAK,QACH,MAAO,GAAGC,EAAK,IAAID,EAAM,EAC7B,CACF,C"}
|
|
@@ -1 +1 @@
|
|
|
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":"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"}
|
|
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,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { createContext as e, useContext as t } from "react";
|
|
3
3
|
import { jsx as n } from "react/jsx-runtime";
|
|
4
|
-
|
|
4
|
+
// src/lib/language-provider.tsx
|
|
5
5
|
var r = "zh-TW", i = ["zh-TW", "en"], a = e(r);
|
|
6
6
|
function o({ children: e, language: t }) {
|
|
7
7
|
return /* @__PURE__ */ n(a.Provider, {
|
|
@@ -12,7 +12,6 @@ function o({ children: e, language: t }) {
|
|
|
12
12
|
function s() {
|
|
13
13
|
return t(a);
|
|
14
14
|
}
|
|
15
|
-
//#endregion
|
|
16
15
|
export { s as i, i as n, r, o as t };
|
|
17
16
|
|
|
18
|
-
//# sourceMappingURL=language-provider-
|
|
17
|
+
//# sourceMappingURL=language-provider-phkWOapf.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"language-provider-
|
|
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,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { n as e } from "./auth-provider-C2b-5mGt.js";
|
|
3
|
+
import { r as t } from "./router-adapter-CkxCyzf5.js";
|
|
4
4
|
import { useCallback as n, useEffect as r, useState as i } from "react";
|
|
5
5
|
import { Button as a, Input as o, Typography as s } from "@mezzanine-ui/react";
|
|
6
6
|
import { listApiTestMembers as c } from "@rytass/bpm-core-client";
|
|
@@ -17,7 +17,7 @@ import '../login.css';var f = {
|
|
|
17
17
|
demoUserButton: "bpm_demoUserButton_YdkSa"
|
|
18
18
|
}, p = "/rytass-logo.png", m = "lin.ceo@example.internal", h = "demo";
|
|
19
19
|
function g({ logoSrc: g = p, defaultIdentifier: v = m, defaultPassword: b = h, defaultNextPath: x, brandTitle: S = "BPM Admin", brandSubtitle: C = "BPM API 登入" } = {}) {
|
|
20
|
-
let w =
|
|
20
|
+
let w = t(), { loading: T, login: E, member: D } = e(), [O, k] = i([]), [A, j] = i(null), [M, N] = i(v), [P, F] = i(b), [I, L] = i(!1);
|
|
21
21
|
r(() => {
|
|
22
22
|
!T && D && w.replace(_(x));
|
|
23
23
|
}, [
|
|
@@ -152,7 +152,6 @@ function v() {
|
|
|
152
152
|
function y(e) {
|
|
153
153
|
return e instanceof Error ? e.message : "登入失敗";
|
|
154
154
|
}
|
|
155
|
-
//#endregion
|
|
156
155
|
export { g as t };
|
|
157
156
|
|
|
158
|
-
//# sourceMappingURL=login-
|
|
157
|
+
//# sourceMappingURL=login-D6cc3bo7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-C6qlTC6F.js","names":["styles","useRouterAdapter","useAuth","login_module_default","DEFAULT_LOGO","DEFAULT_IDENTIFIER","DEFAULT_PASSWORD","LoginView","router","testMembers","setTestMembers","useState","error","setError","identifier","setIdentifier","defaultIdentifier","password","setPassword","defaultPassword","submitting","setSubmitting","useEffect","member","resolveNextPath","defaultNextPath","loading","listApiTestMembers","handleSubmit","useCallback","event","login","loginError","readErrorMessage","handleIdentifierChange","handlePasswordChange","logoSrc","Typography","brandTitle","brandSubtitle","Input","Button","LoginIcon","testMember","fallback","next","readBrowserSearchParams"],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '登入失敗';\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"login-D6cc3bo7.js","names":["styles","useAuth","useRouterAdapter","login_module_default","DEFAULT_LOGO","DEFAULT_IDENTIFIER","DEFAULT_PASSWORD","LoginView","router","testMembers","setTestMembers","useState","error","setError","identifier","setIdentifier","defaultIdentifier","password","setPassword","defaultPassword","submitting","setSubmitting","useEffect","member","resolveNextPath","defaultNextPath","loading","listApiTestMembers","handleSubmit","useCallback","event","login","loginError","readErrorMessage","handleIdentifierChange","handlePasswordChange","logoSrc","Typography","brandTitle","brandSubtitle","Input","Button","LoginIcon","testMember","fallback","next","readBrowserSearchParams"],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '登入失敗';\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";require('../login.css');const e=require("./
|
|
2
|
-
//# sourceMappingURL=login-
|
|
1
|
+
"use client";require('../login.css');const e=require("./auth-provider-4BeCw7cI.cjs"),t=require("./router-adapter--gYs13E8.cjs");let n=require("react"),r=require("@mezzanine-ui/react"),i=require("@rytass/bpm-core-client"),a=require("react/jsx-runtime"),o=require("@mezzanine-ui/icons");var s={loginPage:`bpm_loginPage_BjzaI`,loginPanel:`bpm_loginPanel_xo0TW`,brand:`bpm_brand_KVmvZ`,brandLogo:`bpm_brandLogo_jxEAG`,form:`bpm_form_rKKP2`,demoUsers:`bpm_demoUsers_g-5Hd`,demoUserList:`bpm_demoUserList_IxtKp`,demoUserButton:`bpm_demoUserButton_YdkSa`},c=`/rytass-logo.png`,l=`lin.ceo@example.internal`,u=`demo`;function d({logoSrc:d=c,defaultIdentifier:p=l,defaultPassword:h=u,defaultNextPath:g,brandTitle:_=`BPM Admin`,brandSubtitle:v=`BPM API 登入`}={}){let y=t.r(),{loading:b,login:x,member:S}=e.n(),[C,w]=(0,n.useState)([]),[T,E]=(0,n.useState)(null),[D,O]=(0,n.useState)(p),[k,A]=(0,n.useState)(h),[j,M]=(0,n.useState)(!1);(0,n.useEffect)(()=>{!b&&S&&y.replace(f(g))},[g,b,S,y]),(0,n.useEffect)(()=>{(async()=>{try{w(await(0,i.listApiTestMembers)())}catch{w([])}})()},[]);let N=(0,n.useCallback)(async e=>{e.preventDefault(),E(null),M(!0);try{await x({identifier:D,password:k}),y.replace(f(g))}catch(e){E(m(e))}finally{M(!1)}},[g,D,x,k,y]);function P(e){O(e.target.value)}function F(e){A(e.target.value)}return(0,a.jsx)(`main`,{className:s.loginPage,children:(0,a.jsxs)(`section`,{className:s.loginPanel,children:[(0,a.jsxs)(`div`,{className:s.brand,children:[(0,a.jsx)(`img`,{alt:``,className:s.brandLogo,src:d}),(0,a.jsxs)(`div`,{children:[(0,a.jsx)(r.Typography,{variant:`h3`,children:_}),(0,a.jsx)(r.Typography,{color:`text-neutral`,variant:`body`,children:v})]})]}),(0,a.jsxs)(`form`,{className:s.form,onSubmit:N,children:[(0,a.jsxs)(`label`,{children:[(0,a.jsx)(r.Typography,{color:`text-neutral`,variant:`caption`,children:`帳號`}),(0,a.jsx)(r.Input,{fullWidth:!0,name:`identifier`,onChange:P,placeholder:`member id 或 email`,value:D})]}),(0,a.jsxs)(`label`,{children:[(0,a.jsx)(r.Typography,{color:`text-neutral`,variant:`caption`,children:`密碼`}),(0,a.jsx)(r.Input,{fullWidth:!0,inputType:`password`,name:`password`,onChange:F,value:k,variant:`password`})]}),T?(0,a.jsx)(r.Typography,{color:`text-error`,variant:`body`,children:T}):null,(0,a.jsx)(r.Button,{disabled:j,icon:o.LoginIcon,iconType:`leading`,type:`submit`,variant:`base-primary`,children:`登入`})]}),C.length?(0,a.jsxs)(`div`,{className:s.demoUsers,children:[(0,a.jsx)(r.Typography,{color:`text-neutral`,variant:`caption`,children:`測試帳號`}),(0,a.jsx)(`div`,{className:s.demoUserList,children:C.map(e=>(0,a.jsxs)(`button`,{className:s.demoUserButton,onClick:()=>O(e.email),type:`button`,children:[(0,a.jsx)(`span`,{children:e.name}),(0,a.jsx)(`span`,{children:e.email})]},e.memberId))})]}):null]})})}function f(e){if(e&&e.startsWith(`/`)&&!e.startsWith(`//`))return e;let t=p().get(`next`);return!t||!t.startsWith(`/`)||t.startsWith(`//`)?`/`:t}function p(){return typeof window>`u`?new URLSearchParams:new URLSearchParams(window.location.search)}function m(e){return e instanceof Error?e.message:`登入失敗`}Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return d}});
|
|
2
|
+
//# sourceMappingURL=login-uvVcltrS.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-AtVxZBsC.cjs","names":["useRouterAdapter","useAuth","useState","listApiTestMembers","useCallback","styles","Typography","Input","Button","LoginIcon","require_router_adapter","require_auth_provider","react","_mezzanine_ui_react","_rytass_bpm_core_client","react_jsx_runtime","_mezzanine_ui_icons","login_module_default","DEFAULT_LOGO","DEFAULT_IDENTIFIER","DEFAULT_PASSWORD","LoginView","router","testMembers","setTestMembers","error","setError","identifier","setIdentifier","defaultIdentifier","password","setPassword","defaultPassword","submitting","setSubmitting","member","resolveNextPath","defaultNextPath","loading","handleSubmit","event","login","loginError","readErrorMessage","handleIdentifierChange","handlePasswordChange","logoSrc","brandTitle","brandSubtitle","testMember","fallback","next","readBrowserSearchParams"],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '登入失敗';\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"login-uvVcltrS.cjs","names":["useRouterAdapter","useAuth","useState","listApiTestMembers","useCallback","styles","Typography","Input","Button","LoginIcon","require_auth_provider","require_router_adapter","react","_mezzanine_ui_react","_rytass_bpm_core_client","react_jsx_runtime","_mezzanine_ui_icons","login_module_default","DEFAULT_LOGO","DEFAULT_IDENTIFIER","DEFAULT_PASSWORD","LoginView","router","testMembers","setTestMembers","error","setError","identifier","setIdentifier","defaultIdentifier","password","setPassword","defaultPassword","submitting","setSubmitting","member","resolveNextPath","defaultNextPath","loading","handleSubmit","event","login","loginError","readErrorMessage","handleIdentifierChange","handlePasswordChange","logoSrc","brandTitle","brandSubtitle","testMember","fallback","next","readBrowserSearchParams"],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '登入失敗';\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications-Bc4mgSds.cjs","names":["useAuth","useState","updateNotificationPreference","_Fragment","PageHeader","ContentHeader","SectionGroup","Section","FilterArea","styles","FilterLine","Filter","FormField","FormFieldLayout","RadioGroup","Typography","require_rolldown_runtime","require_auth_provider","react","_mezzanine_ui_react","react_jsx_runtime","_rytass_bpm_core_client_workflow","_mezzanine_ui_core_form","_mezzanine_ui_react_ContentHeader","notification_settings_module_default","DIGEST_OPTIONS","ENABLED_SEGMENT_OPTIONS","DEFAULT_PREFERENCE","SettingsNotificationsView","currentMemberId","member","preference","setPreference","loading","setLoading","saving","setSaving","error","setError","nextPreference","readErrorMessage","requestError","handlePreferenceChange","previousPreference","controlsDisabled","FILTER_FIELD_STYLE","readEnabledSegmentValue","event","readEnabledSegmentValueId","readDigestMode","value","enabled"],"sources":["../../src/views/settings/notifications/notification-settings.module.scss","../../src/views/settings/notifications/SettingsNotificationsView.tsx"],"sourcesContent":[".preferenceFilter {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.segmentFilterControl {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n min-width: 0;\n white-space: nowrap;\n\n :global(.mzn-input-check-group--segmented) {\n align-self: flex-start;\n transform: translateY(-7px);\n }\n}\n\n.segmentFilterLabel {\n flex: 0 0 auto;\n color: var(--mzn-color-text-primary, #1f2937);\n font-size: 13px;\n line-height: 16px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n CSSProperties,\n ReactElement,\n useEffect,\n useState,\n} from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n PageHeader,\n RadioGroup,\n Section,\n SectionGroup,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport { useAuth } from '../../../lib/auth-provider';\nimport {\n NotificationDigestMode,\n NotificationPreferenceRecord,\n readNotificationPreference,\n updateNotificationPreference,\n} from '@rytass/bpm-core-client/workflow';\nimport styles from './notification-settings.module.scss';\n\ninterface DigestOption {\n readonly id: NotificationDigestMode;\n readonly name: string;\n}\n\ntype EnabledSegmentValue = 'OFF' | 'ON';\n\ninterface EnabledSegmentOption {\n readonly id: EnabledSegmentValue;\n readonly name: string;\n}\n\nconst DIGEST_OPTIONS: readonly DigestOption[] = [\n { id: 'INSTANT', name: '即時通知' },\n { id: 'DAILY', name: '每日摘要' },\n];\n\nconst ENABLED_SEGMENT_OPTIONS: readonly EnabledSegmentOption[] = [\n { id: 'ON', name: '開' },\n { id: 'OFF', name: '關' },\n];\n\nconst DEFAULT_PREFERENCE: NotificationPreferenceRecord = {\n emailDigestMode: 'INSTANT',\n emailEnabled: true,\n inAppEnabled: true,\n memberId: '',\n quietHoursEnd: null,\n quietHoursStart: null,\n updatedAt: '',\n};\n\n\nexport function SettingsNotificationsView(): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [preference, setPreference] =\n useState<NotificationPreferenceRecord>(DEFAULT_PREFERENCE);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect((): void => {\n if (!currentMemberId) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n readNotificationPreference(currentMemberId)\n .then((nextPreference): void => {\n setPreference(nextPreference);\n })\n .catch((requestError: unknown): void => {\n setError(readErrorMessage(requestError));\n })\n .finally((): void => {\n setLoading(false);\n });\n }, [currentMemberId]);\n\n async function handlePreferenceChange(\n nextPreference: NotificationPreferenceRecord,\n ): Promise<void> {\n if (!currentMemberId || saving) {\n return;\n }\n\n const previousPreference = preference;\n setPreference(nextPreference);\n setSaving(true);\n\n try {\n setPreference(\n await updateNotificationPreference({\n emailDigestMode: nextPreference.emailDigestMode,\n emailEnabled: nextPreference.emailEnabled,\n inAppEnabled: nextPreference.inAppEnabled,\n memberId: currentMemberId,\n quietHoursEnd: nextPreference.quietHoursEnd,\n quietHoursStart: nextPreference.quietHoursStart,\n }),\n );\n } catch (requestError: unknown) {\n setPreference(previousPreference);\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const controlsDisabled = loading || saving;\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"調整站內通知、Email 通知與摘要頻率。\"\n title=\"通知設定\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.preferenceFilter} isDirty={false}>\n <FilterLine>\n <Filter minWidth={160} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"inAppEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n 站內通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"inAppEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n inAppEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.inAppEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={180} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"emailEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.emailEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={280} span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"emailDigestMode\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 頻率\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailDigestMode\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailDigestMode: readDigestMode(\n event.target.value,\n ),\n });\n }}\n options={[...DIGEST_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={preference.emailDigestMode}\n />\n </div>\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 偏好設定會立即生效。\n </Typography>\n )}\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nconst FILTER_FIELD_STYLE = {\n minWidth: 0,\n whiteSpace: 'nowrap',\n} satisfies CSSProperties;\n\nfunction readDigestMode(value: unknown): NotificationDigestMode {\n return value === 'DAILY' ? 'DAILY' : 'INSTANT';\n}\n\nfunction readEnabledSegmentValue(value: unknown): boolean {\n return value !== 'OFF';\n}\n\nfunction readEnabledSegmentValueId(enabled: boolean): EnabledSegmentValue {\n return enabled ? 'ON' : 'OFF';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"notifications-Bc4mgSds.cjs","names":["useAuth","useState","updateNotificationPreference","_Fragment","PageHeader","ContentHeader","SectionGroup","Section","FilterArea","styles","FilterLine","Filter","FormField","FormFieldLayout","RadioGroup","Typography","require_rolldown_runtime","require_auth_provider","react","_mezzanine_ui_react","react_jsx_runtime","_rytass_bpm_core_client_workflow","_mezzanine_ui_core_form","_mezzanine_ui_react_ContentHeader","notification_settings_module_default","DIGEST_OPTIONS","ENABLED_SEGMENT_OPTIONS","DEFAULT_PREFERENCE","SettingsNotificationsView","currentMemberId","member","preference","setPreference","loading","setLoading","saving","setSaving","error","setError","nextPreference","readErrorMessage","requestError","handlePreferenceChange","previousPreference","controlsDisabled","FILTER_FIELD_STYLE","readEnabledSegmentValue","event","readEnabledSegmentValueId","readDigestMode","value","enabled"],"sources":["../../src/views/settings/notifications/notification-settings.module.scss","../../src/views/settings/notifications/SettingsNotificationsView.tsx"],"sourcesContent":[".preferenceFilter {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.segmentFilterControl {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n min-width: 0;\n white-space: nowrap;\n\n :global(.mzn-input-check-group--segmented) {\n align-self: flex-start;\n transform: translateY(-7px);\n }\n}\n\n.segmentFilterLabel {\n flex: 0 0 auto;\n color: var(--mzn-color-text-primary, #1f2937);\n font-size: 13px;\n line-height: 16px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n CSSProperties,\n ReactElement,\n useEffect,\n useState,\n} from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n PageHeader,\n RadioGroup,\n Section,\n SectionGroup,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport { useAuth } from '../../../lib/auth-provider';\nimport {\n NotificationDigestMode,\n NotificationPreferenceRecord,\n readNotificationPreference,\n updateNotificationPreference,\n} from '@rytass/bpm-core-client/workflow';\nimport styles from './notification-settings.module.scss';\n\ninterface DigestOption {\n readonly id: NotificationDigestMode;\n readonly name: string;\n}\n\ntype EnabledSegmentValue = 'OFF' | 'ON';\n\ninterface EnabledSegmentOption {\n readonly id: EnabledSegmentValue;\n readonly name: string;\n}\n\nconst DIGEST_OPTIONS: readonly DigestOption[] = [\n { id: 'INSTANT', name: '即時通知' },\n { id: 'DAILY', name: '每日摘要' },\n];\n\nconst ENABLED_SEGMENT_OPTIONS: readonly EnabledSegmentOption[] = [\n { id: 'ON', name: '開' },\n { id: 'OFF', name: '關' },\n];\n\nconst DEFAULT_PREFERENCE: NotificationPreferenceRecord = {\n emailDigestMode: 'INSTANT',\n emailEnabled: true,\n inAppEnabled: true,\n memberId: '',\n quietHoursEnd: null,\n quietHoursStart: null,\n updatedAt: '',\n};\n\n\nexport function SettingsNotificationsView(): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [preference, setPreference] =\n useState<NotificationPreferenceRecord>(DEFAULT_PREFERENCE);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect((): void => {\n if (!currentMemberId) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n readNotificationPreference(currentMemberId)\n .then((nextPreference): void => {\n setPreference(nextPreference);\n })\n .catch((requestError: unknown): void => {\n setError(readErrorMessage(requestError));\n })\n .finally((): void => {\n setLoading(false);\n });\n }, [currentMemberId]);\n\n async function handlePreferenceChange(\n nextPreference: NotificationPreferenceRecord,\n ): Promise<void> {\n if (!currentMemberId || saving) {\n return;\n }\n\n const previousPreference = preference;\n setPreference(nextPreference);\n setSaving(true);\n\n try {\n setPreference(\n await updateNotificationPreference({\n emailDigestMode: nextPreference.emailDigestMode,\n emailEnabled: nextPreference.emailEnabled,\n inAppEnabled: nextPreference.inAppEnabled,\n memberId: currentMemberId,\n quietHoursEnd: nextPreference.quietHoursEnd,\n quietHoursStart: nextPreference.quietHoursStart,\n }),\n );\n } catch (requestError: unknown) {\n setPreference(previousPreference);\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const controlsDisabled = loading || saving;\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"調整站內通知、Email 通知與摘要頻率。\"\n title=\"通知設定\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.preferenceFilter} isDirty={false}>\n <FilterLine>\n <Filter minWidth={160} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"inAppEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n 站內通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"inAppEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n inAppEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.inAppEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={180} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"emailEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.emailEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={280} span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"emailDigestMode\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 頻率\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailDigestMode\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailDigestMode: readDigestMode(\n event.target.value,\n ),\n });\n }}\n options={[...DIGEST_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={preference.emailDigestMode}\n />\n </div>\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 偏好設定會立即生效。\n </Typography>\n )}\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nconst FILTER_FIELD_STYLE = {\n minWidth: 0,\n whiteSpace: 'nowrap',\n} satisfies CSSProperties;\n\nfunction readDigestMode(value: unknown): NotificationDigestMode {\n return value === 'DAILY' ? 'DAILY' : 'INSTANT';\n}\n\nfunction readEnabledSegmentValue(value: unknown): boolean {\n return value !== 'OFF';\n}\n\nfunction readEnabledSegmentValueId(enabled: boolean): EnabledSegmentValue {\n return enabled ? 'ON' : 'OFF';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { n as e } from "./auth-provider-
|
|
2
|
+
import { n as e } from "./auth-provider-C2b-5mGt.js";
|
|
3
3
|
import { useEffect as t, useState as n } from "react";
|
|
4
4
|
import { Filter as r, FilterArea as i, FilterLine as a, FormField as o, PageHeader as s, RadioGroup as c, Section as l, SectionGroup as u, Typography as d } from "@mezzanine-ui/react";
|
|
5
5
|
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
@@ -187,7 +187,6 @@ function D(e) {
|
|
|
187
187
|
function O(e) {
|
|
188
188
|
return e instanceof Error ? e.message : "發生未知錯誤";
|
|
189
189
|
}
|
|
190
|
-
//#endregion
|
|
191
190
|
export { C as t };
|
|
192
191
|
|
|
193
|
-
//# sourceMappingURL=notifications-
|
|
192
|
+
//# sourceMappingURL=notifications-Cvme5Qbl.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications-DJ5wzCye.js","names":["_Fragment","styles","useAuth","Fragment$1","ContentHeader","notification_settings_module_default","DIGEST_OPTIONS","ENABLED_SEGMENT_OPTIONS","DEFAULT_PREFERENCE","SettingsNotificationsView","currentMemberId","member","preference","setPreference","useState","loading","setLoading","saving","setSaving","error","setError","readNotificationPreference","nextPreference","readErrorMessage","requestError","handlePreferenceChange","previousPreference","updateNotificationPreference","controlsDisabled","PageHeader","SectionGroup","Section","FilterArea","FilterLine","Filter","FormField","FormFieldLayout","FILTER_FIELD_STYLE","RadioGroup","readEnabledSegmentValue","event","readEnabledSegmentValueId","readDigestMode","Typography","value","enabled"],"sources":["../../src/views/settings/notifications/notification-settings.module.scss","../../src/views/settings/notifications/SettingsNotificationsView.tsx"],"sourcesContent":[".preferenceFilter {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.segmentFilterControl {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n min-width: 0;\n white-space: nowrap;\n\n :global(.mzn-input-check-group--segmented) {\n align-self: flex-start;\n transform: translateY(-7px);\n }\n}\n\n.segmentFilterLabel {\n flex: 0 0 auto;\n color: var(--mzn-color-text-primary, #1f2937);\n font-size: 13px;\n line-height: 16px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n CSSProperties,\n ReactElement,\n useEffect,\n useState,\n} from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n PageHeader,\n RadioGroup,\n Section,\n SectionGroup,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport { useAuth } from '../../../lib/auth-provider';\nimport {\n NotificationDigestMode,\n NotificationPreferenceRecord,\n readNotificationPreference,\n updateNotificationPreference,\n} from '@rytass/bpm-core-client/workflow';\nimport styles from './notification-settings.module.scss';\n\ninterface DigestOption {\n readonly id: NotificationDigestMode;\n readonly name: string;\n}\n\ntype EnabledSegmentValue = 'OFF' | 'ON';\n\ninterface EnabledSegmentOption {\n readonly id: EnabledSegmentValue;\n readonly name: string;\n}\n\nconst DIGEST_OPTIONS: readonly DigestOption[] = [\n { id: 'INSTANT', name: '即時通知' },\n { id: 'DAILY', name: '每日摘要' },\n];\n\nconst ENABLED_SEGMENT_OPTIONS: readonly EnabledSegmentOption[] = [\n { id: 'ON', name: '開' },\n { id: 'OFF', name: '關' },\n];\n\nconst DEFAULT_PREFERENCE: NotificationPreferenceRecord = {\n emailDigestMode: 'INSTANT',\n emailEnabled: true,\n inAppEnabled: true,\n memberId: '',\n quietHoursEnd: null,\n quietHoursStart: null,\n updatedAt: '',\n};\n\n\nexport function SettingsNotificationsView(): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [preference, setPreference] =\n useState<NotificationPreferenceRecord>(DEFAULT_PREFERENCE);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect((): void => {\n if (!currentMemberId) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n readNotificationPreference(currentMemberId)\n .then((nextPreference): void => {\n setPreference(nextPreference);\n })\n .catch((requestError: unknown): void => {\n setError(readErrorMessage(requestError));\n })\n .finally((): void => {\n setLoading(false);\n });\n }, [currentMemberId]);\n\n async function handlePreferenceChange(\n nextPreference: NotificationPreferenceRecord,\n ): Promise<void> {\n if (!currentMemberId || saving) {\n return;\n }\n\n const previousPreference = preference;\n setPreference(nextPreference);\n setSaving(true);\n\n try {\n setPreference(\n await updateNotificationPreference({\n emailDigestMode: nextPreference.emailDigestMode,\n emailEnabled: nextPreference.emailEnabled,\n inAppEnabled: nextPreference.inAppEnabled,\n memberId: currentMemberId,\n quietHoursEnd: nextPreference.quietHoursEnd,\n quietHoursStart: nextPreference.quietHoursStart,\n }),\n );\n } catch (requestError: unknown) {\n setPreference(previousPreference);\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const controlsDisabled = loading || saving;\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"調整站內通知、Email 通知與摘要頻率。\"\n title=\"通知設定\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.preferenceFilter} isDirty={false}>\n <FilterLine>\n <Filter minWidth={160} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"inAppEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n 站內通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"inAppEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n inAppEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.inAppEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={180} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"emailEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.emailEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={280} span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"emailDigestMode\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 頻率\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailDigestMode\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailDigestMode: readDigestMode(\n event.target.value,\n ),\n });\n }}\n options={[...DIGEST_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={preference.emailDigestMode}\n />\n </div>\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 偏好設定會立即生效。\n </Typography>\n )}\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nconst FILTER_FIELD_STYLE = {\n minWidth: 0,\n whiteSpace: 'nowrap',\n} satisfies CSSProperties;\n\nfunction readDigestMode(value: unknown): NotificationDigestMode {\n return value === 'DAILY' ? 'DAILY' : 'INSTANT';\n}\n\nfunction readEnabledSegmentValue(value: unknown): boolean {\n return value !== 'OFF';\n}\n\nfunction readEnabledSegmentValueId(enabled: boolean): EnabledSegmentValue {\n return enabled ? 'ON' : 'OFF';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"notifications-Cvme5Qbl.js","names":["_Fragment","styles","useAuth","Fragment$1","ContentHeader","notification_settings_module_default","DIGEST_OPTIONS","ENABLED_SEGMENT_OPTIONS","DEFAULT_PREFERENCE","SettingsNotificationsView","currentMemberId","member","preference","setPreference","useState","loading","setLoading","saving","setSaving","error","setError","readNotificationPreference","nextPreference","readErrorMessage","requestError","handlePreferenceChange","previousPreference","updateNotificationPreference","controlsDisabled","PageHeader","SectionGroup","Section","FilterArea","FilterLine","Filter","FormField","FormFieldLayout","FILTER_FIELD_STYLE","RadioGroup","readEnabledSegmentValue","event","readEnabledSegmentValueId","readDigestMode","Typography","value","enabled"],"sources":["../../src/views/settings/notifications/notification-settings.module.scss","../../src/views/settings/notifications/SettingsNotificationsView.tsx"],"sourcesContent":[".preferenceFilter {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.segmentFilterControl {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n min-width: 0;\n white-space: nowrap;\n\n :global(.mzn-input-check-group--segmented) {\n align-self: flex-start;\n transform: translateY(-7px);\n }\n}\n\n.segmentFilterLabel {\n flex: 0 0 auto;\n color: var(--mzn-color-text-primary, #1f2937);\n font-size: 13px;\n line-height: 16px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n CSSProperties,\n ReactElement,\n useEffect,\n useState,\n} from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n PageHeader,\n RadioGroup,\n Section,\n SectionGroup,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport { useAuth } from '../../../lib/auth-provider';\nimport {\n NotificationDigestMode,\n NotificationPreferenceRecord,\n readNotificationPreference,\n updateNotificationPreference,\n} from '@rytass/bpm-core-client/workflow';\nimport styles from './notification-settings.module.scss';\n\ninterface DigestOption {\n readonly id: NotificationDigestMode;\n readonly name: string;\n}\n\ntype EnabledSegmentValue = 'OFF' | 'ON';\n\ninterface EnabledSegmentOption {\n readonly id: EnabledSegmentValue;\n readonly name: string;\n}\n\nconst DIGEST_OPTIONS: readonly DigestOption[] = [\n { id: 'INSTANT', name: '即時通知' },\n { id: 'DAILY', name: '每日摘要' },\n];\n\nconst ENABLED_SEGMENT_OPTIONS: readonly EnabledSegmentOption[] = [\n { id: 'ON', name: '開' },\n { id: 'OFF', name: '關' },\n];\n\nconst DEFAULT_PREFERENCE: NotificationPreferenceRecord = {\n emailDigestMode: 'INSTANT',\n emailEnabled: true,\n inAppEnabled: true,\n memberId: '',\n quietHoursEnd: null,\n quietHoursStart: null,\n updatedAt: '',\n};\n\n\nexport function SettingsNotificationsView(): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [preference, setPreference] =\n useState<NotificationPreferenceRecord>(DEFAULT_PREFERENCE);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect((): void => {\n if (!currentMemberId) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n readNotificationPreference(currentMemberId)\n .then((nextPreference): void => {\n setPreference(nextPreference);\n })\n .catch((requestError: unknown): void => {\n setError(readErrorMessage(requestError));\n })\n .finally((): void => {\n setLoading(false);\n });\n }, [currentMemberId]);\n\n async function handlePreferenceChange(\n nextPreference: NotificationPreferenceRecord,\n ): Promise<void> {\n if (!currentMemberId || saving) {\n return;\n }\n\n const previousPreference = preference;\n setPreference(nextPreference);\n setSaving(true);\n\n try {\n setPreference(\n await updateNotificationPreference({\n emailDigestMode: nextPreference.emailDigestMode,\n emailEnabled: nextPreference.emailEnabled,\n inAppEnabled: nextPreference.inAppEnabled,\n memberId: currentMemberId,\n quietHoursEnd: nextPreference.quietHoursEnd,\n quietHoursStart: nextPreference.quietHoursStart,\n }),\n );\n } catch (requestError: unknown) {\n setPreference(previousPreference);\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const controlsDisabled = loading || saving;\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"調整站內通知、Email 通知與摘要頻率。\"\n title=\"通知設定\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.preferenceFilter} isDirty={false}>\n <FilterLine>\n <Filter minWidth={160} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"inAppEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n 站內通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"inAppEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n inAppEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.inAppEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={180} span={1}>\n <FormField\n layout={FormFieldLayout.VERTICAL}\n name=\"emailEnabled\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 通知\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailEnabled\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailEnabled: readEnabledSegmentValue(\n event.target.value,\n ),\n });\n }}\n options={[...ENABLED_SEGMENT_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={readEnabledSegmentValueId(\n preference.emailEnabled,\n )}\n />\n </div>\n </FormField>\n </Filter>\n <Filter minWidth={280} span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"emailDigestMode\"\n style={FILTER_FIELD_STYLE}\n >\n <div className={styles.segmentFilterControl}>\n <span className={styles.segmentFilterLabel}>\n Email 頻率\n </span>\n <RadioGroup\n disabled={controlsDisabled}\n name=\"emailDigestMode\"\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n void handlePreferenceChange({\n ...preference,\n emailDigestMode: readDigestMode(\n event.target.value,\n ),\n });\n }}\n options={[...DIGEST_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={preference.emailDigestMode}\n />\n </div>\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 偏好設定會立即生效。\n </Typography>\n )}\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nconst FILTER_FIELD_STYLE = {\n minWidth: 0,\n whiteSpace: 'nowrap',\n} satisfies CSSProperties;\n\nfunction readDigestMode(value: unknown): NotificationDigestMode {\n return value === 'DAILY' ? 'DAILY' : 'INSTANT';\n}\n\nfunction readEnabledSegmentValue(value: unknown): boolean {\n return value !== 'OFF';\n}\n\nfunction readEnabledSegmentValueId(enabled: boolean): EnabledSegmentValue {\n return enabled ? 'ON' : 'OFF';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":";;;;;;;;;;;;A,GC2CMM,IAA0C,CAC9C;CAAE,IAAI;CAAW,MAAM;AAAO,GAC9B;CAAE,IAAI;CAAS,MAAM;AAAO,CAC9B,GAEMC,IAA2D,CAC/D;CAAE,IAAI;CAAM,MAAM;AAAI,GACtB;CAAE,IAAI;CAAO,MAAM;AAAI,CACzB,GAEMC,IAAmD;CACvD,iBAAiB;CACjB,cAAc;CACd,cAAc;CACd,UAAU;CACV,eAAe;CACf,iBAAiB;CACjB,WAAW;AACb;AAGA,SAAgBC,IAA0C;CACxD,IAAM,EAAE,cAAWP,EAAQ,GACrBQ,IAAkBC,GAAQ,YAAY,MACtC,CAACC,GAAYC,KACjBC,EAAuCN,CAAkB,GACrD,CAACO,GAASC,KAAcF,EAAS,EAAI,GACrC,CAACG,GAAQC,KAAaJ,EAAS,EAAK,GACpC,CAACK,GAAOC,KAAYN,EAAwB,IAAI;CAEtD,QAAsB;EACpB,AAAKJ,MAILM,EAAW,EAAI,GACfI,EAAS,IAAI,GAEbC,EAA2BX,CAAe,CAAC,CACxC,MAAM,MAAyB;GAC9B,EAAcY,CAAc;EAC9B,CAAC,CAAC,CACD,OAAO,MAAgC;GACtC,EAASC,EAAiBC,CAAY,CAAC;EACzC,CAAC,CAAC,CACD,cAAoB;GACnB,EAAW,EAAK;EAClB,CAAC;CACL,GAAG,CAACd,CAAe,CAAC;CAEpB,eAAee,EACb,GACe;EACf,IAAI,CAACf,KAAmBO,GACtB;EAGF,IAAMS,IAAqBd;EAE3B,AADAC,EAAcS,CAAc,GAC5BJ,EAAU,EAAI;EAEd,IAAI;GACF,EACE,MAAMS,EAA6B;IACjC,iBAAiBL,EAAe;IAChC,cAAcA,EAAe;IAC7B,cAAcA,EAAe;IAC7B,UAAUZ;IACV,eAAeY,EAAe;IAC9B,iBAAiBA,EAAe;GAClC,CAAC,CACH;EACF,SAASE,GAAuB;GAE9B,AADAX,EAAca,CAAkB,GAChCN,EAASG,EAAiBC,CAAY,CAAC;EACzC,UAAU;GACR,EAAU,EAAK;EACjB;CACF;CAEA,IAAMI,IAAmBb,KAAWE;CAEpC,OACE,kBAAAjB,GAAA,EAAA,UAAA,CACI,kBAAC6B,GAAD,EAAA,UACE,kBAACzB,GAAD;EACE,aAAY;EACZ,OAAM;CACP,CAAA,EACS,CAAA,GAEZ,kBAAC0B,GAAD,EAAA,UACE,kBAACC,GAAD;EACE,YACE,kBAACC,GAAD;GAAY,WAAW/B,EAAO;GAAkB,SAAS;GACvD,UAAA,kBAACgC,GAAD,EAAA,UAAA;IACE,kBAACC,GAAD;KAAQ,UAAU;KAAK,MAAM;KAC3B,UAAA,kBAACC,GAAD;MACE,QAAQC,EAAgB;MACxB,MAAK;MACL,OAAOC;MAEP,UAAA,kBAAC,OAAD;OAAK,WAAWpC,EAAO;OAAvB,UAAA,CACE,kBAAC,QAAD;QAAM,WAAWA,EAAO;QAAoB,UAAA;OAEtC,CAAA,GACN,kBAACqC,GAAD;QACE,UAAUV;QACV,MAAK;QACL,WACE,MACS;SACT,EAA4B;UAC1B,GAAGhB;UACH,cAAc2B,EACZC,EAAM,OAAO,KACf;SACF,CAAC;QACH;QACA,SAAS,CAAC,GAAGjC,CAAuB;QACpC,MAAK;QACL,MAAK;QACL,OAAOkC,EACL7B,EAAW,YACb;OACD,CAAA,CACE;;KACI,CAAA;IACL,CAAA;IACR,kBAACsB,GAAD;KAAQ,UAAU;KAAK,MAAM;KAC3B,UAAA,kBAACC,GAAD;MACE,QAAQC,EAAgB;MACxB,MAAK;MACL,OAAOC;MAEP,UAAA,kBAAC,OAAD;OAAK,WAAWpC,EAAO;OAAvB,UAAA,CACE,kBAAC,QAAD;QAAM,WAAWA,EAAO;QAAoB,UAAA;OAEtC,CAAA,GACN,kBAACqC,GAAD;QACE,UAAUV;QACV,MAAK;QACL,WACE,MACS;SACT,EAA4B;UAC1B,GAAGhB;UACH,cAAc2B,EACZC,EAAM,OAAO,KACf;SACF,CAAC;QACH;QACA,SAAS,CAAC,GAAGjC,CAAuB;QACpC,MAAK;QACL,MAAK;QACL,OAAOkC,EACL7B,EAAW,YACb;OACD,CAAA,CACE;;KACI,CAAA;IACL,CAAA;IACR,kBAACsB,GAAD;KAAQ,UAAU;KAAK,MAAM;KAC3B,UAAA,kBAACC,GAAD;MACE,WAAA;MACA,QAAQC,EAAgB;MACxB,MAAK;MACL,OAAOC;MAEP,UAAA,kBAAC,OAAD;OAAK,WAAWpC,EAAO;OAAvB,UAAA,CACE,kBAAC,QAAD;QAAM,WAAWA,EAAO;QAAoB,UAAA;OAEtC,CAAA,GACN,kBAACqC,GAAD;QACE,UAAUV;QACV,MAAK;QACL,WACE,MACS;SACT,EAA4B;UAC1B,GAAGhB;UACH,iBAAiB8B,EACfF,EAAM,OAAO,KACf;SACF,CAAC;QACH;QACA,SAAS,CAAC,GAAGlC,CAAc;QAC3B,MAAK;QACL,MAAK;QACL,OAAOM,EAAW;OACnB,CAAA,CACE;;KACI,CAAA;IACL,CAAA;GACE,EAAA,CAAA;EACF,CAAA;EAGb,UAAAO,IACC,kBAACwB,GAAD;GAAY,OAAM;GAAa,SAAQ;GACpC,UAAAxB;EACS,CAAA,IAEZ,kBAACwB,GAAD;GAAY,OAAM;GAAe,SAAQ;GAAO,UAAA;EAEpC,CAAA;CAEP,CAAA,EACG,CAAA,CACd,EAAA,CAAA;AAER;AAEA,IAAMN,IAAqB;CACzB,UAAU;CACV,YAAY;AACd;AAEA,SAASK,EAAe,GAAwC;CAC9D,OAAOE,MAAU,UAAU,UAAU;AACvC;AAEA,SAASL,EAAwB,GAAyB;CACxD,OAAOK,MAAU;AACnB;AAEA,SAASH,EAA0B,GAAuC;CACxE,OAAOI,IAAU,OAAO;AAC1B;AAEA,SAAStB,EAAiB,GAAwB;CAChD,OAAOJ,aAAiB,QAAQA,EAAM,UAAU;AAClD"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";require('../style.css');require('../orgs.css');const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./bpm-form-field-Bc6k4ZEO.cjs"),n=require("./admin-pickers-BjbkBpSJ.cjs");;/* empty css */const r=require("./format-member-label-t7ANTF62.cjs");let i=require("react"),a=require("@mezzanine-ui/react"),o=require("@rytass/bpm-core-client"),s=require("react/jsx-runtime"),c=require("@mezzanine-ui/core/form"),l=require("@mezzanine-ui/react/ContentHeader");l=e.t(l,1);let u=require("@mezzanine-ui/icons"),d=require("@rytass/bpm-core-client/organization"),f=require("@xyflow/react"),p=require("dagre");p=e.t(p,1);function m(e){return new Map(e.map(e=>[e.id,e.parentId]))}function h({orgUnitId:e,parentDraft:t,parentId:n}){let r=n===e?e:n,i=_({orgUnitId:e,parentDraft:t,parentId:r});if(i)return{message:i,parentDraft:t,status:`INVALID`};if((t.get(e)??null)===r)return{message:`父子關係沒有變更。`,parentDraft:t,status:`UNCHANGED`};let a=new Map(t);return a.set(e,r),{message:r?`已暫存新的上層組織。`:`已暫存為根節點。`,parentDraft:a,status:`UPDATED`}}function g({orgUnits:e,parentDraft:t}){return e.map(e=>{let n=t.get(e.id)??null;return n===e.parentId?null:{orgUnitId:e.id,parentId:n,previousParentId:e.parentId}}).filter(e=>!!e)}function _({orgUnitId:e,parentDraft:t,parentId:n}){return t.has(e)?n?t.has(n)?n===e?`組織不可成為自己的上層。`:v({orgUnitId:e,parentDraft:t,parentId:n})?`不可搬移到自己的下層組織。`:null:`找不到新的上層組織。`:null:`找不到要搬移的組織節點。`}function v({orgUnitId:e,parentDraft:t,parentId:n}){let r=new Set,i=n;for(;i;){if(i===e||r.has(i))return!0;r.add(i),i=t.get(i)??null}return!1}var y={orgTreeEditor:`bpm_orgTreeEditor_TMOtD`,orgTreeSummary:`bpm_orgTreeSummary_vpcV8`,orgTreeChangeList:`bpm_orgTreeChangeList_EzmOv`,orgTreeCanvas:`bpm_orgTreeCanvas_MeD7y`,orgTreePanel:`bpm_orgTreePanel_VFZph`,orgTreeNode:`bpm_orgTreeNode_bN4FS`,orgTreeNodeRoot:`bpm_orgTreeNodeRoot_Hr2KD`,orgTreeNodeSelected:`bpm_orgTreeNodeSelected_eRwtL`,orgTreeNodeChanged:`bpm_orgTreeNodeChanged_qPJg0`,orgTreeNodeDeleted:`bpm_orgTreeNodeDeleted_Ab0cI`,orgTreeNodeHeader:`bpm_orgTreeNodeHeader_LEXl-`,orgTreeNodeBadge:`bpm_orgTreeNodeBadge_zLc7t`,orgTreeNodeActions:`bpm_orgTreeNodeActions_cMldI`},b=`__org-tree-root__`,x=232,S=118,C=232,w=86,T=320,E=1.4,D=.25,ee=.005,te=250,O=80,ne=.85,re=.7,ie=1200,k=600,A=96,j={COMPANY:`公司`,DEPARTMENT:`部門`,DIVISION:`事業群`,TEAM:`小組`},ae={orgUnit:M},oe=(0,i.forwardRef)(function({onCreateChild:e,onCreateRoot:t,onEditOrgUnit:n,onSaveDraft:r,onStateChange:o,orgUnits:c},l){let[u,d]=(0,i.useState)(!1),[p,_]=(0,i.useState)(null),[v,C]=(0,i.useState)(null),[w,T]=(0,i.useState)(()=>m(c)),[A,j]=(0,i.useState)(()=>new Set),[oe,M]=(0,i.useState)(()=>new Map),[ce,ue]=(0,i.useState)([]),N=(0,i.useRef)(null),P=(0,i.useRef)(null),F=(0,i.useCallback)(e=>{P.current=e,j(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),I=(0,i.useCallback)(()=>{P.current=b,j(new Set)},[]),he=(0,i.useCallback)(()=>{P.current=b,j(()=>{let e=se(c,w);return new Set([...e.keys()].filter(e=>e!==b))})},[c,w]),L=(0,i.useMemo)(()=>new Map(c.map(e=>[e.id,e])),[c]),R=(0,i.useMemo)(()=>g({orgUnits:c,parentDraft:w}),[c,w]),z=(0,i.useMemo)(()=>le({collapsedIds:A,isEditing:u,measuredHeights:oe,onCreateChild:n=>{n?e(n):t()},onEditOrgUnit:e=>{let t=L.get(e);t&&n(t)},onToggleCollapse:F,orgUnits:c,orgUnitsById:L,parentDraft:w,selectedOrgUnitId:v}),[A,F,u,oe,e,t,n,c,L,w,v]),B=(0,i.useRef)(z);B.current=z;let V=R.length>0,H=(0,i.useMemo)(()=>{let e=Math.max(z.bounds.width,z.bounds.height);return e<=0?D:Math.min(D,Math.max(ee,te/e))},[z.bounds.height,z.bounds.width]),U=c.length<=O,W=(0,i.useCallback)(()=>{let e=N.current;if(!e)return;let{bounds:t,rootCenter:n}=B.current,r=t.width>0?ie/t.width:1/0,i=t.height>0?k/t.height:1/0,a=Math.min(r,i);if(!Number.isFinite(a)||a>=re){e.fitView({padding:.18});return}let o=k/(2*ne);e.setCenter(n.x,Math.min(t.height/2,o),{duration:0,zoom:ne})},[]),ge=(0,i.useCallback)(e=>{N.current=e,W()},[W]);(0,i.useImperativeHandle)(l,()=>({cancelEditing:be,saveDraft:xe,startEditing:ye})),(0,i.useEffect)(()=>{o({hasDraftChanges:V,isEditing:u})},[V,u,o]),(0,i.useEffect)(()=>{let e=m(c);T(e),j(new Set),M(new Map),C(null),_(null),d(!1)},[c]),(0,i.useEffect)(()=>{ue(z.nodes)},[z.nodes]),(0,i.useEffect)(()=>{if(typeof window>`u`)return()=>void 0;let e=window.requestAnimationFrame(()=>{W()});return()=>window.cancelAnimationFrame(e)},[W,c]),(0,i.useEffect)(()=>{let e=P.current;if(!e)return;P.current=null;let t=N.current,n=z.nodes.find(t=>t.id===e);t&&n&&t.setCenter(n.position.x+(n.width??x)/2,n.position.y+(n.initialHeight??S)/2,{duration:200,zoom:t.getZoom()})},[z.nodes]);let G=(0,i.useCallback)((e,t)=>{T(n=>{let r=h({orgUnitId:e,parentDraft:n,parentId:t});return _(r.message),r.parentDraft})},[]),_e=(0,i.useCallback)(e=>{if(!u||!e.target||!e.source)return;let t=e.source===b?null:e.source;e.target===b?_(`根節點不能搬移到其他節點下。`):G(e.target,t)},[G,u]),K=(0,i.useCallback)(e=>{let t=e.filter(e=>e.type===`dimensions`&&e.dimensions);t.length>0&&M(e=>{let n=new Map(e);return t.reduce((e,t)=>{if(t.type!==`dimensions`||!t.dimensions)return e;let r=Math.ceil(t.dimensions.height);return Math.abs((n.get(t.id)??0)-r)<=1?e:(n.set(t.id,r),!0)},!1)?n:e}),u&&ue(t=>(0,f.applyNodeChanges)(e,[...t]))},[u]),ve=(0,i.useCallback)((e,t,n)=>{if(!u||t.id===b)return;let r=fe(e,t.id)??de(t,n);r===void 0?_(`拖曳到目標父節點附近,或從父節點拉線到子節點。`):G(t.id,r===b?null:r)},[G,u]);function ye(){d(!0),T(m(c)),_(`已進入編輯模式,拖曳節點或拉線只會更新前端草稿。`)}function be(){d(!1),T(m(c)),_(`已取消草稿變更。`)}async function xe(){r?(await r(R),d(!1),_(`組織樹草稿已儲存。`)):_(`批次儲存 API 尚未接上,草稿仍保留在前端。`)}return(0,s.jsxs)(`div`,{className:y.orgTreeEditor,children:[(0,s.jsxs)(`div`,{className:y.orgTreeSummary,children:[(0,s.jsx)(a.Typography,{color:`text-neutral`,variant:`caption`,children:p??(V?`目前有 ${R.length} 筆父子關係草稿變更。`:`目前沒有草稿變更。`)}),V?(0,s.jsx)(`ul`,{className:y.orgTreeChangeList,children:R.map(e=>(0,s.jsxs)(`li`,{children:[me(e.orgUnitId,L),`:`,me(e.previousParentId,L),` -> `,me(e.parentId,L)]},e.orgUnitId))}):null]}),(0,s.jsx)(`div`,{className:y.orgTreeCanvas,children:(0,s.jsxs)(f.ReactFlow,{connectionMode:f.ConnectionMode.Strict,edges:[...z.edges],fitViewOptions:{minZoom:H,padding:.18},isValidConnection:e=>pe({source:e.source,target:e.target},w),maxZoom:E,minZoom:H,nodeTypes:ae,nodes:[...ce],nodesConnectable:u,nodesDraggable:u,onConnect:_e,onInit:ge,onNodeClick:(e,t)=>{C(t.id===b?null:t.id)},onNodeDoubleClick:(e,t)=>{if(t.id!==b){let e=L.get(t.id);e&&n(e)}},onNodeDragStop:ve,onNodesChange:K,onPaneClick:()=>C(null),panOnDrag:!0,proOptions:{hideAttribution:!0},children:[(0,s.jsx)(f.Background,{}),(0,s.jsx)(f.Controls,{}),(0,s.jsxs)(f.Panel,{className:`${y.orgTreePanel} nopan nodrag`,position:`top-right`,children:[(0,s.jsx)(a.Button,{onClick:I,size:`sub`,variant:`base-secondary`,children:`全部展開`}),(0,s.jsx)(a.Button,{onClick:he,size:`sub`,variant:`base-secondary`,children:`全部收合`})]}),U?(0,s.jsx)(f.MiniMap,{pannable:!0,zoomable:!0}):null]})})]})});function M({data:e,selected:t}){return(0,s.jsxs)(`div`,{className:[y.orgTreeNode,e.isSyntheticRoot?y.orgTreeNodeRoot:``,e.changed?y.orgTreeNodeChanged:``,e.deleted?y.orgTreeNodeDeleted:``,t?y.orgTreeNodeSelected:``].filter(Boolean).join(` `),children:[e.isSyntheticRoot?null:(0,s.jsx)(f.Handle,{id:`target`,isConnectable:e.isEditing,position:f.Position.Top,type:`target`}),(0,s.jsx)(f.Handle,{id:`source`,isConnectable:e.isEditing,position:f.Position.Bottom,type:`source`}),(0,s.jsxs)(`div`,{className:y.orgTreeNodeHeader,children:[(0,s.jsx)(a.Typography,{component:`span`,ellipsis:!0,title:e.name,variant:`label-primary`,children:e.name}),e.changed?(0,s.jsx)(`span`,{className:y.orgTreeNodeBadge,children:`草稿`}):null]}),(0,s.jsx)(a.Typography,{color:`text-neutral`,component:`span`,ellipsis:!0,title:e.code,variant:`caption`,children:e.isSyntheticRoot?`根節點容器`:`${e.typeLabel} · ${e.code}`}),e.isSyntheticRoot?null:(0,s.jsxs)(a.Typography,{color:`text-neutral`,component:`span`,ellipsis:!0,title:e.parentLabel,variant:`caption`,children:[`上層:`,e.parentLabel]}),(0,s.jsxs)(`div`,{className:`${y.orgTreeNodeActions} nodrag nopan`,children:[e.childCount>0?(0,s.jsx)(a.Button,{onClick:t=>{t.stopPropagation(),e.onToggleCollapse(e.orgUnitId??b)},size:`sub`,variant:`base-secondary`,children:e.collapsed?`展開 (${e.childCount})`:`收合 (${e.childCount})`}):null,e.isSyntheticRoot?(0,s.jsx)(a.Button,{icon:u.PlusIcon,iconType:`leading`,onClick:()=>e.onCreateChild(null),size:`sub`,variant:`base-secondary`,children:`新增根節點`}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.Button,{icon:u.EditIcon,iconType:`leading`,onClick:()=>{e.orgUnitId&&e.onEdit(e.orgUnitId)},size:`sub`,variant:`base-secondary`,children:`編輯`}),(0,s.jsx)(a.Button,{icon:u.PlusIcon,iconType:`leading`,onClick:()=>e.onCreateChild(e.orgUnitId),size:`sub`,variant:`base-secondary`,children:`新增子節點`})]})]})]})}function se(e,t){let n=new Map;return e.forEach(e=>{let r=t.get(e.id)??b;n.set(r,[...n.get(r)??[],e.id])}),n}function ce({childrenMap:e,collapsedIds:t}){let n=new Set,r=i=>{t.has(i)||(e.get(i)??[]).forEach(e=>{n.add(e),r(e)})};return r(b),n}function le({collapsedIds:e,isEditing:t,measuredHeights:n,onCreateChild:r,onEditOrgUnit:i,onToggleCollapse:a,orgUnits:o,orgUnitsById:s,parentDraft:c,selectedOrgUnitId:l}){let u=se(o,c),d=ce({childrenMap:u,collapsedIds:e}),f=o.filter(e=>d.has(e.id)),m=e=>u.get(e)?.length??0,h=(e,t)=>Math.max(t,n.get(e)??0),g=new p.graphlib.Graph;g.setDefaultEdgeLabel(()=>({})),g.setGraph({marginx:36,marginy:36,nodesep:44,rankdir:`TB`,ranksep:A}),g.setNode(b,{height:h(b,w),width:C}),f.forEach(e=>{g.setNode(e.id,{height:h(e.id,S),width:x})}),f.forEach(e=>{let t=c.get(e.id)??null;g.setEdge(t??b,e.id)}),p.layout(g);let _=g.graph(),v={height:_.height??0,width:_.width??0},y=g.node(b),T={x:v.width>0?v.width/2:y?.x??0,y:y?.y??0},E={...ue({data:{changed:!1,childCount:m(b),code:b,collapsed:e.has(b),deleted:!1,isEditing:t,isSyntheticRoot:!0,name:`組織根節點`,onCreateChild:r,onEdit:i,onToggleCollapse:a,orgUnitId:null,parentLabel:``,path:``,typeLabel:``},graph:g,height:h(b,w),id:b,selected:l===null,width:C}),position:{x:T.x-C/2,y:T.y-h(b,w)/2}},D=f.map(n=>{let o=c.get(n.id)??null,u=me(o,s);return ue({data:{changed:o!==n.parentId,childCount:m(n.id),code:n.code,collapsed:e.has(n.id),deleted:!!n.deletedAt,isEditing:t,isSyntheticRoot:!1,name:n.name,onCreateChild:r,onEdit:i,onToggleCollapse:a,orgUnitId:n.id,parentLabel:u,path:n.path,typeLabel:he(n.type)},graph:g,height:h(n.id,S),id:n.id,selected:l===n.id,width:x})});return{bounds:v,edges:f.map(e=>{let n=c.get(e.id)??null,r=n!==e.parentId;return{animated:t&&r,data:{},id:`org-tree-edge-${n??`root`}-${e.id}`,source:n??b,sourceHandle:`source`,style:r?{stroke:`var(--mzn-color-primary, #0057ff)`,strokeWidth:2}:void 0,target:e.id,targetHandle:`target`,type:`smoothstep`}}),nodes:[E,...D],rootCenter:T}}function ue({data:e,graph:t,height:n,id:r,selected:i,width:a}){let o=t.node(r);return{data:e,id:r,initialHeight:n,initialWidth:a,position:o?{x:o.x-a/2,y:o.y-n/2}:{x:0,y:0},selected:i,sourcePosition:f.Position.Bottom,targetPosition:f.Position.Top,type:`orgUnit`,width:a}}function de(e,t){let n=F(e);return t.filter(t=>t.id!==e.id).map(e=>({distance:I(n,F(e)),id:e.id})).filter(e=>e.distance<=T).sort((e,t)=>e.distance-t.distance)[0]?.id}function fe(e,t){let n=N(e);if(n)return Array.from(document.querySelectorAll(`.react-flow__node[data-id]`)).map(e=>{let r=e.dataset.id;if(!r||r===t)return null;let i=e.getBoundingClientRect(),a={x:i.left+i.width/2,y:i.top+i.height/2};return{distance:I(n,a),id:r}}).filter(e=>!!e).filter(e=>e.distance<=T).sort((e,t)=>e.distance-t.distance)[0]?.id}function N(e){return P(e)?{x:e.clientX,y:e.clientY}:null}function P(e){return typeof e==`object`&&!!e&&`clientX`in e&&`clientY`in e&&typeof e.clientX==`number`&&typeof e.clientY==`number`}function F(e){return{x:e.position.x+(e.width??x)/2,y:e.position.y+(e.measured?.height??e.initialHeight??S)/2}}function I(e,t){return Math.hypot(e.x-t.x,e.y-t.y)}function pe(e,t){return!e.source||!e.target||e.target===b?!1:_({orgUnitId:e.target,parentDraft:t,parentId:e.source===b?null:e.source})===null}function me(e,t){if(!e)return`根節點`;let n=t.get(e);return n?`${n.name} · ${n.code}`:`未知組織`}function he(e){return j[e.toUpperCase()]??`未知類型`}var L={tableIntroActions:`bpm_tableIntroActions_WO4XU`,modalFields:`bpm_modalFields_juyv6`,tableIntro:`bpm_tableIntro_u3hcm`,tableFrame:`bpm_tableFrame_IdbmB`,orgFilterArea:`bpm_orgFilterArea_Xjbbp`,membershipFilterArea:`bpm_membershipFilterArea_zob-Y`,scopeLabel:`bpm_scopeLabel_TLHMC`},R={hasDraftChanges:!1,isEditing:!1},z=100,B=[{id:`COMPANY`,name:`公司`},{id:`DIVISION`,name:`事業群`},{id:`DEPARTMENT`,name:`部門`},{id:`TEAM`,name:`小組`}],V={id:`ALL`,name:`全部類型`},H=[V,...B],U=[{id:`MEMBER`,name:`指定會員`},{id:`ORG_UNIT`,name:`指定組織`},{id:`POSITION`,name:`指定職位`}],W={id:`ALL`,name:`全部範圍`},ge=[W,...U],G={activeOnly:!1,id:`ALL`,name:`全部狀態`},_e=[G,{activeOnly:!0,id:`ACTIVE`,name:`目前有效`}],K=[{id:`true`,name:`主要歸屬`,value:!0},{id:`false`,name:`一般歸屬`,value:!1}],ve=[10,20,50],ye=1368,be=948,xe=1292,Se=1124;function Ce(){let[e,r]=(0,i.useState)(`ORG_UNITS`),[c,u]=(0,i.useState)(null),[f,p]=(0,i.useState)(null),[m,h]=(0,i.useState)(null),[g,_]=(0,i.useState)(!0),[v,y]=(0,i.useState)(null),[b,x]=(0,i.useState)(G),[S,C]=(0,i.useState)(1),[w,T]=(0,i.useState)(10),[E,D]=(0,i.useState)(W),[ee,te]=(0,i.useState)(0),[O,ne]=(0,i.useState)([]),[re,ie]=(0,i.useState)(null),[k,A]=(0,i.useState)(null),[j,ae]=(0,i.useState)(null),[oe,M]=(0,i.useState)(1),[se,ce]=(0,i.useState)(10),[le,ue]=(0,i.useState)(null),[de,fe]=(0,i.useState)(0),[N,P]=(0,i.useState)(null),[F,I]=(0,i.useState)(1),[pe,me]=(0,i.useState)(10),[he,L]=(0,i.useState)(``),[R,B]=(0,i.useState)(0),[H,U]=(0,i.useState)(V),[ge,_e]=(0,i.useState)(`TABLE`),[K,ve]=(0,i.useState)([]),[ye,be]=(0,i.useState)([]),[xe,Se]=(0,i.useState)([]),[Ce,q]=(0,i.useState)(null),[Oe,ke]=(0,i.useState)(1),[Ae,je]=(0,i.useState)(10),[J,Ie]=(0,i.useState)(``),[We,Ge]=(0,i.useState)(0),[Ke,qe]=(0,i.useState)([]),[Je,Ye]=(0,i.useState)([]),[Xe,Ze]=(0,i.useState)([]),[Y,X]=(0,i.useState)(!1),[Z,Q]=(0,i.useState)(null),[$e,et]=(0,i.useState)(null);(0,i.useEffect)(()=>{c&&p(c)},[c]),(0,i.useEffect)(()=>{let e=!1;return Q(null),et(null),c?.type===`ORG_UNIT`&&(0,d.readOrgUnitRetirementImpact)(c.id).then(t=>{e||Q(t)}).catch(t=>{e||h(nt(t))}),()=>{e=!0}},[c]);let tt=c?.type===`ORG_UNIT`,rt=tt&&Z!==null&&(Z.openMembershipCount>0||Z.openManagerResolutionCount>0),it=tt&&(Z===null||Z.childUnitCount>0||rt&&!$e),at=c??f,ot=(0,i.useCallback)(async()=>{_(!0),h(null);try{let e=await(0,d.readOrganizationDashboard)({managerActiveOnly:b.activeOnly,managerPage:S,managerPageSize:w,managerScopeType:E.id===`ALL`?null:E.id,membershipActiveOnly:re?.activeOnly??!1,membershipOrgUnitId:j?.id??null,membershipPage:oe,membershipPageSize:se,membershipPositionId:le?.id??null,orgUnitPage:F,orgUnitPageSize:pe,orgUnitSearchText:he,orgUnitType:H.id===`ALL`?null:H.id,positionPage:Oe,positionPageSize:Ae,positionSearchText:J}),t=Le(e.memberships,e.managerResolutions),n=await(0,o.resolveMembers)(t);ne(n),ve(e.orgUnits),B(e.orgUnitCount),be(e.filteredOrgUnits),qe(e.positions),Ge(e.positionCount),Se(e.filteredPositions),fe(e.membershipCount),Ze(e.filteredMemberships),te(e.managerResolutionCount),Ye(e.filteredManagerResolutions)}catch(e){h(nt(e))}finally{_(!1)}},[b,S,w,E,re,j,oe,se,le,F,pe,he,H,Oe,Ae,J]);(0,i.useEffect)(()=>{ot()},[ot]);let $=(0,i.useMemo)(()=>new Map(K.map(e=>[e.id,e])),[K]),st=(0,i.useMemo)(()=>new Map(Ke.map(e=>[e.id,e])),[Ke]),ct=(0,i.useMemo)(()=>new Map(O.map(e=>[e.memberId,e])),[O]),lt=(0,i.useMemo)(()=>ye.map(e=>({...e,key:e.id,parentName:e.parentId?Be($.get(e.parentId)):`根節點`,typeLabel:Ve(e.type)})),[ye,$]),ut=(0,i.useMemo)(()=>xe.map(e=>({...e,key:e.id})),[xe]),dt=(0,i.useMemo)(()=>Xe.map(e=>({...e,key:e.id,memberName:ze(ct.get(e.memberId)),orgUnitName:Be($.get(e.orgUnitId)),positionName:e.positionId?He(st.get(e.positionId)):`未指定`})),[ct,Xe,$,st]),ft=(0,i.useMemo)(()=>Je.map(e=>({...e,key:e.id,managerName:ze(ct.get(e.managerMemberId)),scopeLabel:Re(e,{membersById:ct,orgUnitsById:$,positionsById:st})})),[Je,ct,$,st]),pt=(0,i.useMemo)(()=>({render:e=>[{name:`編輯`,onClick:()=>P({record:e,type:`EDIT`})},{name:`停用`,onClick:()=>u({confirmText:`停用組織`,description:`停用「${e.name}」後,這個組織節點將不再出現在可用組織清單中。`,id:e.id,title:`停用組織節點`,type:`ORG_UNIT`}),variant:`destructive-secondary`}],variant:`base-secondary`,width:128}),[]),mt=(0,i.useMemo)(()=>({render:e=>[{name:`編輯`,onClick:()=>q({record:e,type:`EDIT`})},{name:`刪除`,onClick:()=>u({confirmText:`刪除職位`,description:`刪除職位「${e.name}」。若仍有會員歸屬、主管解析規則、簽核範本或進行中單據的加簽對象引用它,系統會拒絕刪除並說明原因。`,id:e.id,title:`刪除職位`,type:`POSITION`}),variant:`destructive-secondary`}],variant:`base-secondary`,width:128}),[]),ht=(0,i.useMemo)(()=>({render:e=>[{name:`編輯`,onClick:()=>A({record:e,type:`EDIT`})},{name:`刪除`,onClick:()=>u({confirmText:`刪除歸屬`,description:`刪除「${e.memberName}」在「${e.orgUnitName}」的會員歸屬。`,id:e.id,title:`刪除會員歸屬`,type:`MEMBERSHIP`}),variant:`destructive-secondary`}],variant:`base-secondary`,width:128}),[]),gt=(0,i.useMemo)(()=>({render:e=>[{name:`編輯`,onClick:()=>y({record:e,type:`EDIT`})},{name:`刪除`,onClick:()=>u({confirmText:`刪除主管規則`,description:`刪除「${e.scopeLabel}」指派給「${e.managerName}」的主管解析規則。`,id:e.id,title:`刪除主管解析規則`,type:`MANAGER_RESOLUTION`}),variant:`destructive-secondary`}],variant:`base-secondary`,width:128}),[]);function _t(e){I(1),L(e)}function vt(e){I(1),U(e)}function yt(e){ke(1),Ie(e)}function bt(e){M(1),ie(e)}function xt(e){M(1),ae(e)}function St(e){M(1),ue(e)}function Ct(e){C(1),x(e)}function wt(e){C(1),D(e)}function Tt(){Y||u(null)}async function Et(){c&&await Ot(async()=>{c.type===`ORG_UNIT`&&await(0,d.deleteOrgUnit)(c.id,{transferToOrgUnitId:rt?$e?.id??null:null}),c.type===`POSITION`&&await(0,d.deletePosition)(c.id),c.type===`MEMBERSHIP`&&await(0,d.deleteMembership)(c.id),c.type===`MANAGER_RESOLUTION`&&await(0,d.deleteManagerResolution)(c.id),u(null)})}async function Dt(e){X(!0),h(null);try{await(0,d.commitOrgUnitTreeDraft)({moves:e.map(e=>({baseUpdatedAt:$.get(e.orgUnitId)?.updatedAt??``,id:e.orgUnitId,parentId:e.parentId}))}),await ot()}catch(e){throw h(nt(e)),e}finally{X(!1)}}async function Ot(e){X(!0),h(null);try{await e(),await ot()}catch(e){h(nt(e))}finally{X(!1)}}return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.PageHeader,{children:(0,s.jsx)(l.default,{description:`維護組織樹、職位、會員歸屬與簽核主管解析規則。`,title:`組織管理`})}),(0,s.jsx)(a.SectionGroup,{children:(0,s.jsxs)(a.Section,{tab:(0,s.jsxs)(a.Tab,{activeKey:e,onChange:e=>r(Ue(e)),children:[(0,s.jsx)(a.TabItem,{children:`組織樹`},`ORG_UNITS`),(0,s.jsx)(a.TabItem,{children:`職位`},`POSITIONS`),(0,s.jsx)(a.TabItem,{children:`會員歸屬`},`MEMBERSHIPS`),(0,s.jsx)(a.TabItem,{children:`簽核主管`},`MANAGERS`)]}),children:[m?(0,s.jsx)(a.Typography,{color:`text-error`,variant:`body`,children:m}):null,e===`ORG_UNITS`?(0,s.jsx)(we,{actions:pt,loading:g,onCreate:()=>P({parentId:null,record:null,type:`CREATE`}),onCreateChild:e=>P({parentId:e,record:null,type:`CREATE`}),onEditOrgUnit:e=>P({record:e,type:`EDIT`}),onPageChange:I,onSaveDraft:Dt,onPageSizeChange:e=>{I(1),me(e)},onSearchTextChange:_t,onTypeFilterChange:vt,orgUnits:K,page:F,pageSize:pe,rows:lt,searchText:he,saving:Y,total:R,typeFilter:H,viewMode:ge,onViewModeChange:_e}):null,e===`POSITIONS`?(0,s.jsx)(Te,{actions:mt,loading:g,onCreate:()=>q({record:null,type:`CREATE`}),onPageChange:ke,onPageSizeChange:e=>{ke(1),je(e)},onSearchTextChange:yt,page:Oe,pageSize:Ae,rows:ut,searchText:J,total:We}):null,e===`MEMBERSHIPS`?(0,s.jsx)(Ee,{actions:ht,loading:g,onCreate:()=>A({record:null,type:`CREATE`}),onActiveFilterChange:bt,onOrgUnitFilterChange:xt,onPageChange:M,onPageSizeChange:e=>{M(1),ce(e)},onPositionFilterChange:St,orgUnitFilter:j,orgUnits:K,page:oe,pageSize:se,positionFilter:le,positions:Ke,rows:dt,statusFilter:re,total:de}):null,e===`MANAGERS`?(0,s.jsx)(De,{actions:gt,loading:g,onCreate:()=>y({record:null,type:`CREATE`}),onActiveFilterChange:Ct,onPageChange:C,onPageSizeChange:e=>{C(1),T(e)},onScopeTypeFilterChange:wt,page:S,pageSize:w,rows:ft,scopeTypeFilter:E,statusFilter:b,total:ee}):null]})}),(0,s.jsx)(Me,{modal:N,onClose:()=>P(null),onSubmit:e=>Ot(async()=>{N?.type===`EDIT`&&N.record?await(0,d.updateOrgUnit)({...e,id:N.record.id,metadataJson:null}):await(0,d.createOrgUnit)({code:e.code??``,metadataJson:`{}`,name:e.name??``,parentId:e.parentId,type:e.type??`DEPARTMENT`}),P(null)}),orgUnits:K,saving:Y}),(0,s.jsx)(Ne,{modal:Ce,onClose:()=>q(null),onSubmit:e=>Ot(async()=>{Ce?.type===`EDIT`&&Ce.record?await(0,d.updatePosition)({...e,id:Ce.record.id,metadataJson:null}):await(0,d.createPosition)({code:e.code??``,level:e.level??0,metadataJson:`{}`,name:e.name??``}),q(null)}),saving:Y}),(0,s.jsx)(Pe,{membersById:ct,modal:k,onClose:()=>A(null),onSubmit:e=>Ot(async()=>{k?.type===`EDIT`&&k.record?await(0,d.updateMembership)({effectiveFrom:e.effectiveFrom,effectiveTo:e.effectiveTo,id:k.record.id,isPrimary:e.isPrimary,orgUnitId:e.orgUnitId,positionId:e.positionId}):await(0,d.createMembership)({effectiveFrom:e.effectiveFrom??Qe(),effectiveTo:e.effectiveTo,isPrimary:e.isPrimary??!1,memberId:e.memberId??``,orgUnitId:e.orgUnitId??``,positionId:e.positionId}),A(null)}),orgUnits:K,positions:Ke,saving:Y}),(0,s.jsx)(Fe,{membersById:ct,modal:v,onClose:()=>y(null),onSubmit:e=>Ot(async()=>{v?.type===`EDIT`&&v.record?await(0,d.updateManagerResolution)({...e,id:v.record.id}):await(0,d.createManagerResolution)({effectiveFrom:e.effectiveFrom??Qe(),effectiveTo:e.effectiveTo,managerMemberId:e.managerMemberId??``,priority:e.priority??z,scopeId:e.scopeId??``,scopeType:e.scopeType??`MEMBER`}),y(null)}),orgUnits:K,positions:Ke,saving:Y}),(0,s.jsxs)(a.Modal,{cancelText:`取消`,confirmButtonProps:{disabled:it,variant:`destructive-primary`},confirmText:rt?`轉移並停用`:at?.confirmText??``,loading:Y,modalStatusType:`error`,modalType:`standard`,onCancel:Tt,onClose:Tt,onConfirm:()=>void Et(),open:!!c,showModalFooter:!0,showModalHeader:!0,size:`regular`,supportingText:`此操作會立即套用,請確認後再繼續。`,title:at?.title??``,children:[(0,s.jsx)(a.Typography,{color:`text-neutral`,variant:`body`,children:at?.description??``}),tt&&Z===null&&!m?(0,s.jsx)(a.Typography,{color:`text-neutral`,variant:`body`,children:`正在確認這個組織目前的歸屬與主管規則…`}):null,tt&&Z&&Z.childUnitCount>0?(0,s.jsx)(a.Typography,{color:`text-error`,variant:`body`,children:`這個組織底下還有 ${Z.childUnitCount} 個子組織,請先移動或停用子組織。`}):null,rt&&Z?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.Typography,{color:`text-neutral`,variant:`body`,children:`這個組織還有 ${Z.openMembershipCount} 筆尚未結束的會員歸屬(含未來才生效的)、${Z.openManagerResolutionCount} 筆簽核主管規則。請選擇承接組織:已生效的歸屬會在昨天結束,並自今天起改掛到承接組織,保留歷史;尚未生效的歸屬直接改掛;這個組織的簽核主管規則會一併結束。`}),(0,s.jsx)(t.t,{label:`承接組織`,name:`successorOrgUnitId`,children:(0,s.jsx)(n.n,{name:`successorOrgUnitId`,onChange:et,orgUnits:K.filter(e=>e.id!==c?.id),placeholder:`選擇承接組織`,value:$e})})]}):null,m?(0,s.jsx)(a.Typography,{color:`text-error`,variant:`body`,children:m}):null]})]})}function we({actions:e,loading:t,onCreate:n,onCreateChild:r,onEditOrgUnit:o,onPageChange:l,onSaveDraft:d,onPageSizeChange:f,onSearchTextChange:p,onTypeFilterChange:m,onViewModeChange:h,orgUnits:g,page:_,pageSize:v,rows:y,searchText:b,saving:x,total:S,typeFilter:C,viewMode:w}){let T=(0,i.useRef)(null),[E,D]=(0,i.useState)(R),ee=w===`TABLE`?`FLOW`:`TABLE`,te=w===`TABLE`?`切換樹狀圖`:`切換表格`,O=w===`FLOW`,ne=Oe({isTreeMode:O,isTreeEditing:E.isEditing}),re=ke({isTreeMode:O,isTreeEditing:E.isEditing}),ie=Ae({hasDraftChanges:E.hasDraftChanges,isTreeEditing:E.isEditing,isTreeMode:O,loading:t,saving:x}),k=(0,i.useMemo)(()=>[{dataIndex:`code`,key:`code`,title:`代碼`,width:180},{dataIndex:`name`,key:`name`,title:`名稱`,width:240},{dataIndex:`typeLabel`,key:`typeLabel`,title:`類型`,width:120},{dataIndex:`parentName`,key:`parentName`,title:`上層`,width:280},{dataIndex:`path`,key:`path`,title:`Path`,width:420}],[]);return(0,i.useEffect)(()=>{w===`TABLE`&&D(R)},[w]),(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(je,{actionDisabled:ie,actionIcon:re,actionLabel:ne,actions:(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.Button,{onClick:()=>h(ee),variant:`base-secondary`,children:te}),O&&E.isEditing?(0,s.jsx)(a.Button,{disabled:x,icon:u.CloseIcon,iconType:`leading`,onClick:()=>T.current?.cancelEditing(),variant:`base-secondary`,children:`取消`}):null]}),description:`組織節點使用 ltree path 維護階層,搬移節點會同步更新子節點 path。`,onCreate:()=>{O?E.isEditing?T.current?.saveDraft():T.current?.startEditing():n()},title:`組織樹`}),w===`TABLE`?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.FilterArea,{className:L.orgFilterArea,size:`sub`,children:(0,s.jsxs)(a.FilterLine,{children:[(0,s.jsx)(a.Filter,{span:3,children:(0,s.jsx)(a.FormField,{fullWidth:!0,layout:c.FormFieldLayout.VERTICAL,name:`orgUnitSearchText`,children:(0,s.jsx)(a.Input,{fullWidth:!0,onChange:e=>p(e.target.value),placeholder:`搜尋組織名稱或代碼`,size:`sub`,value:b,variant:`base`})})}),(0,s.jsx)(a.Filter,{span:2,children:(0,s.jsx)(a.FormField,{fullWidth:!0,layout:c.FormFieldLayout.VERTICAL,name:`orgUnitTypeFilter`,children:(0,s.jsx)(a.Select,{clearable:!1,fullWidth:!0,onChange:e=>m(Ge(e)),options:[...H],placeholder:`類型`,size:`sub`,value:C})})})]})}),(0,s.jsx)(`div`,{className:L.tableFrame,children:(0,s.jsx)(a.Table,{actions:e,columns:k,dataSource:[...y],fullWidth:!0,loading:t,pagination:q({onPageChange:l,onPageSizeChange:f,page:_,pageSize:v,total:S}),style:{minWidth:ye}})})]}):(0,s.jsx)(oe,{ref:T,onCreateChild:r,onCreateRoot:n,onEditOrgUnit:o,onSaveDraft:d,onStateChange:D,orgUnits:g,saving:x})]})}function Te({actions:e,loading:t,onCreate:n,onPageChange:r,onPageSizeChange:o,onSearchTextChange:l,page:u,pageSize:d,rows:f,searchText:p,total:m}){let h=(0,i.useMemo)(()=>[{dataIndex:`code`,key:`code`,title:`代碼`,width:180},{dataIndex:`name`,key:`name`,title:`名稱`,width:280},{dataIndex:`level`,key:`level`,title:`職等`,width:96},{dataIndex:`updatedAt`,key:`updatedAt`,title:`更新時間`,width:220}],[]);return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(je,{actionLabel:`新增職位`,description:`職位提供會員歸屬與主管解析規則使用。`,onCreate:n,title:`職位`}),(0,s.jsx)(a.FilterArea,{className:L.orgFilterArea,size:`sub`,children:(0,s.jsx)(a.FilterLine,{children:(0,s.jsx)(a.Filter,{span:3,children:(0,s.jsx)(a.FormField,{fullWidth:!0,layout:c.FormFieldLayout.VERTICAL,name:`positionSearchText`,children:(0,s.jsx)(a.Input,{fullWidth:!0,onChange:e=>l(e.target.value),placeholder:`搜尋職位名稱或代碼`,size:`sub`,value:p,variant:`base`})})})})}),(0,s.jsx)(`div`,{className:L.tableFrame,children:(0,s.jsx)(a.Table,{actions:e,columns:h,dataSource:[...f],fullWidth:!0,loading:t,pagination:q({onPageChange:r,onPageSizeChange:o,page:u,pageSize:d,total:m}),style:{minWidth:be}})})]})}function Ee({actions:e,loading:t,onCreate:r,onActiveFilterChange:o,onOrgUnitFilterChange:l,onPageChange:u,onPageSizeChange:d,onPositionFilterChange:f,orgUnitFilter:p,orgUnits:m,page:h,pageSize:g,positionFilter:_,positions:v,rows:y,statusFilter:b,total:x}){let S=(0,i.useMemo)(()=>[{dataIndex:`memberName`,key:`memberName`,title:`會員`,width:280},{dataIndex:`orgUnitName`,key:`orgUnitName`,title:`組織`,width:280},{dataIndex:`positionName`,key:`positionName`,title:`職位`,width:220},{key:`isPrimary`,render:e=>e.isPrimary?`主要`:`一般`,title:`類型`,width:104},{dataIndex:`effectiveFrom`,key:`effectiveFrom`,title:`生效日`,width:140},{dataIndex:`effectiveTo`,key:`effectiveTo`,title:`結束日`,width:140}],[]),C=(0,i.useMemo)(()=>m.map(n.a),[m]),w=(0,i.useMemo)(()=>v.map(n.o),[v]);return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(je,{actionLabel:`新增歸屬`,description:`會員歸屬是 BPM 內部組織權限、主管解析與條件判斷的來源。`,onCreate:r,title:`會員歸屬`}),(0,s.jsx)(a.FilterArea,{className:[L.orgFilterArea,L.membershipFilterArea].join(` `),size:`sub`,children:(0,s.jsxs)(a.FilterLine,{children:[(0,s.jsx)(a.Filter,{span:2,children:(0,s.jsx)(a.FormField,{fullWidth:!0,layout:c.FormFieldLayout.VERTICAL,name:`membershipOrgUnitFilter`,children:(0,s.jsx)(a.AutoComplete,{disabledOptionsFilter:!0,emptyText:`沒有符合的組織`,inputProps:{autoCapitalize:`none`,autoCorrect:`off`,name:`membershipOrgUnitFilter`,spellCheck:!1},mode:`single`,name:`membershipOrgUnitFilter`,onChange:e=>l(Xe(e)),options:[...C],placeholder:`全部組織`,size:`sub`,value:p})})}),(0,s.jsx)(a.Filter,{span:2,children:(0,s.jsx)(a.FormField,{fullWidth:!0,layout:c.FormFieldLayout.VERTICAL,name:`membershipPositionFilter`,children:(0,s.jsx)(a.AutoComplete,{disabledOptionsFilter:!0,emptyText:`沒有符合的職位`,inputProps:{autoCapitalize:`none`,autoCorrect:`off`,name:`membershipPositionFilter`,spellCheck:!1},mode:`single`,name:`membershipPositionFilter`,onChange:e=>f(Ze(e)),options:[...w],placeholder:`全部職位`,size:`sub`,value:_})})}),(0,s.jsx)(a.Filter,{span:2,children:(0,s.jsx)(a.FormField,{fullWidth:!0,layout:c.FormFieldLayout.VERTICAL,name:`membershipStatusFilter`,children:(0,s.jsx)(a.AutoComplete,{disabledOptionsFilter:!0,emptyText:`沒有符合的狀態`,inputProps:{autoCapitalize:`none`,autoCorrect:`off`,name:`membershipStatusFilter`,spellCheck:!1},mode:`single`,name:`membershipStatusFilter`,onChange:e=>o(Ye(e)),options:[..._e],placeholder:`全部狀態`,size:`sub`,value:b})})})]})}),(0,s.jsx)(`div`,{className:L.tableFrame,children:(0,s.jsx)(a.Table,{actions:e,columns:S,dataSource:[...y],fullWidth:!0,loading:t,pagination:q({onPageChange:u,onPageSizeChange:d,page:h,pageSize:g,total:x}),style:{minWidth:xe}})})]})}function De({actions:e,loading:t,onCreate:n,onActiveFilterChange:r,onPageChange:o,onPageSizeChange:l,onScopeTypeFilterChange:u,page:d,pageSize:f,rows:p,scopeTypeFilter:m,statusFilter:h,total:g}){let _=(0,i.useMemo)(()=>[{dataIndex:`scopeLabel`,key:`scopeLabel`,title:`套用範圍`,width:320},{dataIndex:`managerName`,key:`managerName`,title:`簽核主管`,width:300},{dataIndex:`priority`,key:`priority`,title:`優先序`,width:96},{dataIndex:`effectiveFrom`,key:`effectiveFrom`,title:`生效日`,width:140},{dataIndex:`effectiveTo`,key:`effectiveTo`,title:`結束日`,width:140}],[]);return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(je,{actionLabel:`新增主管規則`,description:`簽核主管規則獨立於組織樹 parent,解析優先序為會員、組織、職位。`,onCreate:n,title:`簽核主管`}),(0,s.jsx)(a.FilterArea,{className:L.orgFilterArea,size:`sub`,children:(0,s.jsxs)(a.FilterLine,{children:[(0,s.jsx)(a.Filter,{span:3,children:(0,s.jsx)(a.FormField,{fullWidth:!0,layout:c.FormFieldLayout.VERTICAL,name:`managerScopeTypeFilter`,children:(0,s.jsx)(a.Select,{clearable:!1,fullWidth:!0,onChange:e=>u(qe(e)),options:[...ge],placeholder:`套用範圍`,size:`sub`,value:m})})}),(0,s.jsx)(a.Filter,{span:2,children:(0,s.jsx)(a.FormField,{fullWidth:!0,layout:c.FormFieldLayout.VERTICAL,name:`managerStatusFilter`,children:(0,s.jsx)(a.Select,{clearable:!1,fullWidth:!0,onChange:e=>r(Je(e)),options:[..._e],placeholder:`狀態`,size:`sub`,value:h})})})]})}),(0,s.jsx)(`div`,{className:L.tableFrame,children:(0,s.jsx)(a.Table,{actions:e,columns:_,dataSource:[...p],fullWidth:!0,loading:t,pagination:q({onPageChange:o,onPageSizeChange:l,page:d,pageSize:f,total:g}),style:{minWidth:Se}})})]})}function q({onPageChange:e,onPageSizeChange:t,page:n,pageSize:r,total:i}){return{current:n,onChange:e,onChangePageSize:t,pageSize:r,pageSizeLabel:`每頁筆數`,pageSizeOptions:[...ve],renderResultSummary:(e,t,n)=>`顯示 ${e}-${t} 筆,共 ${n} 筆`,showPageSizeOptions:!0,total:i}}function Oe({isTreeEditing:e,isTreeMode:t}){return t?e?`儲存`:`開始編輯`:`新增組織`}function ke({isTreeEditing:e,isTreeMode:t}){return t?e?u.SaveIcon:u.EditIcon:u.PlusIcon}function Ae({hasDraftChanges:e,isTreeEditing:t,isTreeMode:n,loading:r,saving:i}){return n?t?!e||i:r:!1}function je({actionDisabled:e=!1,actionIcon:t=u.PlusIcon,actionLabel:n,actions:r,description:i,onCreate:o,title:c}){return(0,s.jsxs)(`div`,{className:L.tableIntro,children:[(0,s.jsxs)(`div`,{children:[(0,s.jsx)(a.Typography,{component:`h2`,variant:`h3`,children:c}),(0,s.jsx)(a.Typography,{color:`text-neutral`,variant:`body`,children:i})]}),(0,s.jsxs)(`div`,{className:L.tableIntroActions,children:[r,(0,s.jsx)(a.Button,{disabled:e,icon:t,iconType:`leading`,onClick:o,children:n})]})]})}function Me({modal:e,onClose:r,onSubmit:o,orgUnits:c,saving:l}){let[u,d]=(0,i.useState)(``),[f,p]=(0,i.useState)(``),[m,h]=(0,i.useState)(null),[g,_]=(0,i.useState)(B[2]);return(0,i.useEffect)(()=>{if(!e)return;let t=e.record,r=t?.parentId??e.parentId??null,i=r?c.find(e=>e.id===r)??null:null;d(t?.code??``),p(t?.name??``),h(i?n.a(i):null),_(B.find(e=>e.id===t?.type)??B[2])},[e,c]),(0,s.jsx)(a.Modal,{cancelText:`取消`,confirmButtonProps:{disabled:!u||!f},confirmText:e?.type===`EDIT`?`儲存`:`建立`,loading:l,modalType:`standard`,onCancel:r,onClose:r,onConfirm:()=>void o({code:u,name:f,parentId:m?.id??null,type:g.id}),open:!!e,showModalFooter:!0,showModalHeader:!0,size:`regular`,title:e?.type===`EDIT`?`編輯組織`:`新增組織`,children:(0,s.jsxs)(`div`,{className:L.modalFields,children:[(0,s.jsx)(J,{label:`代碼`,name:`orgCode`,onChange:d,placeholder:`例如 FIN-TW`,value:u}),(0,s.jsx)(J,{label:`名稱`,name:`orgName`,onChange:p,placeholder:`例如 財務部`,value:f}),(0,s.jsx)(t.t,{label:`類型`,name:`orgType`,children:(0,s.jsx)(a.Select,{clearable:!1,fullWidth:!0,onChange:e=>_(We(e)),options:[...B],placeholder:`選擇組織類型`,value:g})}),(0,s.jsx)(t.t,{label:`上層組織`,name:`parentId`,children:(0,s.jsx)(n.n,{name:`parentId`,onChange:h,orgUnits:c.filter(t=>t.id!==e?.record?.id),placeholder:`選擇上層組織`,value:m})})]})})}function Ne({modal:e,onClose:t,onSubmit:n,saving:r}){let[o,c]=(0,i.useState)(``),[l,u]=(0,i.useState)(`0`),[d,f]=(0,i.useState)(``);return(0,i.useEffect)(()=>{e&&(c(e.record?.code??``),u(String(e.record?.level??0)),f(e.record?.name??``))},[e]),(0,s.jsx)(a.Modal,{cancelText:`取消`,confirmButtonProps:{disabled:!o||!d},confirmText:e?.type===`EDIT`?`儲存`:`建立`,loading:r,modalType:`standard`,onCancel:t,onClose:t,onConfirm:()=>void n({code:o,level:Number(l),name:d}),open:!!e,showModalFooter:!0,showModalHeader:!0,size:`regular`,title:e?.type===`EDIT`?`編輯職位`:`新增職位`,children:(0,s.jsxs)(`div`,{className:L.modalFields,children:[(0,s.jsx)(J,{label:`代碼`,name:`positionCode`,onChange:c,placeholder:`例如 FIN-MGR`,value:o}),(0,s.jsx)(J,{label:`名稱`,name:`positionName`,onChange:f,placeholder:`例如 財務主管`,value:d}),(0,s.jsx)(J,{label:`職等`,name:`positionLevel`,onChange:u,placeholder:`例如 5`,value:l})]})})}function Pe({membersById:e,modal:r,onClose:o,onSubmit:c,orgUnits:l,positions:u,saving:d}){let[f,p]=(0,i.useState)(Qe()),[m,h]=(0,i.useState)(``),[g,_]=(0,i.useState)(K[1]),[v,y]=(0,i.useState)(null),[b,x]=(0,i.useState)(null),[S,C]=(0,i.useState)(null);return(0,i.useEffect)(()=>{if(!r)return;let t=r.record,i=t?e.get(t.memberId):null;p(t?.effectiveFrom??Qe()),h(t?.effectiveTo??``),_(K.find(e=>e.value===t?.isPrimary)??K[1]),y(i?n.i(i):null),x(Q(l.find(e=>e.id===t?.orgUnitId),n.a)),C(Q(u.find(e=>e.id===t?.positionId),n.o))},[e,r,l,u]),(0,s.jsx)(a.Modal,{cancelText:`取消`,confirmButtonProps:{disabled:!b||r?.type!==`EDIT`&&!v},confirmText:r?.type===`EDIT`?`儲存`:`建立`,loading:d,modalType:`standard`,onCancel:o,onClose:o,onConfirm:()=>void c({effectiveFrom:f,effectiveTo:m||null,isPrimary:g.value,memberId:v?.id??null,orgUnitId:b?.id??null,positionId:S?.id??null}),open:!!r,showModalFooter:!0,showModalHeader:!0,size:`regular`,title:r?.type===`EDIT`?`編輯會員歸屬`:`新增會員歸屬`,children:(0,s.jsxs)(`div`,{className:L.modalFields,children:[(0,s.jsx)(t.t,{label:`會員`,name:`memberId`,children:(0,s.jsx)(n.t,{disabled:r?.type===`EDIT`,name:`memberId`,onChange:y,placeholder:`搜尋會員姓名或信箱`,value:v})}),(0,s.jsx)(t.t,{label:`組織`,name:`orgUnitId`,children:(0,s.jsx)(n.n,{name:`orgUnitId`,onChange:x,orgUnits:l,placeholder:`選擇歸屬組織`,value:b})}),(0,s.jsx)(t.t,{label:`職位`,name:`positionId`,children:(0,s.jsx)(n.r,{name:`positionId`,onChange:C,placeholder:`選擇職位`,positions:u,value:S})}),(0,s.jsx)(t.t,{label:`歸屬類型`,name:`isPrimary`,children:(0,s.jsx)(a.Select,{clearable:!1,fullWidth:!0,onChange:e=>_(Y(e)),options:[...K],placeholder:`選擇歸屬類型`,value:g})}),(0,s.jsx)(Ie,{label:`生效日`,name:`membershipEffectiveFrom`,onChange:p,placeholder:`YYYY-MM-DD`,value:f}),(0,s.jsx)(Ie,{label:`結束日`,name:`membershipEffectiveTo`,onChange:h,placeholder:`YYYY-MM-DD,未設定代表無期限`,value:m})]})})}function Fe({membersById:e,modal:r,onClose:o,onSubmit:c,orgUnits:l,positions:u,saving:d}){let[f,p]=(0,i.useState)(Qe()),[m,h]=(0,i.useState)(``),[g,_]=(0,i.useState)(null),[v,y]=(0,i.useState)(String(z)),[b,x]=(0,i.useState)(null),[S,C]=(0,i.useState)(null),[w,T]=(0,i.useState)(null),[E,D]=(0,i.useState)(U[0]);(0,i.useEffect)(()=>{if(!r)return;let t=r.record,i=U.find(e=>e.id===t?.scopeType)??U[0];p(t?.effectiveFrom??Qe()),h(t?.effectiveTo??``),_(Q(t?e.get(t.managerMemberId):null,n.i)),y(String(t?.priority??z)),x(i.id===`MEMBER`?Q(t?e.get(t.scopeId):null,n.i):null),C(i.id===`ORG_UNIT`?Q(l.find(e=>e.id===t?.scopeId),n.a):null),T(i.id===`POSITION`?Q(u.find(e=>e.id===t?.scopeId),n.o):null),D(i)},[e,r,l,u]);let ee=E.id===`MEMBER`?b?.id:E.id===`ORG_UNIT`?S?.id:w?.id,te=!!(E.id===`MEMBER`&&b?.id&&g?.id&&b.id===g.id);return(0,s.jsx)(a.Modal,{cancelText:`取消`,confirmButtonProps:{disabled:!g||!ee||te},confirmText:r?.type===`EDIT`?`儲存`:`建立`,loading:d,modalType:`standard`,onCancel:o,onClose:o,onConfirm:()=>void c({effectiveFrom:f,effectiveTo:m||null,managerMemberId:g?.id??null,priority:Number(v),scopeId:ee??null,scopeType:E.id}),open:!!r,showModalFooter:!0,showModalHeader:!0,size:`regular`,title:r?.type===`EDIT`?`編輯主管規則`:`新增主管規則`,children:(0,s.jsxs)(`div`,{className:L.modalFields,children:[(0,s.jsx)(t.t,{label:`套用範圍`,name:`scopeType`,children:(0,s.jsx)(a.Select,{clearable:!1,fullWidth:!0,onChange:e=>D(Ke(e)),options:[...U],placeholder:`選擇套用範圍`,value:E})}),E.id===`MEMBER`?(0,s.jsx)(t.t,{label:`會員`,name:`scopeMemberId`,children:(0,s.jsx)(n.t,{name:`scopeMemberId`,onChange:x,placeholder:`搜尋套用會員`,value:b})}):null,E.id===`ORG_UNIT`?(0,s.jsx)(t.t,{label:`組織`,name:`scopeOrgUnitId`,children:(0,s.jsx)(n.n,{name:`scopeOrgUnitId`,onChange:C,orgUnits:l,placeholder:`選擇套用組織`,value:S})}):null,E.id===`POSITION`?(0,s.jsx)(t.t,{label:`職位`,name:`scopePositionId`,children:(0,s.jsx)(n.r,{name:`scopePositionId`,onChange:T,placeholder:`選擇套用職位`,positions:u,value:w})}):null,(0,s.jsx)(t.t,{label:`簽核主管`,name:`managerMemberId`,children:(0,s.jsx)(n.t,{name:`managerMemberId`,onChange:_,placeholder:`搜尋簽核主管`,value:g})}),te?(0,s.jsx)(a.Typography,{color:`text-error`,variant:`caption`,children:`簽核主管不可設定為套用會員本人。`}):null,(0,s.jsx)(J,{hintText:`數字越大越優先,同一位成員命中多條規則時只會採用最高的那一層。建議:指定到人 200、部門主管 100、全公司通用的墊底規則 10。`,label:`優先序`,name:`managerPriority`,onChange:y,placeholder:`例如 100`,value:v}),(0,s.jsx)(Ie,{label:`生效日`,name:`managerEffectiveFrom`,onChange:p,placeholder:`YYYY-MM-DD`,value:f}),(0,s.jsx)(Ie,{label:`結束日`,name:`managerEffectiveTo`,onChange:h,placeholder:`YYYY-MM-DD,未設定代表無期限`,value:m})]})})}function J({hintText:e,label:n,name:r,onChange:i,placeholder:o,value:c}){return(0,s.jsx)(t.t,{hintText:e,label:n,name:r,children:(0,s.jsx)(a.Input,{fullWidth:!0,name:r,onChange:e=>i(e.target.value),placeholder:o,value:c})})}function Ie({label:e,name:n,onChange:r,placeholder:i,value:o}){return(0,s.jsx)(t.t,{label:e,name:n,children:(0,s.jsx)(a.DatePicker,{format:`YYYY-MM-DD`,fullWidth:!0,inputProps:{name:n},onChange:e=>r($e(e)),placeholder:i,value:o.trim()?o:void 0})})}function Le(e,t){return[...new Set([...e.map(e=>e.memberId),...t.map(e=>e.managerMemberId),...t.filter(e=>e.scopeType===`MEMBER`).map(e=>e.scopeId)])]}function Re(e,t){return e.scopeType===`MEMBER`?`會員:${ze(t.membersById.get(e.scopeId))}`:e.scopeType===`ORG_UNIT`?`組織:${Be(t.orgUnitsById.get(e.scopeId))}`:`職位:${He(t.positionsById.get(e.scopeId))}`}function ze(e){return e?r.t(e.name,e.email,`dot`):`未知會員`}function Be(e){return e?`${e.name} · ${e.code}`:`未知組織`}function Ve(e){return B.find(t=>t.id.toLowerCase()===e.toLowerCase())?.name??`未知類型`}function He(e){return e?`${e.name} · ${e.code}`:`未指定`}function Ue(e){return e===`MANAGERS`||e===`MEMBERSHIPS`||e===`POSITIONS`?e:`ORG_UNITS`}function We(e){let t=Z(e)?e:null;if(typeof t?.id==`string`){let e=t.id;return B.find(t=>t.id.toLowerCase()===e.toLowerCase())??B[2]}return B[2]}function Ge(e){let t=Z(e)?e:null;if(typeof t?.id==`string`){let e=t.id;return H.find(t=>t.id.toLowerCase()===e.toLowerCase())??V}return V}function Ke(e){return X(e,U,U[0])}function qe(e){return X(e,ge,W)}function Je(e){return X(e,_e,G)}function Ye(e){let t=Z(e)?e:null,n=typeof t?.id==`string`?t.id:null;return _e.find(e=>e.id===n)??null}function Xe(e){let t=Z(e)?e:null,n=typeof t?.id==`string`?t.id:null,r=typeof t?.name==`string`?t.name:null;return n&&r?{id:n,name:r}:null}function Ze(e){let t=Z(e)?e:null,n=typeof t?.id==`string`?t.id:null,r=typeof t?.name==`string`?t.name:null;return n&&r?{id:n,name:r}:null}function Y(e){return X(e,K,K[1])}function X(e,t,n){let r=Z(e)?e:null,i=typeof r?.id==`string`?r.id:null;return t.find(e=>e.id===i)??n}function Z(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Q(e,t){return e?t(e):null}function Qe(){return et(new Date)}function $e(e){let t=e?new Date(e):null;return t&&!Number.isNaN(t.getTime())?et(t):``}function et(e){return`${e.getFullYear()}-${tt(e.getMonth()+1)}-${tt(e.getDate())}`}function tt(e){return String(e).padStart(2,`0`)}function nt(e){return e instanceof Error?e.message:`讀取組織資料失敗。`}Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return Ce}});
|
|
2
|
+
//# sourceMappingURL=orgs-B_2J2tLX.cjs.map
|