@rytass/bpm-core-react 0.14.0 → 0.16.2
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 +83 -0
- package/dist/chunks/{FormBuilderView-BYLN6W-P.js → FormBuilderView-BYL42M0d.js} +499 -476
- package/dist/chunks/FormBuilderView-BYL42M0d.js.map +1 -0
- package/dist/chunks/FormBuilderView-aBcA-_tt.cjs +3 -0
- package/dist/chunks/FormBuilderView-aBcA-_tt.cjs.map +1 -0
- package/dist/chunks/FormRendererView-DRFi_IH7.cjs +4 -0
- package/dist/chunks/FormRendererView-DRFi_IH7.cjs.map +1 -0
- package/dist/chunks/FormRendererView-cZGOOjU5.js +986 -0
- package/dist/chunks/FormRendererView-cZGOOjU5.js.map +1 -0
- package/dist/chunks/{admin-pickers-DLlG_1du.js → admin-pickers-DxhzECkD.js} +52 -51
- package/dist/chunks/admin-pickers-DxhzECkD.js.map +1 -0
- package/dist/chunks/admin-pickers-qe3j--gX.cjs +2 -0
- package/dist/chunks/admin-pickers-qe3j--gX.cjs.map +1 -0
- package/dist/chunks/approval-instance-list-page-CPXMP3PL.js.map +1 -1
- package/dist/chunks/approval-instance-list-page-ax0pm8bt.cjs.map +1 -1
- package/dist/chunks/auth-provider-4BeCw7cI.cjs.map +1 -1
- package/dist/chunks/auth-provider-B5oPmvk2.js.map +1 -1
- package/dist/chunks/bpm-form-field-Bc6k4ZEO.cjs.map +1 -1
- package/dist/chunks/bpm-form-field-Cao0rMol.js.map +1 -1
- package/dist/chunks/{categories-DGlxuC6X.js → categories-1gKxeEqw.js} +4 -10
- package/dist/chunks/categories-1gKxeEqw.js.map +1 -0
- package/dist/chunks/{categories-Yid05WAX.cjs → categories-DjENouRm.cjs} +2 -2
- package/dist/chunks/categories-DjENouRm.cjs.map +1 -0
- package/dist/chunks/{compose-CSWbRgJJ.js → compose-DO6n0NJi.js} +5 -5
- package/dist/chunks/compose-DO6n0NJi.js.map +1 -0
- package/dist/chunks/compose-DUH9cQ-L.cjs +2 -0
- package/dist/chunks/compose-DUH9cQ-L.cjs.map +1 -0
- package/dist/chunks/dashboard-page-BWQBSGDq.cjs +2 -0
- package/dist/chunks/dashboard-page-BWQBSGDq.cjs.map +1 -0
- package/dist/chunks/{dashboard-page-CeCLV1tp.js → dashboard-page-DdBvV6YI.js} +15 -16
- package/dist/chunks/dashboard-page-DdBvV6YI.js.map +1 -0
- package/dist/chunks/delegations-BLzYnOae.cjs +2 -0
- package/dist/chunks/delegations-BLzYnOae.cjs.map +1 -0
- package/dist/chunks/delegations-Ckf03icM.cjs +2 -0
- package/dist/chunks/delegations-Ckf03icM.cjs.map +1 -0
- package/dist/chunks/{delegations-DRupxJ4_.js → delegations-MxF3uJGW.js} +205 -215
- package/dist/chunks/delegations-MxF3uJGW.js.map +1 -0
- package/dist/chunks/{delegations-B8gLSt0i.js → delegations-_b4_xBit.js} +167 -173
- package/dist/chunks/delegations-_b4_xBit.js.map +1 -0
- package/dist/chunks/designer-2YvjP_GU.cjs +65 -0
- package/dist/chunks/designer-2YvjP_GU.cjs.map +1 -0
- package/dist/chunks/{designer-BtsIL0BB.js → designer-B-cfiob9.js} +1334 -1254
- package/dist/chunks/designer-B-cfiob9.js.map +1 -0
- package/dist/chunks/{detail-C8NOqY0p.js → detail-C6NdVijG.js} +852 -841
- package/dist/chunks/detail-C6NdVijG.js.map +1 -0
- package/dist/chunks/detail-DKobK4Cg.cjs +2 -0
- package/dist/chunks/detail-DKobK4Cg.cjs.map +1 -0
- package/dist/chunks/format-date-time-CB-LxzqT.js.map +1 -1
- package/dist/chunks/format-date-time-D-cx5M8z.cjs.map +1 -1
- package/dist/chunks/format-member-label-DudVI55w.js +13 -0
- package/dist/chunks/format-member-label-DudVI55w.js.map +1 -0
- package/dist/chunks/format-member-label-t7ANTF62.cjs +2 -0
- package/dist/chunks/format-member-label-t7ANTF62.cjs.map +1 -0
- package/dist/chunks/login-AtVxZBsC.cjs.map +1 -1
- package/dist/chunks/login-C6qlTC6F.js.map +1 -1
- package/dist/chunks/notifications-Bc4mgSds.cjs.map +1 -1
- package/dist/chunks/notifications-DJ5wzCye.js.map +1 -1
- package/dist/chunks/{orgs-BpqVu5ka.js → orgs-Bpp1U60k.js} +538 -557
- package/dist/chunks/orgs-Bpp1U60k.js.map +1 -0
- package/dist/chunks/orgs-ywaE1_02.cjs +2 -0
- package/dist/chunks/orgs-ywaE1_02.cjs.map +1 -0
- package/dist/chunks/providers-DHtDZFNA.cjs.map +1 -1
- package/dist/chunks/providers-J5eyByQj.js.map +1 -1
- package/dist/chunks/router-adapter--gYs13E8.cjs.map +1 -1
- package/dist/chunks/router-adapter-DftlFTOd.js.map +1 -1
- package/dist/chunks/routes-config-C1j_vFKo.cjs.map +1 -1
- package/dist/chunks/routes-config-yZbESPlw.js.map +1 -1
- package/dist/chunks/task-node-label-BF6UllxP.cjs +2 -0
- package/dist/chunks/task-node-label-BF6UllxP.cjs.map +1 -0
- package/dist/chunks/task-node-label-CWZgOBj0.js +9 -0
- package/dist/chunks/task-node-label-CWZgOBj0.js.map +1 -0
- package/dist/chunks/use-bpm-member-D57LKe_u.js.map +1 -1
- package/dist/chunks/use-bpm-member-DpNAQtNC.cjs.map +1 -1
- package/dist/chunks/users-BSVo_eNe.js +215 -0
- package/dist/chunks/users-BSVo_eNe.js.map +1 -0
- package/dist/chunks/users-DFSBTDMy.cjs +2 -0
- package/dist/chunks/users-DFSBTDMy.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/format-member-label.d.ts +12 -0
- package/dist/lib/member-profile-resolution.d.ts +48 -0
- package/dist/lib/routes-config.d.ts +1 -1
- package/dist/lib/task-node-label.d.ts +10 -0
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.js.map +1 -1
- package/dist/next/workflow-chat-route.cjs.map +1 -1
- 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 +1 -1
- package/dist/pages/admin/delegations/index.js.map +1 -1
- 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 +1 -1
- package/dist/pages/admin/orgs/index.js.map +1 -1
- 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 +1 -1
- package/dist/pages/admin/users/index.js.map +1 -1
- package/dist/pages/admin/webhook-endpoints/index.cjs.map +1 -1
- package/dist/pages/admin/webhook-endpoints/index.js.map +1 -1
- package/dist/pages/cc/index.cjs.map +1 -1
- package/dist/pages/cc/index.js.map +1 -1
- package/dist/pages/dashboard/index.cjs.map +1 -1
- package/dist/pages/dashboard/index.js.map +1 -1
- package/dist/pages/delegations/index.cjs +1 -1
- package/dist/pages/delegations/index.cjs.map +1 -1
- package/dist/pages/delegations/index.js +1 -1
- package/dist/pages/delegations/index.js.map +1 -1
- package/dist/pages/inbox/index.cjs.map +1 -1
- package/dist/pages/inbox/index.js.map +1 -1
- 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 +1 -1
- package/dist/pages/instances/detail/index.js.map +1 -1
- package/dist/pages/instances/new/index.cjs.map +1 -1
- package/dist/pages/instances/new/index.js.map +1 -1
- package/dist/pages/login/index.cjs.map +1 -1
- package/dist/pages/login/index.js.map +1 -1
- package/dist/pages/root/index.cjs.map +1 -1
- package/dist/pages/root/index.js.map +1 -1
- package/dist/pages/search/index.cjs.map +1 -1
- package/dist/pages/search/index.js.map +1 -1
- package/dist/pages/sent/index.cjs.map +1 -1
- package/dist/pages/sent/index.js.map +1 -1
- package/dist/pages/settings/notifications/index.cjs.map +1 -1
- package/dist/pages/settings/notifications/index.js.map +1 -1
- 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 +1 -1
- package/dist/pages/templates/categories/index.js.map +1 -1
- 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 +1 -1
- package/dist/pages/templates/compose/index.js.map +1 -1
- 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 +1 -1
- package/dist/pages/templates/designer/index.js.map +1 -1
- package/dist/pages/templates/index.cjs.map +1 -1
- package/dist/pages/templates/index.js.map +1 -1
- package/dist/pages/templates/versions/index.cjs.map +1 -1
- package/dist/pages/templates/versions/index.js.map +1 -1
- package/dist/views/admin/delegations/index.cjs +1 -1
- package/dist/views/admin/delegations/index.js +1 -2
- 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 -2
- package/dist/views/admin/users/index.cjs +1 -1
- package/dist/views/admin/users/index.js +1 -2
- package/dist/views/admin/webhook-endpoints/index.cjs.map +1 -1
- package/dist/views/admin/webhook-endpoints/index.js.map +1 -1
- package/dist/views/cc/index.cjs.map +1 -1
- package/dist/views/cc/index.js.map +1 -1
- package/dist/views/dashboard/index.cjs +1 -1
- package/dist/views/dashboard/index.cjs.map +1 -1
- package/dist/views/dashboard/index.js +1 -1
- package/dist/views/dashboard/index.js.map +1 -1
- package/dist/views/delegations/index.cjs +1 -1
- package/dist/views/delegations/index.js +1 -2
- package/dist/views/forms/builder/index.cjs +1 -1
- package/dist/views/forms/builder/index.js +1 -2
- package/dist/views/forms/renderer/form-field-link.d.ts +16 -0
- package/dist/views/forms/renderer/index.cjs +1 -1
- package/dist/views/forms/renderer/index.js +1 -2
- package/dist/views/forms/renderer/member-field-options.d.ts +39 -0
- package/dist/views/forms/renderer/member-field.d.ts +21 -0
- package/dist/views/inbox/index.cjs +1 -1
- package/dist/views/inbox/index.cjs.map +1 -1
- package/dist/views/inbox/index.js +101 -102
- package/dist/views/inbox/index.js.map +1 -1
- package/dist/views/instances/detail/index.cjs +1 -1
- package/dist/views/instances/detail/index.js +1 -2
- package/dist/views/instances/detail/sections/container-helpers.d.ts +3 -2
- package/dist/views/instances/detail/submitted-by.d.ts +23 -0
- package/dist/views/instances/index.js +1 -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 +4 -11
- package/dist/views/instances/new/index.js.map +1 -1
- package/dist/views/login/index.js +1 -2
- package/dist/views/search/index.cjs.map +1 -1
- package/dist/views/search/index.js.map +1 -1
- package/dist/views/sent/SentView.d.ts +1 -1
- package/dist/views/sent/index.cjs +1 -1
- package/dist/views/sent/index.cjs.map +1 -1
- package/dist/views/sent/index.js +2 -2
- package/dist/views/sent/index.js.map +1 -1
- package/dist/views/settings/index.js +1 -2
- package/dist/views/settings/notifications/index.js +1 -2
- package/dist/views/templates/categories/index.cjs +1 -1
- package/dist/views/templates/categories/index.js +1 -2
- package/dist/views/templates/compose/index.cjs +1 -1
- package/dist/views/templates/compose/index.js +1 -2
- package/dist/views/templates/designer/designer-version-labels.d.ts +12 -0
- package/dist/views/templates/designer/dry-run-assignee-label.d.ts +28 -0
- package/dist/views/templates/designer/dry-run-sample-data.d.ts +9 -0
- package/dist/views/templates/designer/dynamic-form-resolver-options.d.ts +35 -0
- package/dist/views/templates/designer/index.cjs +1 -1
- package/dist/views/templates/designer/index.js +1 -2
- package/dist/views/templates/index.cjs +1 -1
- package/dist/views/templates/index.cjs.map +1 -1
- package/dist/views/templates/index.js +1 -1
- package/dist/views/templates/index.js.map +1 -1
- package/dist/views/templates/versions/index.cjs.map +1 -1
- package/dist/views/templates/versions/index.js.map +1 -1
- package/package.json +5 -5
- package/dist/chunks/FormBuilderView-BYLN6W-P.js.map +0 -1
- package/dist/chunks/FormBuilderView-VmdPVbg7.cjs +0 -3
- package/dist/chunks/FormBuilderView-VmdPVbg7.cjs.map +0 -1
- package/dist/chunks/FormRendererView-Bs8lTb4k.cjs +0 -2
- package/dist/chunks/FormRendererView-Bs8lTb4k.cjs.map +0 -1
- package/dist/chunks/FormRendererView-UPowZZgw.js +0 -798
- package/dist/chunks/FormRendererView-UPowZZgw.js.map +0 -1
- package/dist/chunks/admin-pickers-Btvij1at.cjs +0 -2
- package/dist/chunks/admin-pickers-Btvij1at.cjs.map +0 -1
- package/dist/chunks/admin-pickers-DLlG_1du.js.map +0 -1
- package/dist/chunks/categories-DGlxuC6X.js.map +0 -1
- package/dist/chunks/categories-Yid05WAX.cjs.map +0 -1
- package/dist/chunks/compose-CHZoX-H0.cjs +0 -2
- package/dist/chunks/compose-CHZoX-H0.cjs.map +0 -1
- package/dist/chunks/compose-CSWbRgJJ.js.map +0 -1
- package/dist/chunks/dashboard-page-CeCLV1tp.js.map +0 -1
- package/dist/chunks/dashboard-page-DooFJsxM.cjs +0 -2
- package/dist/chunks/dashboard-page-DooFJsxM.cjs.map +0 -1
- package/dist/chunks/delegations-8LsxcQM4.cjs +0 -2
- package/dist/chunks/delegations-8LsxcQM4.cjs.map +0 -1
- package/dist/chunks/delegations-B8gLSt0i.js.map +0 -1
- package/dist/chunks/delegations-DRupxJ4_.js.map +0 -1
- package/dist/chunks/delegations-DlYwMiAW.cjs +0 -2
- package/dist/chunks/delegations-DlYwMiAW.cjs.map +0 -1
- package/dist/chunks/designer-BtsIL0BB.js.map +0 -1
- package/dist/chunks/designer-Dm6cX2kZ.cjs +0 -65
- package/dist/chunks/designer-Dm6cX2kZ.cjs.map +0 -1
- package/dist/chunks/detail-C8NOqY0p.js.map +0 -1
- package/dist/chunks/detail-CX-3hZoL.cjs +0 -2
- package/dist/chunks/detail-CX-3hZoL.cjs.map +0 -1
- package/dist/chunks/orgs-BpqVu5ka.js.map +0 -1
- package/dist/chunks/orgs-D0-5BaHT.cjs +0 -2
- package/dist/chunks/orgs-D0-5BaHT.cjs.map +0 -1
- package/dist/chunks/users-C9LA-Rd7.cjs +0 -2
- package/dist/chunks/users-C9LA-Rd7.cjs.map +0 -1
- package/dist/chunks/users-FWrb3ehW.js +0 -214
- package/dist/chunks/users-FWrb3ehW.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-node-label-BF6UllxP.cjs","names":["readTaskNodeText","label","task"],"sources":["../../src/lib/task-node-label.ts"],"sourcesContent":["import type { TaskRecord } from '@rytass/bpm-core-client/workflow';\n\n/**\n * What a task list shows in its 「節點」 column: the node's display name\n * (`nodeLabel`, resolved server-side from the instance's workflow snapshot),\n * falling back to the raw `nodeId` when the server sent none — an unknown node\n * or a host running its own queries. An ad-hoc task keeps the label of the\n * node it was spawned on and gets the same 臨時會簽/臨時加簽 marker the\n * instance timeline uses.\n */\nexport function readTaskNodeText(\n task: Pick<TaskRecord, 'adhocType' | 'isAdhoc' | 'nodeId' | 'nodeLabel'>,\n): string {\n const label = task.nodeLabel?.trim() ? task.nodeLabel : task.nodeId;\n\n if (!task.isAdhoc) {\n return label;\n }\n\n return `${label}${\n task.adhocType === 'COUNTERSIGN' ? '(臨時會簽)' : '(臨時加簽)'\n }`;\n}\n"],"mappings":"AAUA,SAAgBA,EACd,EACQ,CACR,IAAMC,EAAQC,EAAK,WAAW,KAAK,EAAIA,EAAK,UAAYA,EAAK,OAM7D,OAJKA,EAAK,QAIH,GAAGD,IACRC,EAAK,YAAc,cAAgB,SAAW,WAJvCD,CAMX"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/lib/task-node-label.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
let t = e.nodeLabel?.trim() ? e.nodeLabel : e.nodeId;
|
|
4
|
+
return e.isAdhoc ? `${t}${e.adhocType === "COUNTERSIGN" ? "(臨時會簽)" : "(臨時加簽)"}` : t;
|
|
5
|
+
}
|
|
6
|
+
//#endregion
|
|
7
|
+
export { e as t };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=task-node-label-CWZgOBj0.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-node-label-CWZgOBj0.js","names":["readTaskNodeText","label","task"],"sources":["../../src/lib/task-node-label.ts"],"sourcesContent":["import type { TaskRecord } from '@rytass/bpm-core-client/workflow';\n\n/**\n * What a task list shows in its 「節點」 column: the node's display name\n * (`nodeLabel`, resolved server-side from the instance's workflow snapshot),\n * falling back to the raw `nodeId` when the server sent none — an unknown node\n * or a host running its own queries. An ad-hoc task keeps the label of the\n * node it was spawned on and gets the same 臨時會簽/臨時加簽 marker the\n * instance timeline uses.\n */\nexport function readTaskNodeText(\n task: Pick<TaskRecord, 'adhocType' | 'isAdhoc' | 'nodeId' | 'nodeLabel'>,\n): string {\n const label = task.nodeLabel?.trim() ? task.nodeLabel : task.nodeId;\n\n if (!task.isAdhoc) {\n return label;\n }\n\n return `${label}${\n task.adhocType === 'COUNTERSIGN' ? '(臨時會簽)' : '(臨時加簽)'\n }`;\n}\n"],"mappings":";AAUA,SAAgBA,EACd,GACQ;CACR,IAAMC,IAAQC,EAAK,WAAW,KAAK,IAAIA,EAAK,YAAYA,EAAK;CAM7D,OAJKA,EAAK,UAIH,GAAGD,IACRC,EAAK,cAAc,gBAAgB,WAAW,aAJvCD;AAMX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-bpm-member-D57LKe_u.js","names":[],"sources":["../../src/lib/use-bpm-member.ts"],"sourcesContent":["'use client';\n\nimport type { ApiMember } from '@rytass/bpm-core-client';\nimport { useAuth } from './auth-provider';\n\n/**\n * Read the currently authenticated BPM member. Returns `null` when there\n * is no active session — typically only seen on the login page or during\n * the brief loading window before `<AuthProvider>` resolves the cookie.\n *\n * Convenience alias for `useAuth().member` aimed at host navigations\n * (avatar, display name, role-based menu visibility) that should not\n * depend on the broader `useAuth()` surface.\n */\nexport function useBPMMember(): ApiMember | null {\n const { member } = useAuth();\n return member;\n}\n\nconst BPM_ADMIN_PERMISSIONS: ReadonlySet<string> = new Set([\n 'bpm:*',\n 'bpm:admin',\n 'bpm.admin',\n 'bpm:admin:*',\n]);\n\n/**\n * Whether the member passes the server's `@BPMAdminOnly()` check — the\n * `BPM_ADMIN` role or one of the administrator permissions. Use it to decide\n * whether to show administrator-only UI; the server still enforces access.\n */\nexport function isBPMAdminMember(\n member: Pick<ApiMember, 'permissions' | 'roles'> | null,\n): boolean {\n if (!member) {\n return false;\n }\n\n // The member comes from the host's `/auth/me` unvalidated; a host that\n // omits either list must not take the page that asks down with it.\n return (\n (member.roles ?? []).includes('BPM_ADMIN') ||\n (member.permissions ?? []).some((permission) =>\n BPM_ADMIN_PERMISSIONS.has(permission),\n )\n );\n}\n"],"mappings":";;;AAcA,
|
|
1
|
+
{"version":3,"file":"use-bpm-member-D57LKe_u.js","names":["useAuth","useBPMMember","member","BPM_ADMIN_PERMISSIONS","isBPMAdminMember","permission"],"sources":["../../src/lib/use-bpm-member.ts"],"sourcesContent":["'use client';\n\nimport type { ApiMember } from '@rytass/bpm-core-client';\nimport { useAuth } from './auth-provider';\n\n/**\n * Read the currently authenticated BPM member. Returns `null` when there\n * is no active session — typically only seen on the login page or during\n * the brief loading window before `<AuthProvider>` resolves the cookie.\n *\n * Convenience alias for `useAuth().member` aimed at host navigations\n * (avatar, display name, role-based menu visibility) that should not\n * depend on the broader `useAuth()` surface.\n */\nexport function useBPMMember(): ApiMember | null {\n const { member } = useAuth();\n return member;\n}\n\nconst BPM_ADMIN_PERMISSIONS: ReadonlySet<string> = new Set([\n 'bpm:*',\n 'bpm:admin',\n 'bpm.admin',\n 'bpm:admin:*',\n]);\n\n/**\n * Whether the member passes the server's `@BPMAdminOnly()` check — the\n * `BPM_ADMIN` role or one of the administrator permissions. Use it to decide\n * whether to show administrator-only UI; the server still enforces access.\n */\nexport function isBPMAdminMember(\n member: Pick<ApiMember, 'permissions' | 'roles'> | null,\n): boolean {\n if (!member) {\n return false;\n }\n\n // The member comes from the host's `/auth/me` unvalidated; a host that\n // omits either list must not take the page that asks down with it.\n return (\n (member.roles ?? []).includes('BPM_ADMIN') ||\n (member.permissions ?? []).some((permission) =>\n BPM_ADMIN_PERMISSIONS.has(permission),\n )\n );\n}\n"],"mappings":";;;AAcA,SAAgBC,IAAiC;CAC/C,IAAM,EAAE,cAAWD,EAAQ;CAC3B,OAAOE;AACT;AAEA,IAAMC,oBAA6C,IAAI,IAAI;CACzD;CACA;CACA;CACA;AACF,CAAC;AAOD,SAAgBC,EACd,GACS;CAOT,OANKF,KAOFA,EAAO,SAAS,CAAC,EAAA,CAAG,SAAS,WAAW,MACxCA,EAAO,eAAe,CAAC,EAAA,CAAG,MAAM,MAC/BC,EAAsB,IAAIE,CAAU,CACtC,IATO;AAWX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-bpm-member-DpNAQtNC.cjs","names":[],"sources":["../../src/lib/use-bpm-member.ts"],"sourcesContent":["'use client';\n\nimport type { ApiMember } from '@rytass/bpm-core-client';\nimport { useAuth } from './auth-provider';\n\n/**\n * Read the currently authenticated BPM member. Returns `null` when there\n * is no active session — typically only seen on the login page or during\n * the brief loading window before `<AuthProvider>` resolves the cookie.\n *\n * Convenience alias for `useAuth().member` aimed at host navigations\n * (avatar, display name, role-based menu visibility) that should not\n * depend on the broader `useAuth()` surface.\n */\nexport function useBPMMember(): ApiMember | null {\n const { member } = useAuth();\n return member;\n}\n\nconst BPM_ADMIN_PERMISSIONS: ReadonlySet<string> = new Set([\n 'bpm:*',\n 'bpm:admin',\n 'bpm.admin',\n 'bpm:admin:*',\n]);\n\n/**\n * Whether the member passes the server's `@BPMAdminOnly()` check — the\n * `BPM_ADMIN` role or one of the administrator permissions. Use it to decide\n * whether to show administrator-only UI; the server still enforces access.\n */\nexport function isBPMAdminMember(\n member: Pick<ApiMember, 'permissions' | 'roles'> | null,\n): boolean {\n if (!member) {\n return false;\n }\n\n // The member comes from the host's `/auth/me` unvalidated; a host that\n // omits either list must not take the page that asks down with it.\n return (\n (member.roles ?? []).includes('BPM_ADMIN') ||\n (member.permissions ?? []).some((permission) =>\n BPM_ADMIN_PERMISSIONS.has(permission),\n )\n );\n}\n"],"mappings":"6DAcA,
|
|
1
|
+
{"version":3,"file":"use-bpm-member-DpNAQtNC.cjs","names":["useAuth","require_auth_provider","useBPMMember","member","BPM_ADMIN_PERMISSIONS","isBPMAdminMember","permission"],"sources":["../../src/lib/use-bpm-member.ts"],"sourcesContent":["'use client';\n\nimport type { ApiMember } from '@rytass/bpm-core-client';\nimport { useAuth } from './auth-provider';\n\n/**\n * Read the currently authenticated BPM member. Returns `null` when there\n * is no active session — typically only seen on the login page or during\n * the brief loading window before `<AuthProvider>` resolves the cookie.\n *\n * Convenience alias for `useAuth().member` aimed at host navigations\n * (avatar, display name, role-based menu visibility) that should not\n * depend on the broader `useAuth()` surface.\n */\nexport function useBPMMember(): ApiMember | null {\n const { member } = useAuth();\n return member;\n}\n\nconst BPM_ADMIN_PERMISSIONS: ReadonlySet<string> = new Set([\n 'bpm:*',\n 'bpm:admin',\n 'bpm.admin',\n 'bpm:admin:*',\n]);\n\n/**\n * Whether the member passes the server's `@BPMAdminOnly()` check — the\n * `BPM_ADMIN` role or one of the administrator permissions. Use it to decide\n * whether to show administrator-only UI; the server still enforces access.\n */\nexport function isBPMAdminMember(\n member: Pick<ApiMember, 'permissions' | 'roles'> | null,\n): boolean {\n if (!member) {\n return false;\n }\n\n // The member comes from the host's `/auth/me` unvalidated; a host that\n // omits either list must not take the page that asks down with it.\n return (\n (member.roles ?? []).includes('BPM_ADMIN') ||\n (member.permissions ?? []).some((permission) =>\n BPM_ADMIN_PERMISSIONS.has(permission),\n )\n );\n}\n"],"mappings":"6DAcA,SAAgBE,GAAiC,CAC/C,GAAM,CAAE,UAAWF,EAAAA,EAAQ,EAC3B,OAAOG,CACT,CAEA,IAAMC,EAA6C,IAAI,IAAI,CACzD,QACA,YACA,YACA,aACF,CAAC,EAOD,SAAgBC,EACd,EACS,CAOT,OANKF,GAOFA,EAAO,OAAS,CAAC,EAAA,CAAG,SAAS,WAAW,IACxCA,EAAO,aAAe,CAAC,EAAA,CAAG,KAAM,GAC/BC,EAAsB,IAAIE,CAAU,CACtC,EATO,EAWX"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { t as e } from "./format-member-label-DudVI55w.js";
|
|
3
|
+
import { useCallback as t, useEffect as n, useMemo as r, useState as i } from "react";
|
|
4
|
+
import { BaseCard as a, Filter as o, FilterArea as s, FilterLine as c, FormField as l, Input as u, Modal as d, PageHeader as f, Section as p, SectionGroup as ee, Table as m, Typography as h } from "@mezzanine-ui/react";
|
|
5
|
+
import { listMemberDirectoryPage as g, resolveMembers as _ } from "@rytass/bpm-core-client";
|
|
6
|
+
import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
7
|
+
import { FormFieldLayout as x } from "@mezzanine-ui/core/form";
|
|
8
|
+
import S from "@mezzanine-ui/react/ContentHeader";
|
|
9
|
+
import { listMemberships as C, readOrganizationDashboard as w, readResolvedManager as T } from "@rytass/bpm-core-client/organization";
|
|
10
|
+
import '../users.css';var E = {
|
|
11
|
+
memberFilterArea: "bpm_memberFilterArea_9RB2k",
|
|
12
|
+
header: "bpm_header_sIEtg",
|
|
13
|
+
detailFields: "bpm_detailFields_TyKNL",
|
|
14
|
+
detailSection: "bpm_detailSection_-iLjy",
|
|
15
|
+
membershipList: "bpm_membershipList_5n9iq"
|
|
16
|
+
}, D = [
|
|
17
|
+
10,
|
|
18
|
+
20,
|
|
19
|
+
50
|
|
20
|
+
];
|
|
21
|
+
function O() {
|
|
22
|
+
let [e, a] = i(null), [d, O] = i([]), [k, A] = i(null), [j, N] = i(null), [P, F] = i(null), [I, L] = i(!0), [R, z] = i(1), [B, V] = i(10), [H, U] = i(0), [W, G] = i([]), [K, q] = i([]), [J, Y] = i([]), [X, Z] = i(""), ne = r(() => new Map(K.map((e) => [e.id, e])), [K]), re = r(() => new Map(J.map((e) => [e.id, e])), [J]), Q = t(async () => {
|
|
23
|
+
L(!0), F(null);
|
|
24
|
+
try {
|
|
25
|
+
let [e, t] = await Promise.all([g({
|
|
26
|
+
page: R,
|
|
27
|
+
pageSize: B,
|
|
28
|
+
searchText: X
|
|
29
|
+
}), w()]);
|
|
30
|
+
G(e.members), U(e.totalCount), q(t.orgUnits), Y(t.positions);
|
|
31
|
+
} catch (e) {
|
|
32
|
+
F(M(e));
|
|
33
|
+
} finally {
|
|
34
|
+
L(!1);
|
|
35
|
+
}
|
|
36
|
+
}, [
|
|
37
|
+
R,
|
|
38
|
+
B,
|
|
39
|
+
X
|
|
40
|
+
]);
|
|
41
|
+
n(() => {
|
|
42
|
+
Q();
|
|
43
|
+
}, [Q]);
|
|
44
|
+
let ie = r(() => W.map((e) => ({
|
|
45
|
+
...e,
|
|
46
|
+
key: e.memberId
|
|
47
|
+
})), [W]), $ = t(async (e) => {
|
|
48
|
+
a(e), A(null), F(null);
|
|
49
|
+
try {
|
|
50
|
+
let [t, n] = await Promise.all([C({ memberId: e.memberId }), T(e.memberId)]);
|
|
51
|
+
O(t), N(n), A((await _(n.managerMemberId ? [n.managerMemberId] : []))[0] ?? null);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
F(M(e));
|
|
54
|
+
}
|
|
55
|
+
}, []), ae = r(() => [{
|
|
56
|
+
dataIndex: "name",
|
|
57
|
+
key: "name",
|
|
58
|
+
title: "姓名",
|
|
59
|
+
width: 160
|
|
60
|
+
}, {
|
|
61
|
+
dataIndex: "email",
|
|
62
|
+
key: "email",
|
|
63
|
+
title: "信箱",
|
|
64
|
+
width: 260
|
|
65
|
+
}], []), oe = r(() => ({
|
|
66
|
+
render: () => [{
|
|
67
|
+
name: "檢視",
|
|
68
|
+
onClick: (e) => {
|
|
69
|
+
$(e);
|
|
70
|
+
}
|
|
71
|
+
}],
|
|
72
|
+
variant: "base-secondary",
|
|
73
|
+
width: 88
|
|
74
|
+
}), [$]);
|
|
75
|
+
function se() {
|
|
76
|
+
a(null), A(null), O([]), N(null);
|
|
77
|
+
}
|
|
78
|
+
return /* @__PURE__ */ b(v, { children: [
|
|
79
|
+
/* @__PURE__ */ y(f, { children: /* @__PURE__ */ y(S, {
|
|
80
|
+
description: "會員資料由 host member resolver 提供,BPM 僅維護組織歸屬與主管解析。",
|
|
81
|
+
title: "會員對照"
|
|
82
|
+
}) }),
|
|
83
|
+
/* @__PURE__ */ y(ee, { children: /* @__PURE__ */ b(p, {
|
|
84
|
+
filterArea: /* @__PURE__ */ y(s, {
|
|
85
|
+
className: E.memberFilterArea,
|
|
86
|
+
children: /* @__PURE__ */ y(c, { children: /* @__PURE__ */ y(o, {
|
|
87
|
+
span: 3,
|
|
88
|
+
children: /* @__PURE__ */ y(l, {
|
|
89
|
+
fullWidth: !0,
|
|
90
|
+
layout: x.VERTICAL,
|
|
91
|
+
name: "memberSearchText",
|
|
92
|
+
children: /* @__PURE__ */ y(u, {
|
|
93
|
+
fullWidth: !0,
|
|
94
|
+
onChange: (e) => {
|
|
95
|
+
Z(e.target.value), z(1);
|
|
96
|
+
},
|
|
97
|
+
placeholder: "搜尋姓名或信箱",
|
|
98
|
+
size: "sub",
|
|
99
|
+
value: X,
|
|
100
|
+
variant: "base"
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
}) })
|
|
104
|
+
}),
|
|
105
|
+
children: [P ? /* @__PURE__ */ y(h, {
|
|
106
|
+
color: "text-error",
|
|
107
|
+
variant: "body",
|
|
108
|
+
children: P
|
|
109
|
+
}) : null, /* @__PURE__ */ y(m, {
|
|
110
|
+
actions: oe,
|
|
111
|
+
columns: ae,
|
|
112
|
+
dataSource: ie,
|
|
113
|
+
fullWidth: !0,
|
|
114
|
+
loading: I,
|
|
115
|
+
pagination: {
|
|
116
|
+
current: R,
|
|
117
|
+
onChange: (e) => {
|
|
118
|
+
z(e);
|
|
119
|
+
},
|
|
120
|
+
onChangePageSize: (e) => {
|
|
121
|
+
z(1), V(e);
|
|
122
|
+
},
|
|
123
|
+
pageSize: B,
|
|
124
|
+
pageSizeLabel: "每頁筆數",
|
|
125
|
+
pageSizeOptions: D,
|
|
126
|
+
renderResultSummary: (e, t, n) => `顯示 ${e}-${t} 筆,共 ${n} 筆`,
|
|
127
|
+
showPageSizeOptions: !0,
|
|
128
|
+
total: H
|
|
129
|
+
}
|
|
130
|
+
})]
|
|
131
|
+
}) }),
|
|
132
|
+
/* @__PURE__ */ y(te, {
|
|
133
|
+
managerProfile: k,
|
|
134
|
+
member: e,
|
|
135
|
+
memberships: d,
|
|
136
|
+
onClose: se,
|
|
137
|
+
orgUnitsById: ne,
|
|
138
|
+
positionsById: re,
|
|
139
|
+
resolvedManager: j
|
|
140
|
+
})
|
|
141
|
+
] });
|
|
142
|
+
}
|
|
143
|
+
function te({ managerProfile: e, member: t, memberships: n, onClose: r, orgUnitsById: i, positionsById: o, resolvedManager: s }) {
|
|
144
|
+
return /* @__PURE__ */ y(d, {
|
|
145
|
+
cancelText: "關閉",
|
|
146
|
+
confirmText: "關閉",
|
|
147
|
+
modalType: "standard",
|
|
148
|
+
onCancel: r,
|
|
149
|
+
onClose: r,
|
|
150
|
+
onConfirm: r,
|
|
151
|
+
open: !!t,
|
|
152
|
+
showModalFooter: !0,
|
|
153
|
+
showModalHeader: !0,
|
|
154
|
+
size: "regular",
|
|
155
|
+
title: t?.name ?? "會員明細",
|
|
156
|
+
children: t ? /* @__PURE__ */ b("div", {
|
|
157
|
+
className: E.detailFields,
|
|
158
|
+
children: [
|
|
159
|
+
/* @__PURE__ */ y(a, {
|
|
160
|
+
title: "基本資料",
|
|
161
|
+
children: /* @__PURE__ */ y("div", {
|
|
162
|
+
className: E.detailSection,
|
|
163
|
+
children: /* @__PURE__ */ b(h, {
|
|
164
|
+
variant: "body",
|
|
165
|
+
children: ["信箱:", t.email?.trim() ? t.email : "未設定"]
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
}),
|
|
169
|
+
/* @__PURE__ */ y(a, {
|
|
170
|
+
title: "BPM 組織歸屬",
|
|
171
|
+
children: /* @__PURE__ */ y("div", {
|
|
172
|
+
className: E.membershipList,
|
|
173
|
+
children: n.length ? n.map((e) => /* @__PURE__ */ b(h, {
|
|
174
|
+
variant: "body",
|
|
175
|
+
children: [
|
|
176
|
+
k(i.get(e.orgUnitId)),
|
|
177
|
+
" / ",
|
|
178
|
+
e.positionId ? A(o.get(e.positionId)) : "未指定職位",
|
|
179
|
+
" / ",
|
|
180
|
+
e.isPrimary ? "主要" : "一般"
|
|
181
|
+
]
|
|
182
|
+
}, e.id)) : /* @__PURE__ */ y(h, {
|
|
183
|
+
color: "text-neutral",
|
|
184
|
+
variant: "body",
|
|
185
|
+
children: "尚未建立 BPM 組織歸屬"
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
}),
|
|
189
|
+
/* @__PURE__ */ y(a, {
|
|
190
|
+
title: "主管解析",
|
|
191
|
+
children: /* @__PURE__ */ y(h, {
|
|
192
|
+
variant: "body",
|
|
193
|
+
children: s?.managerMemberId ? j(e) : "尚未解析到主管"
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
]
|
|
197
|
+
}) : null
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function k(e) {
|
|
201
|
+
return e ? `${e.name} · ${e.code}` : "未知組織";
|
|
202
|
+
}
|
|
203
|
+
function A(e) {
|
|
204
|
+
return e ? `${e.name} · ${e.code}` : "未知職位";
|
|
205
|
+
}
|
|
206
|
+
function j(t) {
|
|
207
|
+
return t ? e(t.name, t.email, "dot") : "主管資料尚未載入";
|
|
208
|
+
}
|
|
209
|
+
function M(e) {
|
|
210
|
+
return e instanceof Error ? e.message : "讀取會員資料失敗。";
|
|
211
|
+
}
|
|
212
|
+
//#endregion
|
|
213
|
+
export { O as t };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=users-BSVo_eNe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"users-BSVo_eNe.js","names":["_Fragment","styles","formatMemberLabel","Fragment$1","ContentHeader","users_module_default","MEMBER_PAGE_SIZE_OPTIONS","AdminUsersView","detailMember","setDetailMember","useState","detailMemberships","setDetailMemberships","detailManagerProfile","setDetailManagerProfile","detailResolvedManager","setDetailResolvedManager","error","setError","loading","setLoading","memberPage","setMemberPage","memberPageSize","setMemberPageSize","memberTotalCount","setMemberTotalCount","members","setMembers","orgUnits","setOrgUnits","positions","setPositions","searchText","setSearchText","orgUnitsById","useMemo","orgUnit","positionsById","position","refreshMembers","useCallback","memberPageResult","organization","listMemberDirectoryPage","readOrganizationDashboard","requestError","readErrorMessage","rows","member","openDetail","memberships","resolvedManager","listMemberships","readResolvedManager","resolveMembers","columns","tableActions","record","closeDetail","PageHeader","SectionGroup","Section","FilterArea","FilterLine","Filter","FormField","FormFieldLayout","Input","event","Typography","Table","page","pageSize","from","to","total","MemberDetailModal","Modal","onClose","BaseCard","readOrgUnitLabel","membership","readPositionLabel","readMemberLabel","managerProfile"],"sources":["../../src/views/admin/users/users.module.scss","../../src/views/admin/users/AdminUsersView.tsx"],"sourcesContent":[".memberFilterArea {\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 :global(.mzn-input),\n :global(.mzn-input-container),\n :global(.mzn-text-field) {\n width: 100%;\n min-width: 0 !important;\n }\n}\n\n.header {\n display: grid;\n gap: 4px;\n padding: 24px 24px 0;\n}\n\n.detailFields {\n display: grid;\n gap: 16px;\n}\n\n.detailSection {\n display: grid;\n gap: 8px;\n}\n\n.membershipList {\n display: grid;\n gap: 8px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n ReactElement,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n BaseCard,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { formatMemberLabel } from '../../../lib/format-member-label';\nimport styles from './users.module.scss';\nimport {\n listMemberDirectoryPage,\n MemberProfileRecord,\n resolveMembers,\n} from '@rytass/bpm-core-client';\nimport {\n listMemberships,\n MembershipRecord,\n OrgUnitRecord,\n PositionRecord,\n readOrganizationDashboard,\n readResolvedManager,\n ResolvedManagerRecord,\n} from '@rytass/bpm-core-client/organization';\n\ntype MemberRow = Readonly<\n Record<string, unknown> &\n MemberProfileRecord & {\n key: string;\n }\n>;\n\nconst MEMBER_PAGE_SIZE_OPTIONS = [10, 20, 50];\n\nexport function AdminUsersView(): ReactElement {\n const [detailMember, setDetailMember] = useState<MemberProfileRecord | null>(\n null,\n );\n const [detailMemberships, setDetailMemberships] = useState<\n readonly MembershipRecord[]\n >([]);\n const [detailManagerProfile, setDetailManagerProfile] =\n useState<MemberProfileRecord | null>(null);\n const [detailResolvedManager, setDetailResolvedManager] =\n useState<ResolvedManagerRecord | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [memberPage, setMemberPage] = useState(1);\n const [memberPageSize, setMemberPageSize] = useState(10);\n const [memberTotalCount, setMemberTotalCount] = useState(0);\n const [members, setMembers] = useState<readonly MemberProfileRecord[]>([]);\n const [orgUnits, setOrgUnits] = useState<readonly OrgUnitRecord[]>([]);\n const [positions, setPositions] = useState<readonly PositionRecord[]>([]);\n const [searchText, setSearchText] = useState('');\n\n const orgUnitsById = useMemo(\n (): ReadonlyMap<string, OrgUnitRecord> =>\n new Map(orgUnits.map((orgUnit) => [orgUnit.id, orgUnit])),\n [orgUnits],\n );\n const positionsById = useMemo(\n (): ReadonlyMap<string, PositionRecord> =>\n new Map(positions.map((position) => [position.id, position])),\n [positions],\n );\n\n const refreshMembers = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const [memberPageResult, organization] = await Promise.all([\n listMemberDirectoryPage({\n page: memberPage,\n pageSize: memberPageSize,\n searchText,\n }),\n readOrganizationDashboard(),\n ]);\n\n setMembers(memberPageResult.members);\n setMemberTotalCount(memberPageResult.totalCount);\n setOrgUnits(organization.orgUnits);\n setPositions(organization.positions);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [memberPage, memberPageSize, searchText]);\n\n useEffect((): void => {\n void refreshMembers();\n }, [refreshMembers]);\n\n const rows = useMemo(\n (): MemberRow[] =>\n members.map((member) => ({\n ...member,\n key: member.memberId,\n })),\n [members],\n );\n\n const openDetail = useCallback(\n async (member: MemberProfileRecord): Promise<void> => {\n setDetailMember(member);\n setDetailManagerProfile(null);\n setError(null);\n\n try {\n const [memberships, resolvedManager] = await Promise.all([\n listMemberships({ memberId: member.memberId }),\n readResolvedManager(member.memberId),\n ]);\n\n setDetailMemberships(memberships);\n setDetailResolvedManager(resolvedManager);\n setDetailManagerProfile(\n (\n await resolveMembers(\n resolvedManager.managerMemberId\n ? [resolvedManager.managerMemberId]\n : [],\n )\n )[0] ?? null,\n );\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n }\n },\n [],\n );\n\n const columns = useMemo(\n (): TableColumn<MemberRow>[] => [\n { dataIndex: 'name', key: 'name', title: '姓名', width: 160 },\n { dataIndex: 'email', key: 'email', title: '信箱', width: 260 },\n ],\n [],\n );\n const tableActions = useMemo(\n (): TableActions<MemberRow> => ({\n render: (): ReturnType<TableActions<MemberRow>['render']> => [\n {\n name: '檢視',\n onClick: (record): void => {\n void openDetail(record);\n },\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [openDetail],\n );\n\n function closeDetail(): void {\n setDetailMember(null);\n setDetailManagerProfile(null);\n setDetailMemberships([]);\n setDetailResolvedManager(null);\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"會員資料由 host member resolver 提供,BPM 僅維護組織歸屬與主管解析。\"\n title=\"會員對照\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.memberFilterArea}>\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"memberSearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setMemberPage(1);\n }}\n placeholder=\"搜尋姓名或信箱\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n loading={loading}\n pagination={{\n current: memberPage,\n onChange: (page): void => {\n setMemberPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setMemberPage(1);\n setMemberPageSize(pageSize);\n },\n pageSize: memberPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: MEMBER_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: memberTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n\n <MemberDetailModal\n managerProfile={detailManagerProfile}\n member={detailMember}\n memberships={detailMemberships}\n onClose={closeDetail}\n orgUnitsById={orgUnitsById}\n positionsById={positionsById}\n resolvedManager={detailResolvedManager}\n />\n </>\n );\n}\n\nfunction MemberDetailModal({\n managerProfile,\n member,\n memberships,\n onClose,\n orgUnitsById,\n positionsById,\n resolvedManager,\n}: {\n readonly managerProfile: MemberProfileRecord | null;\n readonly member: MemberProfileRecord | null;\n readonly memberships: readonly MembershipRecord[];\n readonly onClose: () => void;\n readonly orgUnitsById: ReadonlyMap<string, OrgUnitRecord>;\n readonly positionsById: ReadonlyMap<string, PositionRecord>;\n readonly resolvedManager: ResolvedManagerRecord | null;\n}): ReactElement {\n return (\n <Modal\n cancelText=\"關閉\"\n confirmText=\"關閉\"\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={onClose}\n open={Boolean(member)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={member?.name ?? '會員明細'}\n >\n {member ? (\n <div className={styles.detailFields}>\n <BaseCard title=\"基本資料\">\n <div className={styles.detailSection}>\n <Typography variant=\"body\">\n 信箱:{member.email?.trim() ? member.email : '未設定'}\n </Typography>\n </div>\n </BaseCard>\n <BaseCard title=\"BPM 組織歸屬\">\n <div className={styles.membershipList}>\n {memberships.length ? (\n memberships.map((membership) => (\n <Typography key={membership.id} variant=\"body\">\n {readOrgUnitLabel(orgUnitsById.get(membership.orgUnitId))}\n {' / '}\n {membership.positionId\n ? readPositionLabel(\n positionsById.get(membership.positionId),\n )\n : '未指定職位'}\n {' / '}\n {membership.isPrimary ? '主要' : '一般'}\n </Typography>\n ))\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 尚未建立 BPM 組織歸屬\n </Typography>\n )}\n </div>\n </BaseCard>\n <BaseCard title=\"主管解析\">\n <Typography variant=\"body\">\n {resolvedManager?.managerMemberId\n ? readMemberLabel(managerProfile)\n : '尚未解析到主管'}\n </Typography>\n </BaseCard>\n </div>\n ) : null}\n </Modal>\n );\n}\n\nfunction readOrgUnitLabel(orgUnit: OrgUnitRecord | undefined): string {\n return orgUnit ? `${orgUnit.name} · ${orgUnit.code}` : '未知組織';\n}\n\nfunction readPositionLabel(position: PositionRecord | undefined): string {\n return position ? `${position.name} · ${position.code}` : '未知職位';\n}\n\nfunction readMemberLabel(member: MemberProfileRecord | null): string {\n return member\n ? formatMemberLabel(member.name, member.email, 'dot')\n : '主管資料尚未載入';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '讀取會員資料失敗。';\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";require('../users.css');const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./format-member-label-t7ANTF62.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/core/form"),s=require("@mezzanine-ui/react/ContentHeader");s=e.t(s,1);let c=require("@rytass/bpm-core-client/organization");var l={memberFilterArea:`bpm_memberFilterArea_9RB2k`,header:`bpm_header_sIEtg`,detailFields:`bpm_detailFields_TyKNL`,detailSection:`bpm_detailSection_-iLjy`,membershipList:`bpm_membershipList_5n9iq`},u=[10,20,50];function d(){let[e,t]=(0,n.useState)(null),[d,p]=(0,n.useState)([]),[m,h]=(0,n.useState)(null),[_,v]=(0,n.useState)(null),[y,b]=(0,n.useState)(null),[x,S]=(0,n.useState)(!0),[C,w]=(0,n.useState)(1),[T,E]=(0,n.useState)(10),[D,O]=(0,n.useState)(0),[k,A]=(0,n.useState)([]),[j,M]=(0,n.useState)([]),[N,P]=(0,n.useState)([]),[F,I]=(0,n.useState)(``),L=(0,n.useMemo)(()=>new Map(j.map(e=>[e.id,e])),[j]),R=(0,n.useMemo)(()=>new Map(N.map(e=>[e.id,e])),[N]),z=(0,n.useCallback)(async()=>{S(!0),b(null);try{let[e,t]=await Promise.all([(0,i.listMemberDirectoryPage)({page:C,pageSize:T,searchText:F}),(0,c.readOrganizationDashboard)()]);A(e.members),O(e.totalCount),M(t.orgUnits),P(t.positions)}catch(e){b(g(e))}finally{S(!1)}},[C,T,F]);(0,n.useEffect)(()=>{z()},[z]);let B=(0,n.useMemo)(()=>k.map(e=>({...e,key:e.memberId})),[k]),V=(0,n.useCallback)(async e=>{t(e),h(null),b(null);try{let[t,n]=await Promise.all([(0,c.listMemberships)({memberId:e.memberId}),(0,c.readResolvedManager)(e.memberId)]);p(t),v(n),h((await(0,i.resolveMembers)(n.managerMemberId?[n.managerMemberId]:[]))[0]??null)}catch(e){b(g(e))}},[]),H=(0,n.useMemo)(()=>[{dataIndex:`name`,key:`name`,title:`姓名`,width:160},{dataIndex:`email`,key:`email`,title:`信箱`,width:260}],[]),U=(0,n.useMemo)(()=>({render:()=>[{name:`檢視`,onClick:e=>{V(e)}}],variant:`base-secondary`,width:88}),[V]);function W(){t(null),h(null),p([]),v(null)}return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(r.PageHeader,{children:(0,a.jsx)(s.default,{description:`會員資料由 host member resolver 提供,BPM 僅維護組織歸屬與主管解析。`,title:`會員對照`})}),(0,a.jsx)(r.SectionGroup,{children:(0,a.jsxs)(r.Section,{filterArea:(0,a.jsx)(r.FilterArea,{className:l.memberFilterArea,children:(0,a.jsx)(r.FilterLine,{children:(0,a.jsx)(r.Filter,{span:3,children:(0,a.jsx)(r.FormField,{fullWidth:!0,layout:o.FormFieldLayout.VERTICAL,name:`memberSearchText`,children:(0,a.jsx)(r.Input,{fullWidth:!0,onChange:e=>{I(e.target.value),w(1)},placeholder:`搜尋姓名或信箱`,size:`sub`,value:F,variant:`base`})})})})}),children:[y?(0,a.jsx)(r.Typography,{color:`text-error`,variant:`body`,children:y}):null,(0,a.jsx)(r.Table,{actions:U,columns:H,dataSource:B,fullWidth:!0,loading:x,pagination:{current:C,onChange:e=>{w(e)},onChangePageSize:e=>{w(1),E(e)},pageSize:T,pageSizeLabel:`每頁筆數`,pageSizeOptions:u,renderResultSummary:(e,t,n)=>`顯示 ${e}-${t} 筆,共 ${n} 筆`,showPageSizeOptions:!0,total:D}})]})}),(0,a.jsx)(f,{managerProfile:m,member:e,memberships:d,onClose:W,orgUnitsById:L,positionsById:R,resolvedManager:_})]})}function f({managerProfile:e,member:t,memberships:n,onClose:i,orgUnitsById:o,positionsById:s,resolvedManager:c}){return(0,a.jsx)(r.Modal,{cancelText:`關閉`,confirmText:`關閉`,modalType:`standard`,onCancel:i,onClose:i,onConfirm:i,open:!!t,showModalFooter:!0,showModalHeader:!0,size:`regular`,title:t?.name??`會員明細`,children:t?(0,a.jsxs)(`div`,{className:l.detailFields,children:[(0,a.jsx)(r.BaseCard,{title:`基本資料`,children:(0,a.jsx)(`div`,{className:l.detailSection,children:(0,a.jsxs)(r.Typography,{variant:`body`,children:[`信箱:`,t.email?.trim()?t.email:`未設定`]})})}),(0,a.jsx)(r.BaseCard,{title:`BPM 組織歸屬`,children:(0,a.jsx)(`div`,{className:l.membershipList,children:n.length?n.map(e=>(0,a.jsxs)(r.Typography,{variant:`body`,children:[p(o.get(e.orgUnitId)),` / `,e.positionId?m(s.get(e.positionId)):`未指定職位`,` / `,e.isPrimary?`主要`:`一般`]},e.id)):(0,a.jsx)(r.Typography,{color:`text-neutral`,variant:`body`,children:`尚未建立 BPM 組織歸屬`})})}),(0,a.jsx)(r.BaseCard,{title:`主管解析`,children:(0,a.jsx)(r.Typography,{variant:`body`,children:c?.managerMemberId?h(e):`尚未解析到主管`})})]}):null})}function p(e){return e?`${e.name} · ${e.code}`:`未知組織`}function m(e){return e?`${e.name} · ${e.code}`:`未知職位`}function h(e){return e?t.t(e.name,e.email,`dot`):`主管資料尚未載入`}function g(e){return e instanceof Error?e.message:`讀取會員資料失敗。`}Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return d}});
|
|
2
|
+
//# sourceMappingURL=users-DFSBTDMy.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"users-DFSBTDMy.cjs","names":["useState","useMemo","useCallback","listMemberDirectoryPage","readOrganizationDashboard","listMemberships","readResolvedManager","resolveMembers","_Fragment","PageHeader","ContentHeader","SectionGroup","Section","FilterArea","styles","FilterLine","Filter","FormField","FormFieldLayout","Input","Typography","Table","Modal","BaseCard","formatMemberLabel","require_rolldown_runtime","require_format_member_label","react","_mezzanine_ui_react","_rytass_bpm_core_client","react_jsx_runtime","_mezzanine_ui_core_form","_mezzanine_ui_react_ContentHeader","_rytass_bpm_core_client_organization","users_module_default","MEMBER_PAGE_SIZE_OPTIONS","AdminUsersView","detailMember","setDetailMember","detailMemberships","setDetailMemberships","detailManagerProfile","setDetailManagerProfile","detailResolvedManager","setDetailResolvedManager","error","setError","loading","setLoading","memberPage","setMemberPage","memberPageSize","setMemberPageSize","memberTotalCount","setMemberTotalCount","members","setMembers","orgUnits","setOrgUnits","positions","setPositions","searchText","setSearchText","orgUnitsById","orgUnit","positionsById","position","refreshMembers","memberPageResult","organization","requestError","readErrorMessage","rows","member","openDetail","memberships","resolvedManager","columns","tableActions","record","closeDetail","event","page","pageSize","from","to","total","MemberDetailModal","onClose","readOrgUnitLabel","membership","readPositionLabel","readMemberLabel","managerProfile"],"sources":["../../src/views/admin/users/users.module.scss","../../src/views/admin/users/AdminUsersView.tsx"],"sourcesContent":[".memberFilterArea {\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 :global(.mzn-input),\n :global(.mzn-input-container),\n :global(.mzn-text-field) {\n width: 100%;\n min-width: 0 !important;\n }\n}\n\n.header {\n display: grid;\n gap: 4px;\n padding: 24px 24px 0;\n}\n\n.detailFields {\n display: grid;\n gap: 16px;\n}\n\n.detailSection {\n display: grid;\n gap: 8px;\n}\n\n.membershipList {\n display: grid;\n gap: 8px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n ReactElement,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n BaseCard,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { formatMemberLabel } from '../../../lib/format-member-label';\nimport styles from './users.module.scss';\nimport {\n listMemberDirectoryPage,\n MemberProfileRecord,\n resolveMembers,\n} from '@rytass/bpm-core-client';\nimport {\n listMemberships,\n MembershipRecord,\n OrgUnitRecord,\n PositionRecord,\n readOrganizationDashboard,\n readResolvedManager,\n ResolvedManagerRecord,\n} from '@rytass/bpm-core-client/organization';\n\ntype MemberRow = Readonly<\n Record<string, unknown> &\n MemberProfileRecord & {\n key: string;\n }\n>;\n\nconst MEMBER_PAGE_SIZE_OPTIONS = [10, 20, 50];\n\nexport function AdminUsersView(): ReactElement {\n const [detailMember, setDetailMember] = useState<MemberProfileRecord | null>(\n null,\n );\n const [detailMemberships, setDetailMemberships] = useState<\n readonly MembershipRecord[]\n >([]);\n const [detailManagerProfile, setDetailManagerProfile] =\n useState<MemberProfileRecord | null>(null);\n const [detailResolvedManager, setDetailResolvedManager] =\n useState<ResolvedManagerRecord | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [memberPage, setMemberPage] = useState(1);\n const [memberPageSize, setMemberPageSize] = useState(10);\n const [memberTotalCount, setMemberTotalCount] = useState(0);\n const [members, setMembers] = useState<readonly MemberProfileRecord[]>([]);\n const [orgUnits, setOrgUnits] = useState<readonly OrgUnitRecord[]>([]);\n const [positions, setPositions] = useState<readonly PositionRecord[]>([]);\n const [searchText, setSearchText] = useState('');\n\n const orgUnitsById = useMemo(\n (): ReadonlyMap<string, OrgUnitRecord> =>\n new Map(orgUnits.map((orgUnit) => [orgUnit.id, orgUnit])),\n [orgUnits],\n );\n const positionsById = useMemo(\n (): ReadonlyMap<string, PositionRecord> =>\n new Map(positions.map((position) => [position.id, position])),\n [positions],\n );\n\n const refreshMembers = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const [memberPageResult, organization] = await Promise.all([\n listMemberDirectoryPage({\n page: memberPage,\n pageSize: memberPageSize,\n searchText,\n }),\n readOrganizationDashboard(),\n ]);\n\n setMembers(memberPageResult.members);\n setMemberTotalCount(memberPageResult.totalCount);\n setOrgUnits(organization.orgUnits);\n setPositions(organization.positions);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [memberPage, memberPageSize, searchText]);\n\n useEffect((): void => {\n void refreshMembers();\n }, [refreshMembers]);\n\n const rows = useMemo(\n (): MemberRow[] =>\n members.map((member) => ({\n ...member,\n key: member.memberId,\n })),\n [members],\n );\n\n const openDetail = useCallback(\n async (member: MemberProfileRecord): Promise<void> => {\n setDetailMember(member);\n setDetailManagerProfile(null);\n setError(null);\n\n try {\n const [memberships, resolvedManager] = await Promise.all([\n listMemberships({ memberId: member.memberId }),\n readResolvedManager(member.memberId),\n ]);\n\n setDetailMemberships(memberships);\n setDetailResolvedManager(resolvedManager);\n setDetailManagerProfile(\n (\n await resolveMembers(\n resolvedManager.managerMemberId\n ? [resolvedManager.managerMemberId]\n : [],\n )\n )[0] ?? null,\n );\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n }\n },\n [],\n );\n\n const columns = useMemo(\n (): TableColumn<MemberRow>[] => [\n { dataIndex: 'name', key: 'name', title: '姓名', width: 160 },\n { dataIndex: 'email', key: 'email', title: '信箱', width: 260 },\n ],\n [],\n );\n const tableActions = useMemo(\n (): TableActions<MemberRow> => ({\n render: (): ReturnType<TableActions<MemberRow>['render']> => [\n {\n name: '檢視',\n onClick: (record): void => {\n void openDetail(record);\n },\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [openDetail],\n );\n\n function closeDetail(): void {\n setDetailMember(null);\n setDetailManagerProfile(null);\n setDetailMemberships([]);\n setDetailResolvedManager(null);\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"會員資料由 host member resolver 提供,BPM 僅維護組織歸屬與主管解析。\"\n title=\"會員對照\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.memberFilterArea}>\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"memberSearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setMemberPage(1);\n }}\n placeholder=\"搜尋姓名或信箱\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n loading={loading}\n pagination={{\n current: memberPage,\n onChange: (page): void => {\n setMemberPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setMemberPage(1);\n setMemberPageSize(pageSize);\n },\n pageSize: memberPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: MEMBER_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: memberTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n\n <MemberDetailModal\n managerProfile={detailManagerProfile}\n member={detailMember}\n memberships={detailMemberships}\n onClose={closeDetail}\n orgUnitsById={orgUnitsById}\n positionsById={positionsById}\n resolvedManager={detailResolvedManager}\n />\n </>\n );\n}\n\nfunction MemberDetailModal({\n managerProfile,\n member,\n memberships,\n onClose,\n orgUnitsById,\n positionsById,\n resolvedManager,\n}: {\n readonly managerProfile: MemberProfileRecord | null;\n readonly member: MemberProfileRecord | null;\n readonly memberships: readonly MembershipRecord[];\n readonly onClose: () => void;\n readonly orgUnitsById: ReadonlyMap<string, OrgUnitRecord>;\n readonly positionsById: ReadonlyMap<string, PositionRecord>;\n readonly resolvedManager: ResolvedManagerRecord | null;\n}): ReactElement {\n return (\n <Modal\n cancelText=\"關閉\"\n confirmText=\"關閉\"\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={onClose}\n open={Boolean(member)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={member?.name ?? '會員明細'}\n >\n {member ? (\n <div className={styles.detailFields}>\n <BaseCard title=\"基本資料\">\n <div className={styles.detailSection}>\n <Typography variant=\"body\">\n 信箱:{member.email?.trim() ? member.email : '未設定'}\n </Typography>\n </div>\n </BaseCard>\n <BaseCard title=\"BPM 組織歸屬\">\n <div className={styles.membershipList}>\n {memberships.length ? (\n memberships.map((membership) => (\n <Typography key={membership.id} variant=\"body\">\n {readOrgUnitLabel(orgUnitsById.get(membership.orgUnitId))}\n {' / '}\n {membership.positionId\n ? readPositionLabel(\n positionsById.get(membership.positionId),\n )\n : '未指定職位'}\n {' / '}\n {membership.isPrimary ? '主要' : '一般'}\n </Typography>\n ))\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 尚未建立 BPM 組織歸屬\n </Typography>\n )}\n </div>\n </BaseCard>\n <BaseCard title=\"主管解析\">\n <Typography variant=\"body\">\n {resolvedManager?.managerMemberId\n ? readMemberLabel(managerProfile)\n : '尚未解析到主管'}\n </Typography>\n </BaseCard>\n </div>\n ) : null}\n </Modal>\n );\n}\n\nfunction readOrgUnitLabel(orgUnit: OrgUnitRecord | undefined): string {\n return orgUnit ? `${orgUnit.name} · ${orgUnit.code}` : '未知組織';\n}\n\nfunction readPositionLabel(position: PositionRecord | undefined): string {\n return position ? `${position.name} · ${position.code}` : '未知職位';\n}\n\nfunction readMemberLabel(member: MemberProfileRecord | null): string {\n return member\n ? formatMemberLabel(member.name, member.email, 'dot')\n : '主管資料尚未載入';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '讀取會員資料失敗。';\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});require('./index.css');const e=require("./chunks/router-adapter--gYs13E8.cjs"),t=require("./chunks/auth-provider-4BeCw7cI.cjs"),n=require("./chunks/format-date-time-D-cx5M8z.cjs"),r=require("./chunks/providers-DHtDZFNA.cjs"),i=require("./chunks/routes-config-C1j_vFKo.cjs"),a=require("./chunks/bpm-form-field-Bc6k4ZEO.cjs"),o=require("./chunks/use-bpm-member-DpNAQtNC.cjs"),s=require("./chunks/admin-pickers-
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});require('./index.css');const e=require("./chunks/router-adapter--gYs13E8.cjs"),t=require("./chunks/auth-provider-4BeCw7cI.cjs"),n=require("./chunks/format-date-time-D-cx5M8z.cjs"),r=require("./chunks/providers-DHtDZFNA.cjs"),i=require("./chunks/routes-config-C1j_vFKo.cjs"),a=require("./chunks/bpm-form-field-Bc6k4ZEO.cjs"),o=require("./chunks/use-bpm-member-DpNAQtNC.cjs"),s=require("./chunks/admin-pickers-qe3j--gX.cjs"),c=require("./chunks/approval-instance-list-page-ax0pm8bt.cjs"),l=require("./chunks/dashboard-page-BWQBSGDq.cjs");let u=require("@mezzanine-ui/react"),d=require("react/jsx-runtime"),f=require("@mezzanine-ui/icons");function p(){let{logout:e}=t.n();return e}var m={root:`bpm_root_jl5S-`,badge:`bpm_badge_SIKJk`};function h({label:e=`通知中心`}={}){let{open:t}=r.o(),{unreadCount:n}=r.i(),i=n>0?`${e},${n} 則未讀`:e;return(0,d.jsxs)(`span`,{className:m.root,children:[(0,d.jsx)(u.NavigationIconButton,{"aria-label":i,icon:f.NotificationUnreadIcon,onClick:()=>{t()},title:e,type:`button`}),n>0?(0,d.jsx)(`span`,{className:m.badge,children:n>99?`99+`:n}):null]})}exports.ApprovalInstanceListPage=c.t,exports.AuthProvider=t.t,exports.BPMFormField=a.t,exports.BPMNotificationBellButton=h,exports.BPMRoutesProvider=i.t,exports.DashboardPage=l.t,exports.MemberPicker=s.t,exports.NotificationDrawer=r.n,exports.NotificationDrawerProvider=r.a,exports.NotificationUnreadProvider=r.r,exports.OrgUnitPicker=s.n,exports.PositionPicker=s.r,exports.Providers=r.t,exports.RouterAdapterProvider=e.t,exports.createDefaultBPMRoutes=i.n,exports.defaultBrowserSearchParams=e.n,exports.formatDateTime=n.t,exports.isBPMAdminMember=o.t,exports.readMemberOption=s.i,exports.readOrgUnitOption=s.a,exports.readPositionOption=s.o,exports.useAuth=t.n,exports.useBPMLogout=p,exports.useBPMMember=o.n,exports.useBPMRoutes=i.r,exports.useNotificationDrawer=r.o,exports.useNotificationUnread=r.i,exports.useRouterAdapter=e.r;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/lib/use-bpm-logout.ts","../src/components/bpm-notification-bell-button.module.scss","../src/components/bpm-notification-bell-button.tsx"],"sourcesContent":["'use client';\n\nimport { useAuth } from './auth-provider';\n\n/**\n * Drop-in logout handler for host navigation menus. Wraps the BPM auth\n * context's logout flow — calls `logoutApi()` against the BPM REST\n * session endpoint, clears the in-memory member, then redirects to the\n * configured `loginPath`.\n *\n * Hosts mount this on their own logout buttons / menu items so they do\n * not need to touch `useAuth()` directly. To customize the post-logout\n * destination, override the `loginPath` prop on `<BPMNextProviders>` or\n * `<AuthProvider>`.\n */\nexport function useBPMLogout(): () => Promise<void> {\n const { logout } = useAuth();\n return logout;\n}\n",".root {\n display: inline-flex;\n position: relative;\n}\n\n.badge {\n align-items: center;\n background: #d92d20;\n border: 1px solid #fff;\n border-radius: 999px;\n color: #fff;\n display: inline-flex;\n font-size: 10px;\n font-weight: 600;\n height: 16px;\n justify-content: center;\n line-height: 1;\n min-width: 16px;\n padding: 0 4px;\n pointer-events: none;\n position: absolute;\n right: -4px;\n top: -4px;\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport { NavigationIconButton } from '@mezzanine-ui/react';\nimport { NotificationUnreadIcon } from '@mezzanine-ui/icons';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport styles from './bpm-notification-bell-button.module.scss';\n\nexport interface BPMNotificationBellButtonProps {\n /** Override the aria-label / tooltip. Defaults to `通知中心`. */\n readonly label?: string;\n}\n\n/**\n * Drop-in notification bell. Reads the unread count from\n * `<NotificationUnreadProvider>`, opens the BPM `<NotificationDrawer />`\n * mounted by `<Providers>` (or `<BPMNextProviders>`) on click, and\n * renders a small red badge with the unread count.\n *\n * Use this when the host navigation wants the BPM notification UX with\n * minimum wiring. Hosts that need a fully custom button can skip this\n * widget and consume `useNotificationDrawer()` + `useNotificationUnread()`\n * directly to wire their own trigger.\n *\n * Visual: uses Mezzanine `NavigationIconButton` so the bell aligns with\n * surrounding Mezzanine navigation chrome. The button is decoupled from\n * the `<Navigation>` container — it does not require a Mezzanine\n * navigation tree to render.\n */\nexport function BPMNotificationBellButton({\n label = '通知中心',\n}: BPMNotificationBellButtonProps = {}): ReactElement {\n const { open } = useNotificationDrawer();\n const { unreadCount } = useNotificationUnread();\n const ariaLabel = unreadCount > 0 ? `${label},${unreadCount} 則未讀` : label;\n return (\n <span className={styles.root}>\n <NavigationIconButton\n aria-label={ariaLabel}\n icon={NotificationUnreadIcon}\n onClick={(): void => {\n open();\n }}\n title={label}\n type=\"button\"\n />\n {unreadCount > 0 ? (\n <span className={styles.badge}>\n {unreadCount > 99 ? '99+' : unreadCount}\n </span>\n ) : null}\n </span>\n );\n}\n"],"mappings":"srBAeA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["useAuth","useNotificationDrawer","useNotificationUnread","styles","NavigationIconButton","NotificationUnreadIcon","require_router_adapter","require_auth_provider","require_format_date_time","require_providers","require_routes_config","require_bpm_form_field","require_use_bpm_member","require_admin_pickers","require_approval_instance_list_page","require_dashboard_page","_mezzanine_ui_react","react_jsx_runtime","_mezzanine_ui_icons","useBPMLogout","logout","bpm_notification_bell_button_module_default","BPMNotificationBellButton","ariaLabel","unreadCount","label","open"],"sources":["../src/lib/use-bpm-logout.ts","../src/components/bpm-notification-bell-button.module.scss","../src/components/bpm-notification-bell-button.tsx"],"sourcesContent":["'use client';\n\nimport { useAuth } from './auth-provider';\n\n/**\n * Drop-in logout handler for host navigation menus. Wraps the BPM auth\n * context's logout flow — calls `logoutApi()` against the BPM REST\n * session endpoint, clears the in-memory member, then redirects to the\n * configured `loginPath`.\n *\n * Hosts mount this on their own logout buttons / menu items so they do\n * not need to touch `useAuth()` directly. To customize the post-logout\n * destination, override the `loginPath` prop on `<BPMNextProviders>` or\n * `<AuthProvider>`.\n */\nexport function useBPMLogout(): () => Promise<void> {\n const { logout } = useAuth();\n return logout;\n}\n",".root {\n display: inline-flex;\n position: relative;\n}\n\n.badge {\n align-items: center;\n background: #d92d20;\n border: 1px solid #fff;\n border-radius: 999px;\n color: #fff;\n display: inline-flex;\n font-size: 10px;\n font-weight: 600;\n height: 16px;\n justify-content: center;\n line-height: 1;\n min-width: 16px;\n padding: 0 4px;\n pointer-events: none;\n position: absolute;\n right: -4px;\n top: -4px;\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport { NavigationIconButton } from '@mezzanine-ui/react';\nimport { NotificationUnreadIcon } from '@mezzanine-ui/icons';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport styles from './bpm-notification-bell-button.module.scss';\n\nexport interface BPMNotificationBellButtonProps {\n /** Override the aria-label / tooltip. Defaults to `通知中心`. */\n readonly label?: string;\n}\n\n/**\n * Drop-in notification bell. Reads the unread count from\n * `<NotificationUnreadProvider>`, opens the BPM `<NotificationDrawer />`\n * mounted by `<Providers>` (or `<BPMNextProviders>`) on click, and\n * renders a small red badge with the unread count.\n *\n * Use this when the host navigation wants the BPM notification UX with\n * minimum wiring. Hosts that need a fully custom button can skip this\n * widget and consume `useNotificationDrawer()` + `useNotificationUnread()`\n * directly to wire their own trigger.\n *\n * Visual: uses Mezzanine `NavigationIconButton` so the bell aligns with\n * surrounding Mezzanine navigation chrome. The button is decoupled from\n * the `<Navigation>` container — it does not require a Mezzanine\n * navigation tree to render.\n */\nexport function BPMNotificationBellButton({\n label = '通知中心',\n}: BPMNotificationBellButtonProps = {}): ReactElement {\n const { open } = useNotificationDrawer();\n const { unreadCount } = useNotificationUnread();\n const ariaLabel = unreadCount > 0 ? `${label},${unreadCount} 則未讀` : label;\n return (\n <span className={styles.root}>\n <NavigationIconButton\n aria-label={ariaLabel}\n icon={NotificationUnreadIcon}\n onClick={(): void => {\n open();\n }}\n title={label}\n type=\"button\"\n />\n {unreadCount > 0 ? (\n <span className={styles.badge}>\n {unreadCount > 99 ? '99+' : unreadCount}\n </span>\n ) : null}\n </span>\n );\n}\n"],"mappings":"srBAeA,SAAgBmB,GAAoC,CAClD,GAAM,CAAE,UAAWnB,EAAAA,EAAQ,EAC3B,OAAOoB,CACT,uDEYA,SAAgBE,EAA0B,CACxC,QAAQ,QAC0B,CAAC,EAAiB,CACpD,GAAM,CAAE,QAASrB,EAAAA,EAAsB,EACjC,CAAE,eAAgBC,EAAAA,EAAsB,EACxCqB,EAAYC,EAAc,EAAI,GAAGC,EAAM,GAAGD,EAAY,MAAQC,EACpE,OACE,EAAAR,EAAA,KAAA,CAAC,OAAD,CAAM,UAAWd,EAAO,KAAxB,SAAA,EACE,EAAAc,EAAA,IAAA,CAACb,EAAAA,qBAAD,CACE,aAAYmB,EACZ,KAAMlB,EAAAA,uBACN,YAAqB,CACnBqB,EAAK,CACP,EACA,MAAOD,EACP,KAAK,QACN,CAAA,EACAD,EAAc,GACb,EAAAP,EAAA,IAAA,CAAC,OAAD,CAAM,UAAWd,EAAO,MACrB,SAAAqB,EAAc,GAAK,MAAQA,CACxB,CAAA,EACJ,IACA,GAEV"}
|
package/dist/index.js
CHANGED
|
@@ -6,9 +6,9 @@ import { a as o, i as s, n as c, o as l, r as u, t as d } from "./chunks/provide
|
|
|
6
6
|
import { n as f, r as p, t as m } from "./chunks/routes-config-yZbESPlw.js";
|
|
7
7
|
import { t as h } from "./chunks/bpm-form-field-Cao0rMol.js";
|
|
8
8
|
import { n as g, t as _ } from "./chunks/use-bpm-member-D57LKe_u.js";
|
|
9
|
-
import { a as v, i as y, n as b, o as x, r as S, t as C } from "./chunks/admin-pickers-
|
|
9
|
+
import { a as v, i as y, n as b, o as x, r as S, t as C } from "./chunks/admin-pickers-DxhzECkD.js";
|
|
10
10
|
import { t as w } from "./chunks/approval-instance-list-page-CPXMP3PL.js";
|
|
11
|
-
import { t as T } from "./chunks/dashboard-page-
|
|
11
|
+
import { t as T } from "./chunks/dashboard-page-DdBvV6YI.js";
|
|
12
12
|
import { NavigationIconButton as E } from "@mezzanine-ui/react";
|
|
13
13
|
import { jsx as D, jsxs as O } from "react/jsx-runtime";
|
|
14
14
|
import { NotificationUnreadIcon as k } from "@mezzanine-ui/icons";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/lib/use-bpm-logout.ts","../src/components/bpm-notification-bell-button.module.scss","../src/components/bpm-notification-bell-button.tsx"],"sourcesContent":["'use client';\n\nimport { useAuth } from './auth-provider';\n\n/**\n * Drop-in logout handler for host navigation menus. Wraps the BPM auth\n * context's logout flow — calls `logoutApi()` against the BPM REST\n * session endpoint, clears the in-memory member, then redirects to the\n * configured `loginPath`.\n *\n * Hosts mount this on their own logout buttons / menu items so they do\n * not need to touch `useAuth()` directly. To customize the post-logout\n * destination, override the `loginPath` prop on `<BPMNextProviders>` or\n * `<AuthProvider>`.\n */\nexport function useBPMLogout(): () => Promise<void> {\n const { logout } = useAuth();\n return logout;\n}\n",".root {\n display: inline-flex;\n position: relative;\n}\n\n.badge {\n align-items: center;\n background: #d92d20;\n border: 1px solid #fff;\n border-radius: 999px;\n color: #fff;\n display: inline-flex;\n font-size: 10px;\n font-weight: 600;\n height: 16px;\n justify-content: center;\n line-height: 1;\n min-width: 16px;\n padding: 0 4px;\n pointer-events: none;\n position: absolute;\n right: -4px;\n top: -4px;\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport { NavigationIconButton } from '@mezzanine-ui/react';\nimport { NotificationUnreadIcon } from '@mezzanine-ui/icons';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport styles from './bpm-notification-bell-button.module.scss';\n\nexport interface BPMNotificationBellButtonProps {\n /** Override the aria-label / tooltip. Defaults to `通知中心`. */\n readonly label?: string;\n}\n\n/**\n * Drop-in notification bell. Reads the unread count from\n * `<NotificationUnreadProvider>`, opens the BPM `<NotificationDrawer />`\n * mounted by `<Providers>` (or `<BPMNextProviders>`) on click, and\n * renders a small red badge with the unread count.\n *\n * Use this when the host navigation wants the BPM notification UX with\n * minimum wiring. Hosts that need a fully custom button can skip this\n * widget and consume `useNotificationDrawer()` + `useNotificationUnread()`\n * directly to wire their own trigger.\n *\n * Visual: uses Mezzanine `NavigationIconButton` so the bell aligns with\n * surrounding Mezzanine navigation chrome. The button is decoupled from\n * the `<Navigation>` container — it does not require a Mezzanine\n * navigation tree to render.\n */\nexport function BPMNotificationBellButton({\n label = '通知中心',\n}: BPMNotificationBellButtonProps = {}): ReactElement {\n const { open } = useNotificationDrawer();\n const { unreadCount } = useNotificationUnread();\n const ariaLabel = unreadCount > 0 ? `${label},${unreadCount} 則未讀` : label;\n return (\n <span className={styles.root}>\n <NavigationIconButton\n aria-label={ariaLabel}\n icon={NotificationUnreadIcon}\n onClick={(): void => {\n open();\n }}\n title={label}\n type=\"button\"\n />\n {unreadCount > 0 ? (\n <span className={styles.badge}>\n {unreadCount > 99 ? '99+' : unreadCount}\n </span>\n ) : null}\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAeA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["styles","defaultBrowserSearchParams","useRouterAdapter","RouterAdapterProvider","useAuth","AuthProvider","formatDateTime","NotificationDrawerProvider","useNotificationUnread","NotificationDrawer","useNotificationDrawer","NotificationUnreadProvider","Providers","createDefaultBPMRoutes","useBPMRoutes","BPMRoutesProvider","BPMFormField","useBPMMember","isBPMAdminMember","readOrgUnitOption","readMemberOption","OrgUnitPicker","readPositionOption","PositionPicker","MemberPicker","ApprovalInstanceListPage","DashboardPage","useBPMLogout","logout","bpm_notification_bell_button_module_default","BPMNotificationBellButton","ariaLabel","unreadCount","label","NavigationIconButton","NotificationUnreadIcon"],"sources":["../src/lib/use-bpm-logout.ts","../src/components/bpm-notification-bell-button.module.scss","../src/components/bpm-notification-bell-button.tsx"],"sourcesContent":["'use client';\n\nimport { useAuth } from './auth-provider';\n\n/**\n * Drop-in logout handler for host navigation menus. Wraps the BPM auth\n * context's logout flow — calls `logoutApi()` against the BPM REST\n * session endpoint, clears the in-memory member, then redirects to the\n * configured `loginPath`.\n *\n * Hosts mount this on their own logout buttons / menu items so they do\n * not need to touch `useAuth()` directly. To customize the post-logout\n * destination, override the `loginPath` prop on `<BPMNextProviders>` or\n * `<AuthProvider>`.\n */\nexport function useBPMLogout(): () => Promise<void> {\n const { logout } = useAuth();\n return logout;\n}\n",".root {\n display: inline-flex;\n position: relative;\n}\n\n.badge {\n align-items: center;\n background: #d92d20;\n border: 1px solid #fff;\n border-radius: 999px;\n color: #fff;\n display: inline-flex;\n font-size: 10px;\n font-weight: 600;\n height: 16px;\n justify-content: center;\n line-height: 1;\n min-width: 16px;\n padding: 0 4px;\n pointer-events: none;\n position: absolute;\n right: -4px;\n top: -4px;\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport { NavigationIconButton } from '@mezzanine-ui/react';\nimport { NotificationUnreadIcon } from '@mezzanine-ui/icons';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport styles from './bpm-notification-bell-button.module.scss';\n\nexport interface BPMNotificationBellButtonProps {\n /** Override the aria-label / tooltip. Defaults to `通知中心`. */\n readonly label?: string;\n}\n\n/**\n * Drop-in notification bell. Reads the unread count from\n * `<NotificationUnreadProvider>`, opens the BPM `<NotificationDrawer />`\n * mounted by `<Providers>` (or `<BPMNextProviders>`) on click, and\n * renders a small red badge with the unread count.\n *\n * Use this when the host navigation wants the BPM notification UX with\n * minimum wiring. Hosts that need a fully custom button can skip this\n * widget and consume `useNotificationDrawer()` + `useNotificationUnread()`\n * directly to wire their own trigger.\n *\n * Visual: uses Mezzanine `NavigationIconButton` so the bell aligns with\n * surrounding Mezzanine navigation chrome. The button is decoupled from\n * the `<Navigation>` container — it does not require a Mezzanine\n * navigation tree to render.\n */\nexport function BPMNotificationBellButton({\n label = '通知中心',\n}: BPMNotificationBellButtonProps = {}): ReactElement {\n const { open } = useNotificationDrawer();\n const { unreadCount } = useNotificationUnread();\n const ariaLabel = unreadCount > 0 ? `${label},${unreadCount} 則未讀` : label;\n return (\n <span className={styles.root}>\n <NavigationIconButton\n aria-label={ariaLabel}\n icon={NotificationUnreadIcon}\n onClick={(): void => {\n open();\n }}\n title={label}\n type=\"button\"\n />\n {unreadCount > 0 ? (\n <span className={styles.badge}>\n {unreadCount > 99 ? '99+' : unreadCount}\n </span>\n ) : null}\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAeA,SAAgB2B,IAAoC;CAClD,IAAM,EAAE,cAAWvB,EAAQ;CAC3B,OAAOwB;AACT;;;;;;;AEYA,SAAgBE,EAA0B,EACxC,WAAQ,WAC0B,CAAC,GAAiB;CACpD,IAAM,EAAE,YAASpB,EAAsB,GACjC,EAAE,mBAAgBF,EAAsB,GACxCuB,IAAYC,IAAc,IAAI,GAAGC,EAAM,GAAGD,EAAY,QAAQC;CACpE,OACE,kBAAC,QAAD;EAAM,WAAWjC,EAAO;EAAxB,UAAA,CACE,kBAACkC,GAAD;GACE,cAAYH;GACZ,MAAMI;GACN,eAAqB;IACnB,EAAK;GACP;GACA,OAAOF;GACP,MAAK;EACN,CAAA,GACAD,IAAc,IACb,kBAAC,QAAD;GAAM,WAAWhC,EAAO;GACrB,UAAAgC,IAAc,KAAK,QAAQA;EACxB,CAAA,IACJ,IACA;;AAEV"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How the e-mail is attached to the name: `ascii` is `name (email)`,
|
|
3
|
+
* `fullwidth` is `name(email)` and `dot` is `name · email`. Each call site
|
|
4
|
+
* keeps the separator it always had.
|
|
5
|
+
*/
|
|
6
|
+
export type MemberLabelStyle = 'ascii' | 'dot' | 'fullwidth';
|
|
7
|
+
/**
|
|
8
|
+
* `name` followed by the e-mail in the given style. A host may resolve a member
|
|
9
|
+
* with `email: ''` (or a blank string), and then the label is just the name —
|
|
10
|
+
* never `name ()` or `name · `.
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatMemberLabel(name: string, email: string | null | undefined, style?: MemberLabelStyle): string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { MemberProfileRecord } from '@rytass/bpm-core-client/template';
|
|
2
|
+
/** Most ids asked for on their own after a failed batch, per round. */
|
|
3
|
+
export declare const MAX_PER_ID_REQUESTS = 20;
|
|
4
|
+
/** Most per-id requests in flight at once. */
|
|
5
|
+
export declare const PER_ID_CONCURRENCY = 4;
|
|
6
|
+
export type MemberProfileResolution = Readonly<{
|
|
7
|
+
/** Ids whose lookup failed even when asked for on their own. */
|
|
8
|
+
failedIds: readonly string[];
|
|
9
|
+
profiles: readonly MemberProfileRecord[];
|
|
10
|
+
/**
|
|
11
|
+
* Ids left unasked because a failed batch held more than the per-id budget.
|
|
12
|
+
* They were not tried, so a later round may ask for them again.
|
|
13
|
+
*/
|
|
14
|
+
skippedIds: readonly string[];
|
|
15
|
+
}>;
|
|
16
|
+
export type MemberProfileResolutionLimits = Readonly<{
|
|
17
|
+
concurrency?: number;
|
|
18
|
+
maxPerIdRequests?: number;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Looks up member profiles in one batch, and degrades per id when the batch
|
|
22
|
+
* request fails.
|
|
23
|
+
*
|
|
24
|
+
* The host fails a whole lookup when it contains an id it cannot handle (a
|
|
25
|
+
* foreign id stored in a template, say). Treating that as "nobody resolved"
|
|
26
|
+
* labels every approver of the template 「未知會員」 for the sake of one bad
|
|
27
|
+
* id, so a failed batch is retried one id at a time and only the ids that fail
|
|
28
|
+
* on their own are reported back in `failedIds`.
|
|
29
|
+
*
|
|
30
|
+
* The retry is bounded: at most `maxPerIdRequests` ids are asked for on their
|
|
31
|
+
* own (the rest come back in `skippedIds` without a request), and at most
|
|
32
|
+
* `concurrency` of those requests run at once, so one bad id in a large
|
|
33
|
+
* template cannot turn into a burst of requests.
|
|
34
|
+
*
|
|
35
|
+
* A single-id batch is not retried: it already is the per-id request.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveMemberProfilesPerIdOnFailure(memberIds: readonly string[], resolveMembers: (memberIds: readonly string[]) => Promise<readonly MemberProfileRecord[]>, limits?: MemberProfileResolutionLimits): Promise<MemberProfileResolution>;
|
|
38
|
+
/**
|
|
39
|
+
* The ids still worth asking the directory for: those that already failed on
|
|
40
|
+
* their own in this session are left out, so they stay 「未知會員」 instead of
|
|
41
|
+
* being re-requested every time the option list changes.
|
|
42
|
+
*
|
|
43
|
+
* A failed id stays 「未知會員」 for the rest of the session, even when the
|
|
44
|
+
* failure was transient (a network blip, say), unless a later member search
|
|
45
|
+
* returns it and replaces the placeholder. Ids skipped over the per-id cap
|
|
46
|
+
* were never asked and must not be added to `failedMemberIds`.
|
|
47
|
+
*/
|
|
48
|
+
export declare function excludeFailedMemberIds(memberIds: readonly string[], failedMemberIds: ReadonlySet<string>): readonly string[];
|
|
@@ -22,7 +22,7 @@ export interface BPMRoutes {
|
|
|
22
22
|
dashboard(): string;
|
|
23
23
|
/** Inbox (待簽) — pending tasks assigned to the current member. */
|
|
24
24
|
inbox(): string;
|
|
25
|
-
/** Sent (我發起的) — instances the current member initiated. */
|
|
25
|
+
/** Sent (我發起的) — instances the current member initiated or submitted on someone's behalf. */
|
|
26
26
|
sent(): string;
|
|
27
27
|
/** CC (抄送給我) — instances the current member is copied on. */
|
|
28
28
|
cc(): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { TaskRecord } from '@rytass/bpm-core-client/workflow';
|
|
2
|
+
/**
|
|
3
|
+
* What a task list shows in its 「節點」 column: the node's display name
|
|
4
|
+
* (`nodeLabel`, resolved server-side from the instance's workflow snapshot),
|
|
5
|
+
* falling back to the raw `nodeId` when the server sent none — an unknown node
|
|
6
|
+
* or a host running its own queries. An ad-hoc task keeps the label of the
|
|
7
|
+
* node it was spawned on and gets the same 臨時會簽/臨時加簽 marker the
|
|
8
|
+
* instance timeline uses.
|
|
9
|
+
*/
|
|
10
|
+
export declare function readTaskNodeText(task: Pick<TaskRecord, 'adhocType' | 'isAdhoc' | 'nodeId' | 'nodeLabel'>): string;
|
package/dist/next/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/next/BPMNextProviders.tsx"],"sourcesContent":["'use client';\n\n// One-line Next.js App Router shim for BPM. Reads `next/navigation`\n// router/pathname hooks, builds a `RouterAdapter`, then composes\n// `<RouterAdapterProvider>` and the shared `<BPMProviders>`.\n//\n// IMPORTANT: This component intentionally does NOT call\n// `useSearchParams()`. Doing so triggers Next.js's CSR bailout at static\n// prerender, and any `<Suspense fallback={null}>` we wrap around it then\n// erases `<RouterAdapterProvider>` from the SSR/hydration tree — leaving\n// every BPM view to throw \"must be used inside <RouterAdapterProvider>\"\n// when it mounts. None of the shipped BPM views consume\n// `RouterAdapter.searchParams()` anyway; hosts that need reactive query\n// string state should call Next's `useSearchParams()` directly in their\n// page (with their own Suspense). The lazy default getter below is\n// good enough for read-on-demand callers.\n\nimport { useMemo, type ReactElement, type ReactNode } from 'react';\nimport { usePathname, useRouter } from 'next/navigation';\nimport type { CalendarLocale } from '@mezzanine-ui/react/moment';\nimport { Providers as BPMProviders } from '../lib/providers';\nimport {\n RouterAdapterProvider,\n defaultBrowserSearchParams,\n type RouterAdapter,\n} from '../lib/router-adapter';\n\nexport interface BPMNextProvidersProps {\n readonly children: ReactNode;\n /**\n * Override the Mezzanine calendar locale. Forwarded to the inner\n * `<Providers>`. Defaults to `CalendarLocale.ZH_TW`.\n */\n readonly locale?: CalendarLocale;\n /**\n * Routes that must remain accessible without a BPM session — visiting\n * these does not redirect to `loginPath` even when `member` is null.\n * Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n *\n * Hosts that mount BPM under a non-root prefix (see\n * `<BPMRoutesProvider>`) should typically expand this to include their\n * own auth-bypass paths.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to redirect unauthenticated users. Forwarded to\n * `<AuthProvider>`. Defaults to `'/login'`. Hosts owning their own\n * login route (the recommended case) override this to their host\n * route, e.g. `'/auth/sign-in'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * One-line Next.js App Router shim for the full BPM provider stack.\n * Mounts in the host's root layout (or layout for the BPM sub-tree):\n *\n * ```tsx\n * import { BPMNextProviders } from '@rytass/bpm-core-react/next';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * <BPMNextProviders loginPath=\"/auth/sign-in\">\n * {children}\n * </BPMNextProviders>\n * </body></html>\n * );\n * }\n * ```\n *\n * Forwards every prop on {@link BPMNextProvidersProps} to the inner\n * `<Providers>` and `<AuthProvider>`. For per-feature path remapping\n * (BPM internal navigation), additionally wrap with\n * `<BPMRoutesProvider>` exported from the same subpath.\n */\nexport function BPMNextProviders({\n children,\n locale,\n publicPaths,\n loginPath,\n}: BPMNextProvidersProps): ReactElement {\n const nextRouter = useRouter();\n const pathname = usePathname();\n\n const adapter = useMemo<RouterAdapter>(\n () => ({\n pathname,\n push: (href: string): void => nextRouter.push(href),\n replace: (href: string): void => nextRouter.replace(href),\n back: (): void => nextRouter.back(),\n searchParams: (): URLSearchParams => defaultBrowserSearchParams(),\n }),\n [nextRouter, pathname],\n );\n\n return (\n <RouterAdapterProvider value={adapter}>\n <BPMProviders\n locale={locale}\n publicPaths={publicPaths}\n loginPath={loginPath}\n >\n {children}\n </BPMProviders>\n </RouterAdapterProvider>\n );\n}\n"],"mappings":"4TA4EA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["useRouter","usePathname","useMemo","defaultBrowserSearchParams","RouterAdapterProvider","BPMProviders","require_router_adapter","require_providers","require_routes_config","react","react_jsx_runtime","next_navigation","BPMNextProviders","nextRouter","pathname","adapter","href"],"sources":["../../src/next/BPMNextProviders.tsx"],"sourcesContent":["'use client';\n\n// One-line Next.js App Router shim for BPM. Reads `next/navigation`\n// router/pathname hooks, builds a `RouterAdapter`, then composes\n// `<RouterAdapterProvider>` and the shared `<BPMProviders>`.\n//\n// IMPORTANT: This component intentionally does NOT call\n// `useSearchParams()`. Doing so triggers Next.js's CSR bailout at static\n// prerender, and any `<Suspense fallback={null}>` we wrap around it then\n// erases `<RouterAdapterProvider>` from the SSR/hydration tree — leaving\n// every BPM view to throw \"must be used inside <RouterAdapterProvider>\"\n// when it mounts. None of the shipped BPM views consume\n// `RouterAdapter.searchParams()` anyway; hosts that need reactive query\n// string state should call Next's `useSearchParams()` directly in their\n// page (with their own Suspense). The lazy default getter below is\n// good enough for read-on-demand callers.\n\nimport { useMemo, type ReactElement, type ReactNode } from 'react';\nimport { usePathname, useRouter } from 'next/navigation';\nimport type { CalendarLocale } from '@mezzanine-ui/react/moment';\nimport { Providers as BPMProviders } from '../lib/providers';\nimport {\n RouterAdapterProvider,\n defaultBrowserSearchParams,\n type RouterAdapter,\n} from '../lib/router-adapter';\n\nexport interface BPMNextProvidersProps {\n readonly children: ReactNode;\n /**\n * Override the Mezzanine calendar locale. Forwarded to the inner\n * `<Providers>`. Defaults to `CalendarLocale.ZH_TW`.\n */\n readonly locale?: CalendarLocale;\n /**\n * Routes that must remain accessible without a BPM session — visiting\n * these does not redirect to `loginPath` even when `member` is null.\n * Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n *\n * Hosts that mount BPM under a non-root prefix (see\n * `<BPMRoutesProvider>`) should typically expand this to include their\n * own auth-bypass paths.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to redirect unauthenticated users. Forwarded to\n * `<AuthProvider>`. Defaults to `'/login'`. Hosts owning their own\n * login route (the recommended case) override this to their host\n * route, e.g. `'/auth/sign-in'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * One-line Next.js App Router shim for the full BPM provider stack.\n * Mounts in the host's root layout (or layout for the BPM sub-tree):\n *\n * ```tsx\n * import { BPMNextProviders } from '@rytass/bpm-core-react/next';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * <BPMNextProviders loginPath=\"/auth/sign-in\">\n * {children}\n * </BPMNextProviders>\n * </body></html>\n * );\n * }\n * ```\n *\n * Forwards every prop on {@link BPMNextProvidersProps} to the inner\n * `<Providers>` and `<AuthProvider>`. For per-feature path remapping\n * (BPM internal navigation), additionally wrap with\n * `<BPMRoutesProvider>` exported from the same subpath.\n */\nexport function BPMNextProviders({\n children,\n locale,\n publicPaths,\n loginPath,\n}: BPMNextProvidersProps): ReactElement {\n const nextRouter = useRouter();\n const pathname = usePathname();\n\n const adapter = useMemo<RouterAdapter>(\n () => ({\n pathname,\n push: (href: string): void => nextRouter.push(href),\n replace: (href: string): void => nextRouter.replace(href),\n back: (): void => nextRouter.back(),\n searchParams: (): URLSearchParams => defaultBrowserSearchParams(),\n }),\n [nextRouter, pathname],\n );\n\n return (\n <RouterAdapterProvider value={adapter}>\n <BPMProviders\n locale={locale}\n publicPaths={publicPaths}\n loginPath={loginPath}\n >\n {children}\n </BPMProviders>\n </RouterAdapterProvider>\n );\n}\n"],"mappings":"4TA4EA,SAAgBY,EAAiB,CAC/B,WACA,SACA,cACA,aACsC,CACtC,IAAMC,GAAA,EAAab,EAAAA,UAAAA,CAAU,EACvBc,GAAA,EAAWb,EAAAA,YAAAA,CAAY,EAEvBc,GAAA,EAAUb,EAAAA,QAAAA,MACP,CACL,WACA,KAAO,GAAuBW,EAAW,KAAKG,CAAI,EAClD,QAAU,GAAuBH,EAAW,QAAQG,CAAI,EACxD,SAAkBH,EAAW,KAAK,EAClC,iBAAqCV,EAAAA,EAA2B,CAClE,GACA,CAACU,EAAYC,CAAQ,CACvB,EAEA,OACE,EAAAJ,EAAA,IAAA,CAACN,EAAAA,EAAD,CAAuB,MAAOW,EAC5B,UAAA,EAAAL,EAAA,IAAA,CAACL,EAAAA,EAAD,CACU,SACK,cACF,YAEV,UACW,CAAA,CACO,CAAA,CAE3B"}
|
package/dist/next/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/next/BPMNextProviders.tsx"],"sourcesContent":["'use client';\n\n// One-line Next.js App Router shim for BPM. Reads `next/navigation`\n// router/pathname hooks, builds a `RouterAdapter`, then composes\n// `<RouterAdapterProvider>` and the shared `<BPMProviders>`.\n//\n// IMPORTANT: This component intentionally does NOT call\n// `useSearchParams()`. Doing so triggers Next.js's CSR bailout at static\n// prerender, and any `<Suspense fallback={null}>` we wrap around it then\n// erases `<RouterAdapterProvider>` from the SSR/hydration tree — leaving\n// every BPM view to throw \"must be used inside <RouterAdapterProvider>\"\n// when it mounts. None of the shipped BPM views consume\n// `RouterAdapter.searchParams()` anyway; hosts that need reactive query\n// string state should call Next's `useSearchParams()` directly in their\n// page (with their own Suspense). The lazy default getter below is\n// good enough for read-on-demand callers.\n\nimport { useMemo, type ReactElement, type ReactNode } from 'react';\nimport { usePathname, useRouter } from 'next/navigation';\nimport type { CalendarLocale } from '@mezzanine-ui/react/moment';\nimport { Providers as BPMProviders } from '../lib/providers';\nimport {\n RouterAdapterProvider,\n defaultBrowserSearchParams,\n type RouterAdapter,\n} from '../lib/router-adapter';\n\nexport interface BPMNextProvidersProps {\n readonly children: ReactNode;\n /**\n * Override the Mezzanine calendar locale. Forwarded to the inner\n * `<Providers>`. Defaults to `CalendarLocale.ZH_TW`.\n */\n readonly locale?: CalendarLocale;\n /**\n * Routes that must remain accessible without a BPM session — visiting\n * these does not redirect to `loginPath` even when `member` is null.\n * Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n *\n * Hosts that mount BPM under a non-root prefix (see\n * `<BPMRoutesProvider>`) should typically expand this to include their\n * own auth-bypass paths.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to redirect unauthenticated users. Forwarded to\n * `<AuthProvider>`. Defaults to `'/login'`. Hosts owning their own\n * login route (the recommended case) override this to their host\n * route, e.g. `'/auth/sign-in'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * One-line Next.js App Router shim for the full BPM provider stack.\n * Mounts in the host's root layout (or layout for the BPM sub-tree):\n *\n * ```tsx\n * import { BPMNextProviders } from '@rytass/bpm-core-react/next';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * <BPMNextProviders loginPath=\"/auth/sign-in\">\n * {children}\n * </BPMNextProviders>\n * </body></html>\n * );\n * }\n * ```\n *\n * Forwards every prop on {@link BPMNextProvidersProps} to the inner\n * `<Providers>` and `<AuthProvider>`. For per-feature path remapping\n * (BPM internal navigation), additionally wrap with\n * `<BPMRoutesProvider>` exported from the same subpath.\n */\nexport function BPMNextProviders({\n children,\n locale,\n publicPaths,\n loginPath,\n}: BPMNextProvidersProps): ReactElement {\n const nextRouter = useRouter();\n const pathname = usePathname();\n\n const adapter = useMemo<RouterAdapter>(\n () => ({\n pathname,\n push: (href: string): void => nextRouter.push(href),\n replace: (href: string): void => nextRouter.replace(href),\n back: (): void => nextRouter.back(),\n searchParams: (): URLSearchParams => defaultBrowserSearchParams(),\n }),\n [nextRouter, pathname],\n );\n\n return (\n <RouterAdapterProvider value={adapter}>\n <BPMProviders\n locale={locale}\n publicPaths={publicPaths}\n loginPath={loginPath}\n >\n {children}\n </BPMProviders>\n </RouterAdapterProvider>\n );\n}\n"],"mappings":";;;;;;;;AA4EA,
|
|
1
|
+
{"version":3,"file":"index.js","names":["BPMProviders","defaultBrowserSearchParams","Providers","createDefaultBPMRoutes","useBPMRoutes","BPMRoutesProvider","BPMNextProviders","nextRouter","useRouter","pathname","usePathname","adapter","useMemo","href","RouterAdapterProvider"],"sources":["../../src/next/BPMNextProviders.tsx"],"sourcesContent":["'use client';\n\n// One-line Next.js App Router shim for BPM. Reads `next/navigation`\n// router/pathname hooks, builds a `RouterAdapter`, then composes\n// `<RouterAdapterProvider>` and the shared `<BPMProviders>`.\n//\n// IMPORTANT: This component intentionally does NOT call\n// `useSearchParams()`. Doing so triggers Next.js's CSR bailout at static\n// prerender, and any `<Suspense fallback={null}>` we wrap around it then\n// erases `<RouterAdapterProvider>` from the SSR/hydration tree — leaving\n// every BPM view to throw \"must be used inside <RouterAdapterProvider>\"\n// when it mounts. None of the shipped BPM views consume\n// `RouterAdapter.searchParams()` anyway; hosts that need reactive query\n// string state should call Next's `useSearchParams()` directly in their\n// page (with their own Suspense). The lazy default getter below is\n// good enough for read-on-demand callers.\n\nimport { useMemo, type ReactElement, type ReactNode } from 'react';\nimport { usePathname, useRouter } from 'next/navigation';\nimport type { CalendarLocale } from '@mezzanine-ui/react/moment';\nimport { Providers as BPMProviders } from '../lib/providers';\nimport {\n RouterAdapterProvider,\n defaultBrowserSearchParams,\n type RouterAdapter,\n} from '../lib/router-adapter';\n\nexport interface BPMNextProvidersProps {\n readonly children: ReactNode;\n /**\n * Override the Mezzanine calendar locale. Forwarded to the inner\n * `<Providers>`. Defaults to `CalendarLocale.ZH_TW`.\n */\n readonly locale?: CalendarLocale;\n /**\n * Routes that must remain accessible without a BPM session — visiting\n * these does not redirect to `loginPath` even when `member` is null.\n * Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n *\n * Hosts that mount BPM under a non-root prefix (see\n * `<BPMRoutesProvider>`) should typically expand this to include their\n * own auth-bypass paths.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to redirect unauthenticated users. Forwarded to\n * `<AuthProvider>`. Defaults to `'/login'`. Hosts owning their own\n * login route (the recommended case) override this to their host\n * route, e.g. `'/auth/sign-in'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * One-line Next.js App Router shim for the full BPM provider stack.\n * Mounts in the host's root layout (or layout for the BPM sub-tree):\n *\n * ```tsx\n * import { BPMNextProviders } from '@rytass/bpm-core-react/next';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * <BPMNextProviders loginPath=\"/auth/sign-in\">\n * {children}\n * </BPMNextProviders>\n * </body></html>\n * );\n * }\n * ```\n *\n * Forwards every prop on {@link BPMNextProvidersProps} to the inner\n * `<Providers>` and `<AuthProvider>`. For per-feature path remapping\n * (BPM internal navigation), additionally wrap with\n * `<BPMRoutesProvider>` exported from the same subpath.\n */\nexport function BPMNextProviders({\n children,\n locale,\n publicPaths,\n loginPath,\n}: BPMNextProvidersProps): ReactElement {\n const nextRouter = useRouter();\n const pathname = usePathname();\n\n const adapter = useMemo<RouterAdapter>(\n () => ({\n pathname,\n push: (href: string): void => nextRouter.push(href),\n replace: (href: string): void => nextRouter.replace(href),\n back: (): void => nextRouter.back(),\n searchParams: (): URLSearchParams => defaultBrowserSearchParams(),\n }),\n [nextRouter, pathname],\n );\n\n return (\n <RouterAdapterProvider value={adapter}>\n <BPMProviders\n locale={locale}\n publicPaths={publicPaths}\n loginPath={loginPath}\n >\n {children}\n </BPMProviders>\n </RouterAdapterProvider>\n );\n}\n"],"mappings":";;;;;;;;AA4EA,SAAgBM,EAAiB,EAC/B,aACA,WACA,gBACA,gBACsC;CACtC,IAAMC,IAAaC,EAAU,GACvBC,IAAWC,EAAY,GAEvBC,IAAUC,SACP;EACL;EACA,OAAO,MAAuBL,EAAW,KAAKM,CAAI;EAClD,UAAU,MAAuBN,EAAW,QAAQM,CAAI;EACxD,YAAkBN,EAAW,KAAK;EAClC,oBAAqCN,EAA2B;CAClE,IACA,CAACM,GAAYE,CAAQ,CACvB;CAEA,OACE,kBAACK,GAAD;EAAuB,OAAOH;EAC5B,UAAA,kBAACX,GAAD;GACU;GACK;GACF;GAEV;EACW,CAAA;CACO,CAAA;AAE3B"}
|