@rytass/bpm-core-react 0.3.8 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +69 -4
  3. package/dist/chunks/approval-instance-list-page-BtEc8Cs3.js +278 -0
  4. package/dist/chunks/approval-instance-list-page-BtEc8Cs3.js.map +1 -0
  5. package/dist/chunks/approval-instance-list-page-UNIIgUZy.cjs +2 -0
  6. package/dist/chunks/approval-instance-list-page-UNIIgUZy.cjs.map +1 -0
  7. package/dist/chunks/auth-provider-D2P-qWmY.cjs +2 -0
  8. package/dist/chunks/auth-provider-D2P-qWmY.cjs.map +1 -0
  9. package/dist/chunks/auth-provider-TTO9eNZV.js +83 -0
  10. package/dist/chunks/auth-provider-TTO9eNZV.js.map +1 -0
  11. package/dist/chunks/{builder-D950gct_.js → builder-C3E-8OJu.js} +474 -478
  12. package/dist/chunks/builder-C3E-8OJu.js.map +1 -0
  13. package/dist/chunks/builder-f-Q_0NUs.cjs +3 -0
  14. package/dist/chunks/builder-f-Q_0NUs.cjs.map +1 -0
  15. package/dist/chunks/categories-B6QZKZRt.cjs +2 -0
  16. package/dist/chunks/categories-B6QZKZRt.cjs.map +1 -0
  17. package/dist/chunks/categories-DBPoSrsi.js +382 -0
  18. package/dist/chunks/categories-DBPoSrsi.js.map +1 -0
  19. package/dist/chunks/chunk-CMqjfN_6.cjs +1 -0
  20. package/dist/chunks/dashboard-page-CQRBJxze.js +119 -0
  21. package/dist/chunks/dashboard-page-CQRBJxze.js.map +1 -0
  22. package/dist/chunks/dashboard-page-DrDChhg1.cjs +2 -0
  23. package/dist/chunks/dashboard-page-DrDChhg1.cjs.map +1 -0
  24. package/dist/chunks/delegations-CFXaJrdX.cjs +2 -0
  25. package/dist/chunks/delegations-CFXaJrdX.cjs.map +1 -0
  26. package/dist/chunks/delegations-D5pPEWsP.js +641 -0
  27. package/dist/chunks/delegations-D5pPEWsP.js.map +1 -0
  28. package/dist/chunks/delegations-DwbYkNUg.cjs +2 -0
  29. package/dist/chunks/delegations-DwbYkNUg.cjs.map +1 -0
  30. package/dist/chunks/delegations-FTLaWo1Y.js +568 -0
  31. package/dist/chunks/delegations-FTLaWo1Y.js.map +1 -0
  32. package/dist/chunks/detail-B9JkYNHc.cjs +2 -0
  33. package/dist/chunks/detail-B9JkYNHc.cjs.map +1 -0
  34. package/dist/chunks/detail-CSxI04gB.js +1518 -0
  35. package/dist/chunks/detail-CSxI04gB.js.map +1 -0
  36. package/dist/chunks/format-date-time-XxBzF0F5.cjs +2 -0
  37. package/dist/chunks/{format-date-time-26_pFvv4.cjs.map → format-date-time-XxBzF0F5.cjs.map} +1 -1
  38. package/dist/chunks/login-BfmfCclF.cjs +2 -0
  39. package/dist/chunks/{login-CQ9MfwcC.cjs.map → login-BfmfCclF.cjs.map} +1 -1
  40. package/dist/chunks/{login-C20yVxbc.js → login-xgI4wLHe.js} +3 -2
  41. package/dist/chunks/{login-C20yVxbc.js.map → login-xgI4wLHe.js.map} +1 -1
  42. package/dist/chunks/notifications-BoNa1BXD.js +193 -0
  43. package/dist/chunks/notifications-BoNa1BXD.js.map +1 -0
  44. package/dist/chunks/notifications-a-FCxV02.cjs +2 -0
  45. package/dist/chunks/notifications-a-FCxV02.cjs.map +1 -0
  46. package/dist/chunks/orgs-BIiqzHvb.cjs +2 -0
  47. package/dist/chunks/orgs-BIiqzHvb.cjs.map +1 -0
  48. package/dist/chunks/orgs-Cc18umVt.js +1944 -0
  49. package/dist/chunks/orgs-Cc18umVt.js.map +1 -0
  50. package/dist/chunks/router-adapter-BdHZXLS3.js +23 -0
  51. package/dist/chunks/router-adapter-BdHZXLS3.js.map +1 -0
  52. package/dist/chunks/router-adapter-BybHrCNP.cjs +2 -0
  53. package/dist/chunks/router-adapter-BybHrCNP.cjs.map +1 -0
  54. package/dist/chunks/templates-CL8bPvgn.cjs +2 -0
  55. package/dist/chunks/templates-CL8bPvgn.cjs.map +1 -0
  56. package/dist/chunks/templates-DNfDOPGm.js +380 -0
  57. package/dist/chunks/templates-DNfDOPGm.js.map +1 -0
  58. package/dist/chunks/users-CUY139DF.js +214 -0
  59. package/dist/chunks/users-CUY139DF.js.map +1 -0
  60. package/dist/chunks/users-qghSMtLn.cjs +2 -0
  61. package/dist/chunks/users-qghSMtLn.cjs.map +1 -0
  62. package/dist/components/approval-instance-list-page.d.ts +1 -2
  63. package/dist/components/bpm-notification-bell-button.d.ts +22 -0
  64. package/dist/components/dashboard-page.d.ts +1 -4
  65. package/dist/index.cjs +1 -1
  66. package/dist/index.cjs.map +1 -1
  67. package/dist/index.css +1 -0
  68. package/dist/index.d.ts +3 -1
  69. package/dist/index.js +206 -97
  70. package/dist/index.js.map +1 -1
  71. package/dist/lib/notification-drawer-provider.d.ts +3 -2
  72. package/dist/lib/notification-unread-provider.d.ts +6 -5
  73. package/dist/lib/providers.d.ts +3 -2
  74. package/dist/lib/use-bpm-logout.d.ts +12 -0
  75. package/dist/lib/use-bpm-member.d.ts +11 -0
  76. package/dist/pages/admin/delegations/index.cjs +1 -1
  77. package/dist/pages/admin/delegations/index.js +1 -1
  78. package/dist/pages/admin/orgs/index.cjs +1 -1
  79. package/dist/pages/admin/orgs/index.js +1 -1
  80. package/dist/pages/admin/users/index.cjs +1 -1
  81. package/dist/pages/admin/users/index.js +1 -1
  82. package/dist/pages/delegations/index.cjs +1 -1
  83. package/dist/pages/delegations/index.js +1 -1
  84. package/dist/pages/forms/builder/index.cjs +1 -1
  85. package/dist/pages/forms/builder/index.js +1 -1
  86. package/dist/pages/instances/detail/index.cjs +1 -1
  87. package/dist/pages/instances/detail/index.js +1 -1
  88. package/dist/pages/login/index.cjs +1 -1
  89. package/dist/pages/login/index.js +1 -1
  90. package/dist/pages/settings/notifications/index.cjs +1 -1
  91. package/dist/pages/settings/notifications/index.js +1 -1
  92. package/dist/pages/templates/categories/index.cjs +1 -1
  93. package/dist/pages/templates/categories/index.js +1 -1
  94. package/dist/pages/templates/index.cjs +1 -1
  95. package/dist/pages/templates/index.js +1 -1
  96. package/dist/views/admin/delegations/AdminDelegationsView.d.ts +1 -4
  97. package/dist/views/admin/delegations/index.cjs +1 -1
  98. package/dist/views/admin/delegations/index.js +1 -1
  99. package/dist/views/admin/index.cjs +1 -1
  100. package/dist/views/admin/index.js +3 -3
  101. package/dist/views/admin/orgs/AdminOrgsView.d.ts +1 -4
  102. package/dist/views/admin/orgs/index.cjs +1 -1
  103. package/dist/views/admin/orgs/index.js +1 -1
  104. package/dist/views/admin/users/AdminUsersView.d.ts +1 -4
  105. package/dist/views/admin/users/index.cjs +1 -1
  106. package/dist/views/admin/users/index.js +1 -1
  107. package/dist/views/cc/CcView.d.ts +1 -3
  108. package/dist/views/cc/index.cjs +1 -1
  109. package/dist/views/cc/index.cjs.map +1 -1
  110. package/dist/views/cc/index.js +2 -3
  111. package/dist/views/cc/index.js.map +1 -1
  112. package/dist/views/dashboard/DashboardView.d.ts +1 -3
  113. package/dist/views/dashboard/index.cjs +1 -1
  114. package/dist/views/dashboard/index.cjs.map +1 -1
  115. package/dist/views/dashboard/index.js +3 -3
  116. package/dist/views/dashboard/index.js.map +1 -1
  117. package/dist/views/delegations/DelegationsView.d.ts +1 -4
  118. package/dist/views/delegations/index.cjs +1 -1
  119. package/dist/views/delegations/index.js +1 -1
  120. package/dist/views/forms/FormsView.d.ts +1 -3
  121. package/dist/views/forms/builder/index.cjs +1 -1
  122. package/dist/views/forms/builder/index.js +1 -1
  123. package/dist/views/forms/index.cjs +1 -1
  124. package/dist/views/forms/index.cjs.map +1 -1
  125. package/dist/views/forms/index.js +95 -99
  126. package/dist/views/forms/index.js.map +1 -1
  127. package/dist/views/inbox/InboxView.d.ts +1 -3
  128. package/dist/views/inbox/index.cjs +1 -1
  129. package/dist/views/inbox/index.cjs.map +1 -1
  130. package/dist/views/inbox/index.js +91 -94
  131. package/dist/views/inbox/index.js.map +1 -1
  132. package/dist/views/instances/detail/index.cjs +1 -1
  133. package/dist/views/instances/detail/index.js +1 -1
  134. package/dist/views/instances/new/index.cjs +1 -1
  135. package/dist/views/instances/new/index.cjs.map +1 -1
  136. package/dist/views/instances/new/index.js +71 -77
  137. package/dist/views/instances/new/index.js.map +1 -1
  138. package/dist/views/login/index.cjs +1 -1
  139. package/dist/views/login/index.js +1 -1
  140. package/dist/views/root/RootView.d.ts +1 -3
  141. package/dist/views/search/SearchView.d.ts +1 -3
  142. package/dist/views/search/index.cjs +1 -1
  143. package/dist/views/search/index.cjs.map +1 -1
  144. package/dist/views/search/index.js +2 -3
  145. package/dist/views/search/index.js.map +1 -1
  146. package/dist/views/sent/SentView.d.ts +1 -3
  147. package/dist/views/sent/index.cjs +1 -1
  148. package/dist/views/sent/index.cjs.map +1 -1
  149. package/dist/views/sent/index.js +2 -3
  150. package/dist/views/sent/index.js.map +1 -1
  151. package/dist/views/settings/index.cjs +1 -1
  152. package/dist/views/settings/index.js +1 -1
  153. package/dist/views/settings/notifications/SettingsNotificationsView.d.ts +1 -4
  154. package/dist/views/settings/notifications/index.cjs +1 -1
  155. package/dist/views/settings/notifications/index.js +1 -1
  156. package/dist/views/templates/TemplatesView.d.ts +1 -4
  157. package/dist/views/templates/categories/TemplateCategoriesView.d.ts +1 -4
  158. package/dist/views/templates/categories/index.cjs +1 -1
  159. package/dist/views/templates/categories/index.js +1 -1
  160. package/dist/views/templates/designer/TemplateDesignerView.d.ts +1 -2
  161. package/dist/views/templates/designer/index.cjs +7 -7
  162. package/dist/views/templates/designer/index.cjs.map +1 -1
  163. package/dist/views/templates/designer/index.js +707 -711
  164. package/dist/views/templates/designer/index.js.map +1 -1
  165. package/dist/views/templates/index.cjs +1 -1
  166. package/dist/views/templates/index.js +2 -2
  167. package/dist/views/templates/versions/TemplateVersionsView.d.ts +1 -2
  168. package/dist/views/templates/versions/index.cjs +1 -1
  169. package/dist/views/templates/versions/index.cjs.map +1 -1
  170. package/dist/views/templates/versions/index.js +45 -49
  171. package/dist/views/templates/versions/index.js.map +1 -1
  172. package/package.json +2 -2
  173. package/dist/app-navigation.css +0 -1
  174. package/dist/chunks/app-navigation-BSkMsEhy.js +0 -268
  175. package/dist/chunks/app-navigation-BSkMsEhy.js.map +0 -1
  176. package/dist/chunks/app-navigation-KnlJCUp1.cjs +0 -2
  177. package/dist/chunks/app-navigation-KnlJCUp1.cjs.map +0 -1
  178. package/dist/chunks/approval-instance-list-page-CVXgE2K3.cjs +0 -2
  179. package/dist/chunks/approval-instance-list-page-CVXgE2K3.cjs.map +0 -1
  180. package/dist/chunks/approval-instance-list-page-CqNdoZqx.js +0 -282
  181. package/dist/chunks/approval-instance-list-page-CqNdoZqx.js.map +0 -1
  182. package/dist/chunks/auth-provider-BV8Iiwfb.cjs +0 -2
  183. package/dist/chunks/auth-provider-BV8Iiwfb.cjs.map +0 -1
  184. package/dist/chunks/auth-provider-Bnox5gsx.js +0 -98
  185. package/dist/chunks/auth-provider-Bnox5gsx.js.map +0 -1
  186. package/dist/chunks/builder-CMlJfQHE.cjs +0 -3
  187. package/dist/chunks/builder-CMlJfQHE.cjs.map +0 -1
  188. package/dist/chunks/builder-D950gct_.js.map +0 -1
  189. package/dist/chunks/categories-5yEM3p3N.cjs +0 -2
  190. package/dist/chunks/categories-5yEM3p3N.cjs.map +0 -1
  191. package/dist/chunks/categories-BIpOG451.js +0 -387
  192. package/dist/chunks/categories-BIpOG451.js.map +0 -1
  193. package/dist/chunks/dashboard-page-Bx1-Ys3e.js +0 -122
  194. package/dist/chunks/dashboard-page-Bx1-Ys3e.js.map +0 -1
  195. package/dist/chunks/dashboard-page-CQNRbMkJ.cjs +0 -2
  196. package/dist/chunks/dashboard-page-CQNRbMkJ.cjs.map +0 -1
  197. package/dist/chunks/delegations-B2j-wNEO.js +0 -646
  198. package/dist/chunks/delegations-B2j-wNEO.js.map +0 -1
  199. package/dist/chunks/delegations-CsB9ozLu.cjs +0 -2
  200. package/dist/chunks/delegations-CsB9ozLu.cjs.map +0 -1
  201. package/dist/chunks/delegations-CvtwTXNP.cjs +0 -2
  202. package/dist/chunks/delegations-CvtwTXNP.cjs.map +0 -1
  203. package/dist/chunks/delegations-dKodb0WW.js +0 -573
  204. package/dist/chunks/delegations-dKodb0WW.js.map +0 -1
  205. package/dist/chunks/detail-BcGAqJ_R.js +0 -1523
  206. package/dist/chunks/detail-BcGAqJ_R.js.map +0 -1
  207. package/dist/chunks/detail-CqjqLd65.cjs +0 -2
  208. package/dist/chunks/detail-CqjqLd65.cjs.map +0 -1
  209. package/dist/chunks/format-date-time-26_pFvv4.cjs +0 -2
  210. package/dist/chunks/login-CQ9MfwcC.cjs +0 -2
  211. package/dist/chunks/notifications-2swRqDPF.js +0 -198
  212. package/dist/chunks/notifications-2swRqDPF.js.map +0 -1
  213. package/dist/chunks/notifications-BaYDebFt.cjs +0 -2
  214. package/dist/chunks/notifications-BaYDebFt.cjs.map +0 -1
  215. package/dist/chunks/orgs-CuHxxd_n.js +0 -1949
  216. package/dist/chunks/orgs-CuHxxd_n.js.map +0 -1
  217. package/dist/chunks/orgs-YMiVLNvL.cjs +0 -2
  218. package/dist/chunks/orgs-YMiVLNvL.cjs.map +0 -1
  219. package/dist/chunks/templates-DTkbSgFY.cjs +0 -2
  220. package/dist/chunks/templates-DTkbSgFY.cjs.map +0 -1
  221. package/dist/chunks/templates-DoDWM68t.js +0 -384
  222. package/dist/chunks/templates-DoDWM68t.js.map +0 -1
  223. package/dist/chunks/users-3ySyUW4u.cjs +0 -2
  224. package/dist/chunks/users-3ySyUW4u.cjs.map +0 -1
  225. package/dist/chunks/users-sMfrSjRQ.js +0 -219
  226. package/dist/chunks/users-sMfrSjRQ.js.map +0 -1
  227. package/dist/components/app-navigation.d.ts +0 -41
package/CHANGELOG.md CHANGED
@@ -8,6 +8,111 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  Releases are managed by [`nx release`](https://nx.dev/recipes/nx-release) with
9
9
  Conventional Commits — see `nx.json` for the release config.
10
10
 
11
+ ## 0.4.0 — 2026-05-28
12
+
13
+ ### Breaking
14
+
15
+ - **`<AppLayout>` removed from the public surface.** The 4-group
16
+ Mezzanine `<Layout>` + `<Navigation>` shell BPM used to ship is no
17
+ longer part of `@rytass/bpm-core-react`. Hosts are expected to own
18
+ their navigation chrome and mount BPM views inside their existing
19
+ sidebar / top bar. Together with `AppLayout`, the following exports
20
+ are gone: `AppLayout`, `AppLayoutProps`, `AppNavigationGroup`.
21
+ - **Views no longer self-wrap.** Every view (`InboxView`, `FormsView`,
22
+ `TemplatesView`, `AdminOrgsView`, … 13 in total) used to render
23
+ `<AppLayout activeHref=…>…</AppLayout>` internally. They now return
24
+ the page content fragment (a `<PageHeader>` + `<SectionGroup>`
25
+ composition) so a host layout can wrap them. The `activeHref?` prop
26
+ on `TemplatesView` / `DelegationsView` / `TemplateDesignerView` /
27
+ `TemplateVersionsView` / `TemplateCategoriesView` / `AdminOrgsView` /
28
+ `AdminUsersView` / `AdminDelegationsView` /
29
+ `SettingsNotificationsView` is removed because it had no consumer
30
+ after the layout was lifted out.
31
+ - **`DashboardPage` props simplified to `{}`** — `activeHref` removed
32
+ for the same reason. The component still renders the five-metric
33
+ workflow dashboard fragment unchanged.
34
+ - **`ApprovalInstanceListPage` no longer accepts `activeHref`.** The
35
+ thin delegators (`SentView`, `CcView`, `SearchView`) drop the
36
+ hard-coded `activeHref` argument accordingly.
37
+
38
+ ### Added
39
+
40
+ - **`<BPMNotificationBellButton />`** — drop-in notification bell for
41
+ host navigations. Opens the BPM `<NotificationDrawer />` (mounted by
42
+ `<Providers>`) on click and renders an unread-count badge from
43
+ `<NotificationUnreadProvider>`. Visual chrome uses Mezzanine
44
+ `NavigationIconButton`, but the button is decoupled from the
45
+ Mezzanine `<Navigation>` container — drop it anywhere in your nav.
46
+ Hosts that want a fully custom trigger can skip this widget and
47
+ consume `useNotificationDrawer().open` + `useNotificationUnread().unreadCount`
48
+ directly.
49
+ - **`useBPMMember()` hook** — host-facing alias of `useAuth().member`.
50
+ Returns the currently authenticated `ApiMember | null` without
51
+ exposing the broader `useAuth()` surface.
52
+ - **`useBPMLogout()` hook** — host-facing alias of `useAuth().logout`.
53
+ Returns `() => Promise<void>` that calls `logoutApi()` and redirects
54
+ to the configured `loginPath`. Mount on host logout buttons / menu
55
+ items so the host nav does not need to import `useAuth()` directly.
56
+
57
+ ### Removed
58
+
59
+ - `AppLayout`, `AppLayoutProps`, `AppNavigationGroup` (see Breaking).
60
+ - `components/app-navigation.tsx` and `components/app-navigation.module.scss`
61
+ are deleted from the lib source tree.
62
+
63
+ ### Why a minor (0.x semver)
64
+
65
+ The library is still in `0.x` so breaking changes ride a minor bump per
66
+ the project's release convention. The change targets a single consumer
67
+ in the monorepo (`apps/client`) plus any external consumer wiring
68
+ `@rytass/bpm-core-react` into their own host — both are expected to
69
+ follow the migration recipe below.
70
+
71
+ ### Migration
72
+
73
+ If your host re-exported page shims (`pages/<feature>`), you only need
74
+ to add a layout wrapper:
75
+
76
+ ```diff
77
+ // app/layout.tsx
78
+ import { BPMNextProviders } from '@rytass/bpm-core-react/next';
79
+ + import { MyHostLayout } from './_components/host-layout';
80
+
81
+ export default function RootLayout({ children }) {
82
+ return (
83
+ <html lang="zh-TW"><body>
84
+ - <BPMNextProviders>{children}</BPMNextProviders>
85
+ + <BPMNextProviders>
86
+ + <MyHostLayout>{children}</MyHostLayout>
87
+ + </BPMNextProviders>
88
+ </body></html>
89
+ );
90
+ }
91
+ ```
92
+
93
+ For a reference `MyHostLayout` that reproduces the legacy 4-group BPM
94
+ nav using `useBPMRoutes` + `useBPMMember` + `useBPMLogout` +
95
+ `<BPMNotificationBellButton />`, copy
96
+ `apps/client/src/app/_components/host-layout.tsx` from the BPMCore
97
+ repo. Adjust groups, branding, and which routes you expose to match
98
+ your host.
99
+
100
+ If your host imported `AppLayout` directly, swap to the same host
101
+ layout pattern — `AppLayout` is no longer exported.
102
+
103
+ ### Documentation
104
+
105
+ - README's `Usage` section rewritten around the new integration shape
106
+ (host owns the layout; BPM provides widgets).
107
+ - New `docs/integration-guide.md` with a step-by-step host integration
108
+ walkthrough, the recommended 4-group nav structure (`我的工作` /
109
+ `查詢與代理` / `簽核設計` / `系統管理`), and notes on consuming the
110
+ widgets / hooks.
111
+ - `docs/api-reference.md` updated to reflect the new root-barrel
112
+ surface (`useBPMMember`, `useBPMLogout`, `BPMNotificationBellButton`)
113
+ and the removal of `AppLayout` / `AppLayoutProps` /
114
+ `AppNavigationGroup`.
115
+
11
116
  ## 0.3.8 — 2026-05-28
12
117
 
13
118
  ### Fixed
package/README.md CHANGED
@@ -6,7 +6,7 @@ This package composes [`@mezzanine-ui/react`](https://www.npmjs.com/package/@mez
6
6
 
7
7
  ## Status
8
8
 
9
- `0.3.7` — adds `BPMRoutesProvider` for host-controlled path remapping (0.3.2), forwards `loginPath` / `publicPaths` / `locale` on `BPMNextProviders` (0.3.3), 19 view subpaths + 19 Next.js page shims (`pages/<feature>`), `next` subpath barrel, and foundation root barrel. See `CHANGELOG.md` for the per-release history.
9
+ `0.4.0` (breaking) drops the bundled navigation shell. BPM views no longer wrap themselves in an `<AppLayout>` / Mezzanine `<Navigation>`; the host owns the layout chrome and mounts BPM views inside its own sidebar / top bar. New host-facing widgets ship in the root barrel: `useBPMMember`, `useBPMLogout`, `<BPMNotificationBellButton />`. See `CHANGELOG.md` for the migration walkthrough, and `docs/integration-guide.md` for a host integration recipe.
10
10
 
11
11
  ## Install
12
12
 
@@ -39,13 +39,78 @@ module.exports = {
39
39
 
40
40
  ## Usage
41
41
 
42
- ### Drop-in Next.js page
42
+ ### Host integration shape
43
+
44
+ BPM ships **page bodies, providers, and widgets** — never a full layout.
45
+ The host wires its own `<Layout>` / `<Navigation>` (or its own design
46
+ system equivalent), then drops BPM widgets and views into the slots:
47
+
48
+ ```tsx
49
+ // app/layout.tsx
50
+ import { BPMNextProviders } from '@rytass/bpm-core-react/next';
51
+ import { MyHostLayout } from './host-layout';
52
+
53
+ export default function RootLayout({ children }) {
54
+ return (
55
+ <html lang="zh-TW"><body>
56
+ <BPMNextProviders>
57
+ <MyHostLayout>{children}</MyHostLayout>
58
+ </BPMNextProviders>
59
+ </body></html>
60
+ );
61
+ }
62
+ ```
63
+
64
+ ```tsx
65
+ // app/host-layout.tsx
66
+ 'use client';
67
+ import {
68
+ BPMNotificationBellButton,
69
+ useBPMLogout,
70
+ useBPMMember,
71
+ useBPMRoutes,
72
+ useRouterAdapter,
73
+ } from '@rytass/bpm-core-react';
74
+
75
+ export function MyHostLayout({ children }) {
76
+ const router = useRouterAdapter();
77
+ const routes = useBPMRoutes();
78
+ const member = useBPMMember();
79
+ const logout = useBPMLogout();
80
+ return (
81
+ <div className="grid grid-cols-[240px_1fr]">
82
+ <aside>
83
+ <h1>My Console</h1>
84
+ <ul>
85
+ <li><a href={routes.dashboard()}>工作台</a></li>
86
+ <li><a href={routes.inbox()}>我的待簽</a></li>
87
+ {/* …host's own nav items… */}
88
+ </ul>
89
+ <div className="flex items-center gap-2">
90
+ <span>{member?.name}</span>
91
+ <BPMNotificationBellButton />
92
+ <button onClick={() => logout()}>登出</button>
93
+ </div>
94
+ </aside>
95
+ <main>{children}</main>
96
+ </div>
97
+ );
98
+ }
99
+ ```
100
+
101
+ The page shims under `pages/*` are unchanged — they remain one-line
102
+ `{ default, metadata }` re-exports and are mounted under the host
103
+ layout exactly like any other Next.js page:
43
104
 
44
105
  ```tsx
45
- // app/login/page.tsx
46
- export { default, metadata } from '@rytass/bpm-core-react/pages/login';
106
+ // app/inbox/page.tsx
107
+ export { default, metadata } from '@rytass/bpm-core-react/pages/inbox';
47
108
  ```
48
109
 
110
+ For an end-to-end reference layout (with the original 4-group BPM nav
111
+ structure, admin-only filtering, and member display), see
112
+ `apps/client/src/app/_components/host-layout.tsx` in the BPMCore repo.
113
+
49
114
  ### Framework-agnostic view
50
115
 
51
116
  ```tsx
@@ -0,0 +1,278 @@
1
+ "use client";
2
+ import { r as e } from "./router-adapter-BdHZXLS3.js";
3
+ import { t } from "./format-date-time-CB-LxzqT.js";
4
+ import { r as n } from "./routes-config-dxahImVe.js";
5
+ import { useCallback as r, useEffect as i, useMemo as a, useState as o } from "react";
6
+ import { Filter as s, FilterArea as c, FilterLine as l, FormField as u, Input as d, PageHeader as f, Section as ee, SectionGroup as te, Select as ne, Table as p, Typography as m } from "@mezzanine-ui/react";
7
+ import { resolveMembers as h } from "@rytass/bpm-core-client";
8
+ import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
9
+ import { listApprovalInstancesPage as y, readApprovalInstanceCaseTitle as b } from "@rytass/bpm-core-client/workflow";
10
+ import x from "@mezzanine-ui/react/ContentHeader";
11
+ import { FormFieldLayout as S } from "@mezzanine-ui/core/form";
12
+ import '../approval-instance-list-page.css';var C = { instanceFilterArea: "bpm_instanceFilterArea_qpvJq" }, w = [
13
+ 10,
14
+ 20,
15
+ 50
16
+ ], T = [
17
+ {
18
+ id: "ALL",
19
+ name: "全部狀態",
20
+ state: null
21
+ },
22
+ {
23
+ id: "RUNNING",
24
+ name: "進行中",
25
+ state: "RUNNING"
26
+ },
27
+ {
28
+ id: "APPROVED",
29
+ name: "已通過",
30
+ state: "APPROVED"
31
+ },
32
+ {
33
+ id: "REJECTED",
34
+ name: "已拒絕",
35
+ state: "REJECTED"
36
+ },
37
+ {
38
+ id: "RETURNED",
39
+ name: "已退回",
40
+ state: "RETURNED"
41
+ },
42
+ {
43
+ id: "CANCELLED",
44
+ name: "已取消",
45
+ state: "CANCELLED"
46
+ },
47
+ {
48
+ id: "EXPIRED",
49
+ name: "已逾期",
50
+ state: "EXPIRED"
51
+ },
52
+ {
53
+ id: "DRAFT",
54
+ name: "草稿",
55
+ state: "DRAFT"
56
+ }
57
+ ];
58
+ function E({ defaultState: b, description: E, emptyMessage: O, searchPlaceholder: k, title: N, view: P }) {
59
+ let F = e(), I = n(), [L, R] = o(null), [z, B] = o(/* @__PURE__ */ new Map()), [V, H] = o(1), [U, W] = o(10), [G, K] = o(0), [q, J] = o(!0), [Y, oe] = o([]), [X, se] = o(""), [Z, Q] = o(D(b)), $ = r(async () => {
60
+ J(!0), R(null);
61
+ try {
62
+ let e = await y({
63
+ page: V,
64
+ pageSize: U,
65
+ searchText: X,
66
+ state: Z.state,
67
+ templateId: null,
68
+ view: P
69
+ });
70
+ oe(e.instances.map(re)), K(e.totalCount);
71
+ } catch (e) {
72
+ R(M(e));
73
+ } finally {
74
+ J(!1);
75
+ }
76
+ }, [
77
+ V,
78
+ U,
79
+ X,
80
+ Z,
81
+ P
82
+ ]);
83
+ i(() => {
84
+ $();
85
+ }, [$]), i(() => {
86
+ let e = Array.from(new Set(Y.map((e) => e.initiatorMemberId).filter(Boolean)));
87
+ if (e.length === 0) {
88
+ B(/* @__PURE__ */ new Map());
89
+ return;
90
+ }
91
+ let t = !1;
92
+ return (async () => {
93
+ try {
94
+ let n = await h(e);
95
+ if (t) return;
96
+ B(new Map(n.map((e) => [e.memberId, e])));
97
+ } catch {
98
+ if (t) return;
99
+ B(/* @__PURE__ */ new Map());
100
+ }
101
+ })(), () => {
102
+ t = !0;
103
+ };
104
+ }, [Y]);
105
+ let ce = a(() => [
106
+ {
107
+ dataIndex: "caseTitle",
108
+ key: "caseTitle",
109
+ title: "案件",
110
+ width: 300
111
+ },
112
+ {
113
+ key: "state",
114
+ render: (e) => /* @__PURE__ */ _(m, {
115
+ color: A(e.state),
116
+ component: "span",
117
+ variant: "body",
118
+ children: e.stateLabel
119
+ }),
120
+ title: "狀態",
121
+ width: 120
122
+ },
123
+ {
124
+ key: "initiatorMemberId",
125
+ render: (e) => /* @__PURE__ */ _(m, {
126
+ component: "span",
127
+ variant: "body",
128
+ children: j(e.initiatorMemberId, z)
129
+ }),
130
+ title: "發起人",
131
+ width: 180
132
+ },
133
+ {
134
+ key: "startedAt",
135
+ render: (e) => /* @__PURE__ */ _(m, {
136
+ component: "span",
137
+ variant: "body",
138
+ children: t(e.startedAt)
139
+ }),
140
+ title: "發起時間",
141
+ width: 220
142
+ },
143
+ {
144
+ key: "completedAt",
145
+ render: (e) => /* @__PURE__ */ _(m, {
146
+ component: "span",
147
+ variant: "body",
148
+ children: t(e.completedAt)
149
+ }),
150
+ title: "完成時間",
151
+ width: 220
152
+ }
153
+ ], [z]), le = a(() => ({
154
+ render: (e) => [{
155
+ name: "查看",
156
+ onClick: () => F.push(I.caseDetail(e.id))
157
+ }],
158
+ variant: "base-secondary",
159
+ width: 88
160
+ }), [F]);
161
+ return /* @__PURE__ */ v(g, { children: [/* @__PURE__ */ _(f, { children: /* @__PURE__ */ _(x, {
162
+ description: E,
163
+ title: N
164
+ }) }), /* @__PURE__ */ _(te, { children: /* @__PURE__ */ v(ee, {
165
+ filterArea: /* @__PURE__ */ _(c, {
166
+ className: C.instanceFilterArea,
167
+ size: "sub",
168
+ children: /* @__PURE__ */ v(l, { children: [/* @__PURE__ */ _(s, {
169
+ span: 3,
170
+ children: /* @__PURE__ */ _(u, {
171
+ fullWidth: !0,
172
+ layout: S.VERTICAL,
173
+ name: "instanceSearchText",
174
+ children: /* @__PURE__ */ _(d, {
175
+ fullWidth: !0,
176
+ onChange: (e) => {
177
+ se(e.target.value), H(1);
178
+ },
179
+ placeholder: k,
180
+ size: "sub",
181
+ value: X,
182
+ variant: "base"
183
+ })
184
+ })
185
+ }), /* @__PURE__ */ _(s, {
186
+ span: 2,
187
+ children: /* @__PURE__ */ _(u, {
188
+ fullWidth: !0,
189
+ layout: S.VERTICAL,
190
+ name: "instanceState",
191
+ children: /* @__PURE__ */ _(ne, {
192
+ clearable: !1,
193
+ fullWidth: !0,
194
+ onChange: (e) => {
195
+ Q(ie(e)), H(1);
196
+ },
197
+ options: [...T],
198
+ placeholder: "狀態",
199
+ renderValue: (e) => `狀態:${ae(e)}`,
200
+ size: "sub",
201
+ value: Z
202
+ })
203
+ })
204
+ })] })
205
+ }),
206
+ children: [
207
+ L ? /* @__PURE__ */ _(m, {
208
+ color: "text-error",
209
+ variant: "body",
210
+ children: L
211
+ }) : null,
212
+ !L && !q && Y.length === 0 ? /* @__PURE__ */ _(m, {
213
+ color: "text-neutral",
214
+ variant: "body",
215
+ children: O
216
+ }) : null,
217
+ /* @__PURE__ */ _(p, {
218
+ actions: le,
219
+ columns: ce,
220
+ dataSource: [...Y],
221
+ fullWidth: !0,
222
+ loading: q,
223
+ pagination: {
224
+ current: V,
225
+ onChange: (e) => {
226
+ H(e);
227
+ },
228
+ onChangePageSize: (e) => {
229
+ H(1), W(e);
230
+ },
231
+ pageSize: U,
232
+ pageSizeLabel: "每頁筆數",
233
+ pageSizeOptions: w,
234
+ renderResultSummary: (e, t, n) => `顯示 ${e}-${t} 筆,共 ${n} 筆`,
235
+ showPageSizeOptions: !0,
236
+ total: G
237
+ }
238
+ })
239
+ ]
240
+ }) })] });
241
+ }
242
+ function re(e) {
243
+ return {
244
+ ...e,
245
+ caseTitle: b(e),
246
+ key: e.id,
247
+ stateLabel: k(e.state)
248
+ };
249
+ }
250
+ function ie(e) {
251
+ return O(e) ? D(e.state) : T[0];
252
+ }
253
+ function D(e) {
254
+ return T.find((t) => t.state === e) ?? T[0];
255
+ }
256
+ function ae(e) {
257
+ return O(e) ? e.name : T[0].name;
258
+ }
259
+ function O(e) {
260
+ return typeof e == "object" && !!e && "id" in e && "name" in e && "state" in e;
261
+ }
262
+ function k(e) {
263
+ return e === "RUNNING" ? "進行中" : e === "APPROVED" ? "已通過" : e === "REJECTED" ? "已拒絕" : e === "RETURNED" ? "已退回" : e === "CANCELLED" ? "已取消" : e === "EXPIRED" ? "已逾期" : "草稿";
264
+ }
265
+ function A(e) {
266
+ return e === "APPROVED" ? "text-success" : e === "REJECTED" || e === "CANCELLED" || e === "EXPIRED" ? "text-error" : "text-neutral";
267
+ }
268
+ function j(e, t) {
269
+ let n = (e ?? "").trim();
270
+ return n ? t.get(n)?.name ?? n : "未知發起人";
271
+ }
272
+ function M(e) {
273
+ return e instanceof Error ? e.message : "讀取簽核案件失敗。";
274
+ }
275
+ //#endregion
276
+ export { E as t };
277
+
278
+ //# sourceMappingURL=approval-instance-list-page-BtEc8Cs3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-instance-list-page-BtEc8Cs3.js","names":[],"sources":["../../src/components/approval-instance-list-page.module.scss","../../src/components/approval-instance-list-page.tsx"],"sourcesContent":[".instanceFilterArea {\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","'use client';\n\nimport type { ChangeEvent, ReactElement } from 'react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n PageHeader,\n Section,\n SectionGroup,\n Select,\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 { resolveMembers, type MemberProfileRecord } from '@rytass/bpm-core-client';\nimport {\n listApprovalInstancesPage,\n readApprovalInstanceCaseTitle,\n type ApprovalInstanceRecord,\n type ApprovalInstanceState,\n type ApprovalInstanceView,\n} from '@rytass/bpm-core-client/workflow';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { formatDateTime } from '../lib/format-date-time';\nimport styles from './approval-instance-list-page.module.scss';\n\nexport interface ApprovalInstanceListPageProps {\n readonly defaultState: ApprovalInstanceState | null;\n readonly description: string;\n readonly emptyMessage: string;\n readonly searchPlaceholder: string;\n readonly title: string;\n readonly view: ApprovalInstanceView;\n}\n\ntype StateFilterOption = Readonly<{\n id: 'ALL' | ApprovalInstanceState;\n name: string;\n state: ApprovalInstanceState | null;\n}>;\n\ntype ApprovalInstanceRow = Readonly<\n Record<string, unknown> &\n ApprovalInstanceRecord & {\n caseTitle: string;\n key: string;\n stateLabel: string;\n }\n>;\n\nconst INSTANCE_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst STATE_FILTER_OPTIONS: readonly StateFilterOption[] = [\n { id: 'ALL', name: '全部狀態', state: null },\n { id: 'RUNNING', name: '進行中', state: 'RUNNING' },\n { id: 'APPROVED', name: '已通過', state: 'APPROVED' },\n { id: 'REJECTED', name: '已拒絕', state: 'REJECTED' },\n { id: 'RETURNED', name: '已退回', state: 'RETURNED' },\n { id: 'CANCELLED', name: '已取消', state: 'CANCELLED' },\n { id: 'EXPIRED', name: '已逾期', state: 'EXPIRED' },\n { id: 'DRAFT', name: '草稿', state: 'DRAFT' },\n];\n\n/**\n * Shared list page for any approval-instance \"view\" (inbox / sent / cc /\n * delegated). Caller picks the view + default state filter; the page renders\n * the standard BPM filter bar + paginated table and navigates to\n * `/instances/:id` on row action.\n */\nexport function ApprovalInstanceListPage({\n defaultState,\n description,\n emptyMessage,\n searchPlaceholder,\n title,\n view,\n}: ApprovalInstanceListPageProps): ReactElement {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const [error, setError] = useState<string | null>(null);\n const [initiatorProfilesById, setInitiatorProfilesById] = useState<\n ReadonlyMap<string, MemberProfileRecord>\n >(new Map());\n const [instancePage, setInstancePage] = useState(1);\n const [instancePageSize, setInstancePageSize] = useState(10);\n const [instanceTotalCount, setInstanceTotalCount] = useState(0);\n const [loading, setLoading] = useState(true);\n const [rows, setRows] = useState<readonly ApprovalInstanceRow[]>([]);\n const [searchText, setSearchText] = useState('');\n const [stateFilter, setStateFilter] = useState<StateFilterOption>(\n readStateFilterOption(defaultState),\n );\n\n const refreshInstances = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await listApprovalInstancesPage({\n page: instancePage,\n pageSize: instancePageSize,\n searchText,\n state: stateFilter.state,\n templateId: null,\n view,\n });\n\n setRows(result.instances.map(readApprovalInstanceRow));\n setInstanceTotalCount(result.totalCount);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [instancePage, instancePageSize, searchText, stateFilter, view]);\n\n useEffect((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n\n useEffect((): (() => void) | void => {\n const initiatorMemberIds = Array.from(\n new Set(rows.map((row) => row.initiatorMemberId).filter(Boolean)),\n );\n\n if (initiatorMemberIds.length === 0) {\n setInitiatorProfilesById(new Map());\n\n return;\n }\n\n let cancelled = false;\n\n void (async (): Promise<void> => {\n try {\n const profiles = await resolveMembers(initiatorMemberIds);\n\n if (cancelled) {\n return;\n }\n\n setInitiatorProfilesById(\n new Map(profiles.map((profile) => [profile.memberId, profile])),\n );\n } catch {\n if (cancelled) {\n return;\n }\n\n setInitiatorProfilesById(new Map());\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, [rows]);\n\n const columns = useMemo(\n (): TableColumn<ApprovalInstanceRow>[] => [\n { dataIndex: 'caseTitle', key: 'caseTitle', title: '案件', width: 300 },\n {\n key: 'state',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography\n color={readInstanceStateColor(record.state)}\n component=\"span\"\n variant=\"body\"\n >\n {record.stateLabel}\n </Typography>\n ),\n title: '狀態',\n width: 120,\n },\n {\n key: 'initiatorMemberId',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {readInitiatorLabel(\n record.initiatorMemberId,\n initiatorProfilesById,\n )}\n </Typography>\n ),\n title: '發起人',\n width: 180,\n },\n {\n key: 'startedAt',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.startedAt)}\n </Typography>\n ),\n title: '發起時間',\n width: 220,\n },\n {\n key: 'completedAt',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.completedAt)}\n </Typography>\n ),\n title: '完成時間',\n width: 220,\n },\n ],\n [initiatorProfilesById],\n );\n const tableActions = useMemo(\n (): TableActions<ApprovalInstanceRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<ApprovalInstanceRow>['render']> => [\n {\n name: '查看',\n onClick: (): void => router.push(routes.caseDetail(record.id)),\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router],\n );\n\n return (\n <>\n <PageHeader>\n <ContentHeader description={description} title={title} />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.instanceFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceSearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setInstancePage(1);\n }}\n placeholder={searchPlaceholder}\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n <Filter span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceState\"\n >\n <Select\n clearable={false}\n fullWidth\n onChange={(option): void => {\n setStateFilter(readSelectedStateFilterOption(option));\n setInstancePage(1);\n }}\n options={[...STATE_FILTER_OPTIONS]}\n placeholder=\"狀態\"\n renderValue={(value): string =>\n `狀態:${readStateFilterLabel(value)}`\n }\n size=\"sub\"\n value={stateFilter}\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 {!error && !loading && rows.length === 0 ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n {emptyMessage}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={[...rows]}\n fullWidth\n loading={loading}\n pagination={{\n current: instancePage,\n onChange: (page): void => {\n setInstancePage(page);\n },\n onChangePageSize: (pageSize): void => {\n setInstancePage(1);\n setInstancePageSize(pageSize);\n },\n pageSize: instancePageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: INSTANCE_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: instanceTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction readApprovalInstanceRow(\n instance: ApprovalInstanceRecord,\n): ApprovalInstanceRow {\n return {\n ...instance,\n caseTitle: readApprovalInstanceCaseTitle(instance),\n key: instance.id,\n stateLabel: readInstanceStateLabel(instance.state),\n };\n}\n\nfunction readSelectedStateFilterOption(option: unknown): StateFilterOption {\n if (!isStateFilterOption(option)) {\n return STATE_FILTER_OPTIONS[0];\n }\n return readStateFilterOption(option.state);\n}\n\nfunction readStateFilterOption(\n state: ApprovalInstanceState | null,\n): StateFilterOption {\n return (\n STATE_FILTER_OPTIONS.find((option) => option.state === state) ??\n STATE_FILTER_OPTIONS[0]\n );\n}\n\nfunction readStateFilterLabel(value: unknown): string {\n return isStateFilterOption(value) ? value.name : STATE_FILTER_OPTIONS[0].name;\n}\n\nfunction isStateFilterOption(value: unknown): value is StateFilterOption {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'id' in value &&\n 'name' in value &&\n 'state' in value\n );\n}\n\nfunction readInstanceStateLabel(state: ApprovalInstanceState): string {\n if (state === 'RUNNING') return '進行中';\n if (state === 'APPROVED') return '已通過';\n if (state === 'REJECTED') return '已拒絕';\n if (state === 'RETURNED') return '已退回';\n if (state === 'CANCELLED') return '已取消';\n if (state === 'EXPIRED') return '已逾期';\n return '草稿';\n}\n\nfunction readInstanceStateColor(\n state: ApprovalInstanceState,\n): 'text-error' | 'text-neutral' | 'text-success' {\n if (state === 'APPROVED') return 'text-success';\n if (state === 'REJECTED' || state === 'CANCELLED' || state === 'EXPIRED') {\n return 'text-error';\n }\n return 'text-neutral';\n}\n\nfunction readInitiatorLabel(\n initiatorMemberId: string | null | undefined,\n initiatorProfilesById: ReadonlyMap<string, MemberProfileRecord>,\n): string {\n const trimmedMemberId = (initiatorMemberId ?? '').trim();\n if (!trimmedMemberId) return '未知發起人';\n return initiatorProfilesById.get(trimmedMemberId)?.name ?? trimmedMemberId;\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('../approval-instance-list-page.css');const e=require("./chunk-CMqjfN_6.cjs"),t=require("./router-adapter-BybHrCNP.cjs"),n=require("./format-date-time-XxBzF0F5.cjs"),r=require("./routes-config-2aKbWq2H.cjs");let i=require("react"),a=require("@mezzanine-ui/react"),o=require("@rytass/bpm-core-client"),s=require("react/jsx-runtime"),c=require("@rytass/bpm-core-client/workflow"),l=require("@mezzanine-ui/react/ContentHeader");l=e.t(l,1);let u=require("@mezzanine-ui/core/form");var d={instanceFilterArea:`bpm_instanceFilterArea_qpvJq`},f=[10,20,50],p=[{id:`ALL`,name:`全部狀態`,state:null},{id:`RUNNING`,name:`進行中`,state:`RUNNING`},{id:`APPROVED`,name:`已通過`,state:`APPROVED`},{id:`REJECTED`,name:`已拒絕`,state:`REJECTED`},{id:`RETURNED`,name:`已退回`,state:`RETURNED`},{id:`CANCELLED`,name:`已取消`,state:`CANCELLED`},{id:`EXPIRED`,name:`已逾期`,state:`EXPIRED`},{id:`DRAFT`,name:`草稿`,state:`DRAFT`}];function m({defaultState:e,description:m,emptyMessage:y,searchPlaceholder:b,title:w,view:T}){let E=t.r(),D=r.r(),[O,k]=(0,i.useState)(null),[A,j]=(0,i.useState)(new Map),[M,N]=(0,i.useState)(1),[P,F]=(0,i.useState)(10),[I,L]=(0,i.useState)(0),[R,z]=(0,i.useState)(!0),[B,V]=(0,i.useState)([]),[H,U]=(0,i.useState)(``),[W,G]=(0,i.useState)(_(e)),K=(0,i.useCallback)(async()=>{z(!0),k(null);try{let e=await(0,c.listApprovalInstancesPage)({page:M,pageSize:P,searchText:H,state:W.state,templateId:null,view:T});V(e.instances.map(h)),L(e.totalCount)}catch(e){k(C(e))}finally{z(!1)}},[M,P,H,W,T]);(0,i.useEffect)(()=>{K()},[K]),(0,i.useEffect)(()=>{let e=Array.from(new Set(B.map(e=>e.initiatorMemberId).filter(Boolean)));if(e.length===0){j(new Map);return}let t=!1;return(async()=>{try{let n=await(0,o.resolveMembers)(e);if(t)return;j(new Map(n.map(e=>[e.memberId,e])))}catch{if(t)return;j(new Map)}})(),()=>{t=!0}},[B]);let q=(0,i.useMemo)(()=>[{dataIndex:`caseTitle`,key:`caseTitle`,title:`案件`,width:300},{key:`state`,render:e=>(0,s.jsx)(a.Typography,{color:x(e.state),component:`span`,variant:`body`,children:e.stateLabel}),title:`狀態`,width:120},{key:`initiatorMemberId`,render:e=>(0,s.jsx)(a.Typography,{component:`span`,variant:`body`,children:S(e.initiatorMemberId,A)}),title:`發起人`,width:180},{key:`startedAt`,render:e=>(0,s.jsx)(a.Typography,{component:`span`,variant:`body`,children:n.t(e.startedAt)}),title:`發起時間`,width:220},{key:`completedAt`,render:e=>(0,s.jsx)(a.Typography,{component:`span`,variant:`body`,children:n.t(e.completedAt)}),title:`完成時間`,width:220}],[A]),J=(0,i.useMemo)(()=>({render:e=>[{name:`查看`,onClick:()=>E.push(D.caseDetail(e.id))}],variant:`base-secondary`,width:88}),[E]);return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.PageHeader,{children:(0,s.jsx)(l.default,{description:m,title:w})}),(0,s.jsx)(a.SectionGroup,{children:(0,s.jsxs)(a.Section,{filterArea:(0,s.jsx)(a.FilterArea,{className:d.instanceFilterArea,size:`sub`,children:(0,s.jsxs)(a.FilterLine,{children:[(0,s.jsx)(a.Filter,{span:3,children:(0,s.jsx)(a.FormField,{fullWidth:!0,layout:u.FormFieldLayout.VERTICAL,name:`instanceSearchText`,children:(0,s.jsx)(a.Input,{fullWidth:!0,onChange:e=>{U(e.target.value),N(1)},placeholder:b,size:`sub`,value:H,variant:`base`})})}),(0,s.jsx)(a.Filter,{span:2,children:(0,s.jsx)(a.FormField,{fullWidth:!0,layout:u.FormFieldLayout.VERTICAL,name:`instanceState`,children:(0,s.jsx)(a.Select,{clearable:!1,fullWidth:!0,onChange:e=>{G(g(e)),N(1)},options:[...p],placeholder:`狀態`,renderValue:e=>`狀態:${v(e)}`,size:`sub`,value:W})})})]})}),children:[O?(0,s.jsx)(a.Typography,{color:`text-error`,variant:`body`,children:O}):null,!O&&!R&&B.length===0?(0,s.jsx)(a.Typography,{color:`text-neutral`,variant:`body`,children:y}):null,(0,s.jsx)(a.Table,{actions:J,columns:q,dataSource:[...B],fullWidth:!0,loading:R,pagination:{current:M,onChange:e=>{N(e)},onChangePageSize:e=>{N(1),F(e)},pageSize:P,pageSizeLabel:`每頁筆數`,pageSizeOptions:f,renderResultSummary:(e,t,n)=>`顯示 ${e}-${t} 筆,共 ${n} 筆`,showPageSizeOptions:!0,total:I}})]})})]})}function h(e){return{...e,caseTitle:(0,c.readApprovalInstanceCaseTitle)(e),key:e.id,stateLabel:b(e.state)}}function g(e){return y(e)?_(e.state):p[0]}function _(e){return p.find(t=>t.state===e)??p[0]}function v(e){return y(e)?e.name:p[0].name}function y(e){return typeof e==`object`&&!!e&&`id`in e&&`name`in e&&`state`in e}function b(e){return e===`RUNNING`?`進行中`:e===`APPROVED`?`已通過`:e===`REJECTED`?`已拒絕`:e===`RETURNED`?`已退回`:e===`CANCELLED`?`已取消`:e===`EXPIRED`?`已逾期`:`草稿`}function x(e){return e===`APPROVED`?`text-success`:e===`REJECTED`||e===`CANCELLED`||e===`EXPIRED`?`text-error`:`text-neutral`}function S(e,t){let n=(e??``).trim();return n?t.get(n)?.name??n:`未知發起人`}function C(e){return e instanceof Error?e.message:`讀取簽核案件失敗。`}Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return m}});
2
+ //# sourceMappingURL=approval-instance-list-page-UNIIgUZy.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-instance-list-page-UNIIgUZy.cjs","names":[],"sources":["../../src/components/approval-instance-list-page.module.scss","../../src/components/approval-instance-list-page.tsx"],"sourcesContent":[".instanceFilterArea {\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","'use client';\n\nimport type { ChangeEvent, ReactElement } from 'react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n PageHeader,\n Section,\n SectionGroup,\n Select,\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 { resolveMembers, type MemberProfileRecord } from '@rytass/bpm-core-client';\nimport {\n listApprovalInstancesPage,\n readApprovalInstanceCaseTitle,\n type ApprovalInstanceRecord,\n type ApprovalInstanceState,\n type ApprovalInstanceView,\n} from '@rytass/bpm-core-client/workflow';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { formatDateTime } from '../lib/format-date-time';\nimport styles from './approval-instance-list-page.module.scss';\n\nexport interface ApprovalInstanceListPageProps {\n readonly defaultState: ApprovalInstanceState | null;\n readonly description: string;\n readonly emptyMessage: string;\n readonly searchPlaceholder: string;\n readonly title: string;\n readonly view: ApprovalInstanceView;\n}\n\ntype StateFilterOption = Readonly<{\n id: 'ALL' | ApprovalInstanceState;\n name: string;\n state: ApprovalInstanceState | null;\n}>;\n\ntype ApprovalInstanceRow = Readonly<\n Record<string, unknown> &\n ApprovalInstanceRecord & {\n caseTitle: string;\n key: string;\n stateLabel: string;\n }\n>;\n\nconst INSTANCE_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst STATE_FILTER_OPTIONS: readonly StateFilterOption[] = [\n { id: 'ALL', name: '全部狀態', state: null },\n { id: 'RUNNING', name: '進行中', state: 'RUNNING' },\n { id: 'APPROVED', name: '已通過', state: 'APPROVED' },\n { id: 'REJECTED', name: '已拒絕', state: 'REJECTED' },\n { id: 'RETURNED', name: '已退回', state: 'RETURNED' },\n { id: 'CANCELLED', name: '已取消', state: 'CANCELLED' },\n { id: 'EXPIRED', name: '已逾期', state: 'EXPIRED' },\n { id: 'DRAFT', name: '草稿', state: 'DRAFT' },\n];\n\n/**\n * Shared list page for any approval-instance \"view\" (inbox / sent / cc /\n * delegated). Caller picks the view + default state filter; the page renders\n * the standard BPM filter bar + paginated table and navigates to\n * `/instances/:id` on row action.\n */\nexport function ApprovalInstanceListPage({\n defaultState,\n description,\n emptyMessage,\n searchPlaceholder,\n title,\n view,\n}: ApprovalInstanceListPageProps): ReactElement {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const [error, setError] = useState<string | null>(null);\n const [initiatorProfilesById, setInitiatorProfilesById] = useState<\n ReadonlyMap<string, MemberProfileRecord>\n >(new Map());\n const [instancePage, setInstancePage] = useState(1);\n const [instancePageSize, setInstancePageSize] = useState(10);\n const [instanceTotalCount, setInstanceTotalCount] = useState(0);\n const [loading, setLoading] = useState(true);\n const [rows, setRows] = useState<readonly ApprovalInstanceRow[]>([]);\n const [searchText, setSearchText] = useState('');\n const [stateFilter, setStateFilter] = useState<StateFilterOption>(\n readStateFilterOption(defaultState),\n );\n\n const refreshInstances = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const result = await listApprovalInstancesPage({\n page: instancePage,\n pageSize: instancePageSize,\n searchText,\n state: stateFilter.state,\n templateId: null,\n view,\n });\n\n setRows(result.instances.map(readApprovalInstanceRow));\n setInstanceTotalCount(result.totalCount);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [instancePage, instancePageSize, searchText, stateFilter, view]);\n\n useEffect((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n\n useEffect((): (() => void) | void => {\n const initiatorMemberIds = Array.from(\n new Set(rows.map((row) => row.initiatorMemberId).filter(Boolean)),\n );\n\n if (initiatorMemberIds.length === 0) {\n setInitiatorProfilesById(new Map());\n\n return;\n }\n\n let cancelled = false;\n\n void (async (): Promise<void> => {\n try {\n const profiles = await resolveMembers(initiatorMemberIds);\n\n if (cancelled) {\n return;\n }\n\n setInitiatorProfilesById(\n new Map(profiles.map((profile) => [profile.memberId, profile])),\n );\n } catch {\n if (cancelled) {\n return;\n }\n\n setInitiatorProfilesById(new Map());\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, [rows]);\n\n const columns = useMemo(\n (): TableColumn<ApprovalInstanceRow>[] => [\n { dataIndex: 'caseTitle', key: 'caseTitle', title: '案件', width: 300 },\n {\n key: 'state',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography\n color={readInstanceStateColor(record.state)}\n component=\"span\"\n variant=\"body\"\n >\n {record.stateLabel}\n </Typography>\n ),\n title: '狀態',\n width: 120,\n },\n {\n key: 'initiatorMemberId',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {readInitiatorLabel(\n record.initiatorMemberId,\n initiatorProfilesById,\n )}\n </Typography>\n ),\n title: '發起人',\n width: 180,\n },\n {\n key: 'startedAt',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.startedAt)}\n </Typography>\n ),\n title: '發起時間',\n width: 220,\n },\n {\n key: 'completedAt',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.completedAt)}\n </Typography>\n ),\n title: '完成時間',\n width: 220,\n },\n ],\n [initiatorProfilesById],\n );\n const tableActions = useMemo(\n (): TableActions<ApprovalInstanceRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<ApprovalInstanceRow>['render']> => [\n {\n name: '查看',\n onClick: (): void => router.push(routes.caseDetail(record.id)),\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router],\n );\n\n return (\n <>\n <PageHeader>\n <ContentHeader description={description} title={title} />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.instanceFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceSearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setInstancePage(1);\n }}\n placeholder={searchPlaceholder}\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n <Filter span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceState\"\n >\n <Select\n clearable={false}\n fullWidth\n onChange={(option): void => {\n setStateFilter(readSelectedStateFilterOption(option));\n setInstancePage(1);\n }}\n options={[...STATE_FILTER_OPTIONS]}\n placeholder=\"狀態\"\n renderValue={(value): string =>\n `狀態:${readStateFilterLabel(value)}`\n }\n size=\"sub\"\n value={stateFilter}\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 {!error && !loading && rows.length === 0 ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n {emptyMessage}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={[...rows]}\n fullWidth\n loading={loading}\n pagination={{\n current: instancePage,\n onChange: (page): void => {\n setInstancePage(page);\n },\n onChangePageSize: (pageSize): void => {\n setInstancePage(1);\n setInstancePageSize(pageSize);\n },\n pageSize: instancePageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: INSTANCE_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: instanceTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction readApprovalInstanceRow(\n instance: ApprovalInstanceRecord,\n): ApprovalInstanceRow {\n return {\n ...instance,\n caseTitle: readApprovalInstanceCaseTitle(instance),\n key: instance.id,\n stateLabel: readInstanceStateLabel(instance.state),\n };\n}\n\nfunction readSelectedStateFilterOption(option: unknown): StateFilterOption {\n if (!isStateFilterOption(option)) {\n return STATE_FILTER_OPTIONS[0];\n }\n return readStateFilterOption(option.state);\n}\n\nfunction readStateFilterOption(\n state: ApprovalInstanceState | null,\n): StateFilterOption {\n return (\n STATE_FILTER_OPTIONS.find((option) => option.state === state) ??\n STATE_FILTER_OPTIONS[0]\n );\n}\n\nfunction readStateFilterLabel(value: unknown): string {\n return isStateFilterOption(value) ? value.name : STATE_FILTER_OPTIONS[0].name;\n}\n\nfunction isStateFilterOption(value: unknown): value is StateFilterOption {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'id' in value &&\n 'name' in value &&\n 'state' in value\n );\n}\n\nfunction readInstanceStateLabel(state: ApprovalInstanceState): string {\n if (state === 'RUNNING') return '進行中';\n if (state === 'APPROVED') return '已通過';\n if (state === 'REJECTED') return '已拒絕';\n if (state === 'RETURNED') return '已退回';\n if (state === 'CANCELLED') return '已取消';\n if (state === 'EXPIRED') return '已逾期';\n return '草稿';\n}\n\nfunction readInstanceStateColor(\n state: ApprovalInstanceState,\n): 'text-error' | 'text-neutral' | 'text-success' {\n if (state === 'APPROVED') return 'text-success';\n if (state === 'REJECTED' || state === 'CANCELLED' || state === 'EXPIRED') {\n return 'text-error';\n }\n return 'text-neutral';\n}\n\nfunction readInitiatorLabel(\n initiatorMemberId: string | null | undefined,\n initiatorProfilesById: ReadonlyMap<string, MemberProfileRecord>,\n): string {\n const trimmedMemberId = (initiatorMemberId ?? '').trim();\n if (!trimmedMemberId) return '未知發起人';\n return initiatorProfilesById.get(trimmedMemberId)?.name ?? trimmedMemberId;\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('../auth-provider.css');const e=require("./router-adapter-BybHrCNP.cjs");let t=require("react"),n=require("@mezzanine-ui/react"),r=require("@rytass/bpm-core-client"),i=require("react/jsx-runtime");var a={authLoading:`bpm_authLoading_GiBnf`},o=(0,t.createContext)(null);function s({children:n,publicPaths:a=[`/login`],loginPath:s=`/login`}){let c=e.r(),[f,p]=(0,t.useState)(!0),[m,h]=(0,t.useState)(null),g=(0,t.useCallback)(async()=>{let e=await(0,r.readApiCurrentMember)();return h(e),e},[]),_=(0,t.useCallback)(async e=>{let t=await(0,r.loginApi)(e);return h(t),t},[]),v=(0,t.useCallback)(async()=>{await(0,r.logoutApi)(),h(null),c.replace(s)},[s,c]);(0,t.useEffect)(()=>{let e=!0;return p(!0),(async()=>{try{let t=await(0,r.readApiCurrentMember)();e&&h(t)}catch{e&&h(null)}finally{e&&p(!1)}})(),()=>{e=!1}},[]),(0,t.useEffect)(()=>{f||u(c.pathname,a)||m||c.replace(`${s}?next=${encodeURIComponent(d(c.pathname))}`)},[f,s,m,a,c]);let y=(0,t.useMemo)(()=>({loading:f,login:_,logout:v,member:m,refresh:g}),[f,_,v,m,g]);return f&&!u(c.pathname,a)?(0,i.jsx)(l,{label:`確認登入狀態`}):!f&&!m&&!u(c.pathname,a)?(0,i.jsx)(l,{label:`前往登入頁`}):(0,i.jsx)(o.Provider,{value:y,children:n})}function c(){let e=(0,t.useContext)(o);if(!e)throw Error(`useAuth must be used inside <AuthProvider>`);return e}function l({label:e}){return(0,i.jsx)(`main`,{className:a.authLoading,children:(0,i.jsx)(n.Typography,{color:`text-neutral`,variant:`body`,children:e})})}function u(e,t){return t.some(t=>t===e)}function d(e){return typeof window>`u`?e??`/`:`${window.location.pathname}${window.location.search}`}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return s}});
2
+ //# sourceMappingURL=auth-provider-D2P-qWmY.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-provider-D2P-qWmY.cjs","names":[],"sources":["../../src/lib/auth-provider.module.scss","../../src/lib/auth-provider.tsx"],"sourcesContent":[".authLoading {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { Typography } from '@mezzanine-ui/react';\nimport {\n loginApi,\n logoutApi,\n readApiCurrentMember,\n type ApiMember,\n} from '@rytass/bpm-core-client';\nimport { useRouterAdapter } from './router-adapter';\nimport styles from './auth-provider.module.scss';\n\ninterface AuthContextValue {\n readonly loading: boolean;\n readonly member: ApiMember | null;\n readonly login: (input: {\n readonly identifier: string;\n readonly password: string;\n }) => Promise<ApiMember>;\n readonly logout: () => Promise<void>;\n readonly refresh: () => Promise<ApiMember | null>;\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\nexport interface AuthProviderProps {\n readonly children: ReactNode;\n /**\n * Paths that should not redirect to `/login` when there is no session.\n * Defaults to `['/login']`. Override when your host runs the login UI\n * under a different path.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to send unauthenticated users. Defaults to `'/login'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * BPM auth context provider. Reads / writes the host BPM API session via\n * `@rytass/bpm-core-client` (`loginApi` / `logoutApi` / `readApiCurrentMember`)\n * and uses the host-supplied {@link useRouterAdapter} to redirect\n * unauthenticated users.\n */\nexport function AuthProvider({\n children,\n publicPaths = ['/login'],\n loginPath = '/login',\n}: AuthProviderProps): ReactElement {\n const router = useRouterAdapter();\n const [loading, setLoading] = useState(true);\n const [member, setMember] = useState<ApiMember | null>(null);\n\n const refresh = useCallback(async (): Promise<ApiMember | null> => {\n const current = await readApiCurrentMember();\n setMember(current);\n return current;\n }, []);\n\n const login = useCallback(\n async (input: {\n readonly identifier: string;\n readonly password: string;\n }): Promise<ApiMember> => {\n const next = await loginApi(input);\n setMember(next);\n return next;\n },\n [],\n );\n\n const logout = useCallback(async (): Promise<void> => {\n await logoutApi();\n setMember(null);\n router.replace(loginPath);\n }, [loginPath, router]);\n\n useEffect((): (() => void) => {\n let active = true;\n setLoading(true);\n void (async () => {\n try {\n const current = await readApiCurrentMember();\n if (active) setMember(current);\n } catch {\n if (active) setMember(null);\n } finally {\n if (active) setLoading(false);\n }\n })();\n return (): void => {\n active = false;\n };\n }, []);\n\n useEffect((): void => {\n if (loading || isPublicPath(router.pathname, publicPaths) || member) return;\n router.replace(\n `${loginPath}?next=${encodeURIComponent(readCurrentPath(router.pathname))}`,\n );\n }, [loading, loginPath, member, publicPaths, router]);\n\n const value = useMemo<AuthContextValue>(\n () => ({ loading, login, logout, member, refresh }),\n [loading, login, logout, member, refresh],\n );\n\n if (loading && !isPublicPath(router.pathname, publicPaths)) {\n return <AuthLoadingState label=\"確認登入狀態\" />;\n }\n if (!loading && !member && !isPublicPath(router.pathname, publicPaths)) {\n return <AuthLoadingState label=\"前往登入頁\" />;\n }\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Access the BPM auth context. Throws when used outside `<AuthProvider>`.\n */\nexport function useAuth(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>');\n return ctx;\n}\n\nfunction AuthLoadingState({\n label,\n}: {\n readonly label: string;\n}): ReactElement {\n return (\n <main className={styles.authLoading}>\n <Typography color=\"text-neutral\" variant=\"body\">\n {label}\n </Typography>\n </main>\n );\n}\n\nfunction isPublicPath(\n pathname: string | null,\n publicPaths: readonly string[],\n): boolean {\n return publicPaths.some((p) => p === pathname);\n}\n\nfunction readCurrentPath(pathname: string | null): string {\n if (typeof window === 'undefined') return pathname ?? '/';\n return `${window.location.pathname}${window.location.search}`;\n}\n"],"mappings":"sOCiCM,GAAA,EAAA,EAAA,eAAqD,IAAI,EAsB/D,SAAgB,EAAa,CAC3B,WACA,cAAc,CAAC,QAAQ,EACvB,YAAY,UACsB,CAClC,IAAM,EAAS,EAAA,EAAiB,EAC1B,CAAC,EAAS,IAAA,EAAA,EAAA,UAAuB,EAAI,EACrC,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAwC,IAAI,EAErD,GAAA,EAAA,EAAA,aAAsB,SAAuC,CACjE,IAAM,EAAU,MAAA,EAAA,EAAA,sBAA2B,EAE3C,OADA,EAAU,CAAO,EACV,CACT,EAAG,CAAC,CAAC,EAEC,GAAA,EAAA,EAAA,aACJ,KAAO,IAGmB,CACxB,IAAM,EAAO,MAAA,EAAA,EAAA,UAAe,CAAK,EAEjC,OADA,EAAU,CAAI,EACP,CACT,EACA,CAAC,CACH,EAEM,GAAA,EAAA,EAAA,aAAqB,SAA2B,CACpD,MAAA,EAAA,EAAA,WAAgB,EAChB,EAAU,IAAI,EACd,EAAO,QAAQ,CAAS,CAC1B,EAAG,CAAC,EAAW,CAAM,CAAC,GAEtB,EAAA,EAAA,eAA8B,CAC5B,IAAI,EAAS,GAYb,OAXA,EAAW,EAAI,GACT,SAAY,CAChB,GAAI,CACF,IAAM,EAAU,MAAA,EAAA,EAAA,sBAA2B,EACvC,GAAQ,EAAU,CAAO,CAC/B,MAAQ,CACF,GAAQ,EAAU,IAAI,CAC5B,QAAU,CACJ,GAAQ,EAAW,EAAK,CAC9B,CACF,GAAG,MACgB,CACjB,EAAS,EACX,CACF,EAAG,CAAC,CAAC,GAEL,EAAA,EAAA,eAAsB,CAChB,GAAW,EAAa,EAAO,SAAU,CAAW,GAAK,GAC7D,EAAO,QACL,GAAG,EAAU,QAAQ,mBAAmB,EAAgB,EAAO,QAAQ,CAAC,GAC1E,CACF,EAAG,CAAC,EAAS,EAAW,EAAQ,EAAa,CAAM,CAAC,EAEpD,IAAM,GAAA,EAAA,EAAA,cACG,CAAE,UAAS,QAAO,SAAQ,SAAQ,SAAQ,GACjD,CAAC,EAAS,EAAO,EAAQ,EAAQ,CAAO,CAC1C,EAQA,OANI,GAAW,CAAC,EAAa,EAAO,SAAU,CAAW,GAChD,EAAA,EAAA,KAAC,EAAD,CAAkB,MAAM,QAAU,CAAA,EAEvC,CAAC,GAAW,CAAC,GAAU,CAAC,EAAa,EAAO,SAAU,CAAW,GAC5D,EAAA,EAAA,KAAC,EAAD,CAAkB,MAAM,OAAS,CAAA,GAEnC,EAAA,EAAA,KAAC,EAAY,SAAb,CAA6B,QAAQ,UAA+B,CAAA,CAC7E,CAKA,SAAgB,GAA4B,CAC1C,IAAM,GAAA,EAAA,EAAA,YAAiB,CAAW,EAClC,GAAI,CAAC,EAAK,MAAU,MAAM,4CAA4C,EACtE,OAAO,CACT,CAEA,SAAS,EAAiB,CACxB,SAGe,CACf,OACE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAW,EAAO,sBACtB,EAAA,EAAA,KAAC,EAAA,WAAD,CAAY,MAAM,eAAe,QAAQ,gBACtC,CACS,CAAA,CACR,CAAA,CAEV,CAEA,SAAS,EACP,EACA,EACS,CACT,OAAO,EAAY,KAAM,GAAM,IAAM,CAAQ,CAC/C,CAEA,SAAS,EAAgB,EAAiC,CAExD,OADI,OAAO,OAAW,IAAoB,GAAY,IAC/C,GAAG,OAAO,SAAS,WAAW,OAAO,SAAS,QACvD"}