@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detail-CSxI04gB.js","names":[],"sources":["../../src/components/pdf-preview.module.scss","../../src/components/pdf-preview.tsx","../../src/views/instances/detail/InstanceDetailView.tsx"],"sourcesContent":[".root {\n display: grid;\n gap: 12px;\n width: min(86vw, 1080px);\n}\n\n.toolbar {\n align-items: center;\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n justify-content: space-between;\n}\n\n.pageControls,\n.zoomControls {\n align-items: center;\n display: flex;\n gap: 8px;\n}\n\n.counter {\n min-width: 92px;\n text-align: center;\n white-space: nowrap;\n}\n\n.viewport {\n align-items: flex-start;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n display: flex;\n height: min(72vh, 760px);\n justify-content: center;\n min-height: 420px;\n overflow: auto;\n padding: 16px;\n}\n\n.page {\n background: #ffffff;\n box-shadow: 0 12px 32px rgb(15 23 42 / 14%);\n color: #111827;\n}\n\n.state {\n align-items: center;\n display: flex;\n justify-content: center;\n min-height: 320px;\n width: 100%;\n}\n\n@media (max-width: 720px) {\n .root {\n width: 92vw;\n }\n\n .toolbar {\n align-items: stretch;\n display: grid;\n }\n\n .pageControls,\n .zoomControls {\n justify-content: space-between;\n }\n\n .counter {\n min-width: 84px;\n }\n\n .viewport {\n height: 68vh;\n min-height: 360px;\n padding: 12px;\n }\n}\n","'use client';\n\nimport {\n ReactElement,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { Button, Typography } from '@mezzanine-ui/react';\nimport {\n ChevronLeftIcon,\n ChevronRightIcon,\n DownloadIcon,\n ZoomInIcon,\n ZoomOutIcon,\n} from '@mezzanine-ui/icons';\nimport { Document, Page, pdfjs } from 'react-pdf';\nimport type { PDFDocumentProxy } from 'pdfjs-dist';\nimport styles from './pdf-preview.module.scss';\n\n// pdfjs ships its worker as a separate chunk. Consumers should call\n// `configurePdfWorker(url)` (or set `pdfjs.GlobalWorkerOptions.workerSrc`\n// directly) before the first `<PDFPreview>` mounts. The library does not\n// hard-bundle a worker path because consumer bundlers may need to resolve\n// it through CDN, public/ asset pipeline, or pinned version.\nexport function configurePdfWorker(workerSrc: string): void {\n pdfjs.GlobalWorkerOptions.workerSrc = workerSrc;\n}\n\nconst DEFAULT_VIEWPORT_WIDTH = 760;\nconst MAX_PAGE_WIDTH = 920;\nconst MIN_PAGE_WIDTH = 320;\nconst PAGE_HORIZONTAL_PADDING = 32;\nconst MIN_SCALE = 0.75;\nconst MAX_SCALE = 1.75;\nconst SCALE_STEP = 0.25;\n\nexport interface PDFPreviewProps {\n readonly filename: string;\n readonly fileUrl: string;\n readonly onDownload?: () => void;\n}\n\n/**\n * Mezzanine-styled PDF preview powered by `react-pdf`. Used by the instance\n * detail view to preview PDF attachments inline.\n */\nexport function PDFPreview({\n filename,\n fileUrl,\n onDownload,\n}: PDFPreviewProps): ReactElement {\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const [numPages, setNumPages] = useState<number | null>(null);\n const [pageNumber, setPageNumber] = useState(1);\n const [scale, setScale] = useState(1);\n const [viewportWidth, setViewportWidth] = useState(DEFAULT_VIEWPORT_WIDTH);\n\n useEffect((): (() => void) | undefined => {\n const viewport = viewportRef.current;\n\n if (!viewport || typeof ResizeObserver === 'undefined') {\n return undefined;\n }\n\n const observer = new ResizeObserver((entries): void => {\n const nextWidth = entries[0]?.contentRect.width;\n\n if (nextWidth) {\n setViewportWidth(nextWidth);\n }\n });\n\n observer.observe(viewport);\n\n return (): void => observer.disconnect();\n }, []);\n\n useEffect((): void => {\n setNumPages(null);\n setPageNumber(1);\n setScale(1);\n }, [fileUrl]);\n\n const pageWidth = useMemo((): number => {\n const availableWidth = Math.max(\n MIN_PAGE_WIDTH,\n viewportWidth - PAGE_HORIZONTAL_PADDING,\n );\n const baseWidth = Math.min(MAX_PAGE_WIDTH, availableWidth);\n\n return Math.round(baseWidth * scale);\n }, [scale, viewportWidth]);\n\n const loadDocument = useCallback((document: PDFDocumentProxy): void => {\n setNumPages(document.numPages);\n setPageNumber(1);\n }, []);\n\n const hasPreviousPage = pageNumber > 1;\n const hasNextPage = numPages !== null && pageNumber < numPages;\n const canZoomOut = scale > MIN_SCALE;\n const canZoomIn = scale < MAX_SCALE;\n const pageLabel =\n numPages === null\n ? `第 ${pageNumber} 頁`\n : `第 ${pageNumber} / ${numPages} 頁`;\n const zoomLabel = `${Math.round(scale * 100)}%`;\n\n return (\n <section aria-label={`${filename} PDF 預覽`} className={styles.root}>\n <div className={styles.toolbar}>\n <div className={styles.pageControls} aria-label=\"頁面切換\">\n <Button\n aria-label=\"上一頁\"\n disabled={!hasPreviousPage}\n icon={ChevronLeftIcon}\n onClick={(): void => {\n setPageNumber((currentPageNumber) =>\n Math.max(1, currentPageNumber - 1),\n );\n }}\n size=\"minor\"\n variant=\"base-secondary\"\n />\n <Typography\n className={styles.counter}\n component=\"span\"\n variant=\"body\"\n >\n {pageLabel}\n </Typography>\n <Button\n aria-label=\"下一頁\"\n disabled={!hasNextPage}\n icon={ChevronRightIcon}\n onClick={(): void => {\n setPageNumber((currentPageNumber) =>\n numPages === null\n ? currentPageNumber\n : Math.min(numPages, currentPageNumber + 1),\n );\n }}\n size=\"minor\"\n variant=\"base-secondary\"\n />\n </div>\n <div className={styles.zoomControls} aria-label=\"縮放\">\n <Button\n aria-label=\"縮小\"\n disabled={!canZoomOut}\n icon={ZoomOutIcon}\n onClick={(): void => {\n setScale((currentScale) =>\n Math.max(MIN_SCALE, currentScale - SCALE_STEP),\n );\n }}\n size=\"minor\"\n variant=\"base-secondary\"\n />\n <Typography\n className={styles.counter}\n component=\"span\"\n variant=\"body\"\n >\n {zoomLabel}\n </Typography>\n <Button\n aria-label=\"放大\"\n disabled={!canZoomIn}\n icon={ZoomInIcon}\n onClick={(): void => {\n setScale((currentScale) =>\n Math.min(MAX_SCALE, currentScale + SCALE_STEP),\n );\n }}\n size=\"minor\"\n variant=\"base-secondary\"\n />\n </div>\n {onDownload ? (\n <Button\n icon={DownloadIcon}\n iconType=\"leading\"\n onClick={onDownload}\n size=\"minor\"\n variant=\"base-primary\"\n >\n 下載\n </Button>\n ) : null}\n </div>\n <div className={styles.viewport} ref={viewportRef}>\n <Document\n error={<PDFPreviewState message=\"PDF 無法載入。\" />}\n file={fileUrl}\n loading={<PDFPreviewState message=\"正在載入 PDF...\" />}\n noData={<PDFPreviewState message=\"沒有可預覽的 PDF。\" />}\n onLoadSuccess={loadDocument}\n >\n <Page\n className={styles.page}\n loading={<PDFPreviewState message=\"正在載入頁面...\" />}\n pageNumber={pageNumber}\n renderAnnotationLayer\n renderTextLayer\n width={pageWidth}\n />\n </Document>\n </div>\n </section>\n );\n}\n\nfunction PDFPreviewState({\n message,\n}: {\n readonly message: string;\n}): ReactElement {\n return (\n <div className={styles.state}>\n <Typography color=\"text-neutral\" variant=\"body\">\n {message}\n </Typography>\n </div>\n );\n}\n","'use client';\n\nimport {\n ChangeEvent,\n CSSProperties,\n Fragment,\n RefCallback,\n ReactElement,\n forwardRef,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n Background,\n Controls,\n Handle,\n Position,\n ReactFlow,\n type Edge as FlowEdge,\n type Node as FlowNode,\n type NodeProps,\n} from '@xyflow/react';\nimport * as dagre from 'dagre';\nimport {\n AutoComplete,\n Button,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Select,\n Stepper,\n Table,\n Textarea,\n Tooltip,\n Typography,\n type StepProps,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { stepClasses } from '@mezzanine-ui/core/stepper';\nimport {\n CheckedIcon,\n DangerousOutlineIcon,\n DownloadIcon,\n FileSearchIcon,\n RefreshCcwIcon,\n ShareIcon,\n UserIcon,\n} from '@mezzanine-ui/icons';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { FormFieldDefinition } from '@rytass/bpm-core-shared/form';\nimport {\n WorkflowDefinition,\n WorkflowNode,\n} from '@rytass/bpm-core-shared/workflow';\nimport {\n focusFormRendererField,\n validateFormRendererValues,\n} from '@rytass/bpm-core-client/form';\nimport {\n ActivityLogRecord,\n AttachmentRecord,\n ApprovalInstanceRecord,\n MemberProfileRecord,\n SignatureRecord,\n SignatureVerificationRecord,\n cancelApprovalInstance,\n decideTask,\n listAttachments,\n listTaskDecisions,\n readApprovalInstance,\n readAttachmentDownloadUrl,\n readAttachmentPreviewUrl,\n readInstanceSignatures,\n resubmitApprovalInstance,\n resolveMemberProfiles,\n searchMembers,\n TaskDecisionRecord,\n TaskRecord,\n WorkflowFormData,\n WorkflowTokenRecord,\n uploadAttachment,\n} from '@rytass/bpm-core-client/workflow';\nimport { BPMFormField } from '../../../components/bpm-form-field';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport { useAuth } from '../../../lib/auth-provider';\nimport { FormRenderer } from '../../forms/renderer/FormRendererView';\nimport { PDFPreview } from '../../../components/pdf-preview';\n\nconst SECTION_BODY_STYLE: CSSProperties = {\n display: 'grid',\n gap: 16,\n};\n\nconst BUTTON_ROW_STYLE: CSSProperties = {\n display: 'flex',\n flexWrap: 'wrap',\n gap: 8,\n};\n\nconst FLOW_NODE_LAYOUT_WIDTH = 184;\nconst FLOW_NODE_LAYOUT_HEIGHT = 96;\n\nconst FLOW_MODAL_BODY_STYLE: CSSProperties = {\n display: 'grid',\n gap: 16,\n};\n\nconst REJECT_REASON_FORM_STYLE: CSSProperties = {\n display: 'grid',\n gap: 12,\n width: '100%',\n};\n\nconst REJECT_REASON_TEXTAREA_STYLE: CSSProperties = {\n minWidth: '100%',\n width: '100%',\n};\n\nconst MODAL_FORM_STYLE: CSSProperties = {\n display: 'grid',\n gap: 12,\n width: '100%',\n};\n\nconst FLOW_CANVAS_STYLE: CSSProperties = {\n background: '#f8fafc',\n border: '1px solid #e2e8f0',\n borderRadius: 8,\n height: 'min(64vh, 620px)',\n minHeight: 440,\n overflow: 'hidden',\n width: 'min(80vw, 1040px)',\n};\n\nconst NODE_STYLE: CSSProperties = {\n background: '#ffffff',\n border: '1px solid #cbd5e1',\n borderRadius: 8,\n boxShadow: '0 8px 18px rgba(15, 23, 42, 0.08)',\n display: 'grid',\n gap: 6,\n minHeight: 82,\n padding: 12,\n width: 184,\n};\n\nconst NODE_STATUS_STYLE: CSSProperties = {\n borderRadius: 999,\n fontSize: 12,\n fontWeight: 600,\n justifySelf: 'start',\n lineHeight: '18px',\n padding: '0 8px',\n};\n\nconst NODE_SECONDARY_STYLE: CSSProperties = {\n color: '#64748b',\n fontSize: 12,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n};\n\nconst NODE_HANDLE_STYLE: CSSProperties = {\n opacity: 0,\n};\n\nconst EDGE_SUMMARY_STYLE: CSSProperties = {\n display: 'flex',\n flexWrap: 'wrap',\n gap: 8,\n};\n\nconst EDGE_SUMMARY_ITEM_STYLE: CSSProperties = {\n alignItems: 'center',\n background: '#ffffff',\n border: '1px solid #cbd5e1',\n borderRadius: 8,\n color: '#334155',\n display: 'inline-flex',\n fontSize: 12,\n fontWeight: 600,\n gap: 6,\n lineHeight: '20px',\n padding: '4px 8px',\n};\n\nconst HISTORY_MEMBER_NAME_STYLE: CSSProperties = {\n cursor: 'help',\n textDecoration: 'underline dotted',\n textUnderlineOffset: 3,\n};\n\nconst HISTORY_DANGER_TEXT_STYLE: CSSProperties = {\n color: 'var(--mzn-color-text-error)',\n};\n\nfunction applyFullWidthTextareaHost(element: HTMLDivElement | null): void {\n if (!element) {\n return;\n }\n\n element.style.width = '100%';\n}\n\nconst READONLY_FLOW_NODE_TYPES = {\n workflowRuntime: WorkflowRuntimeNodeCard,\n};\n\ntype RuntimeTone =\n | 'cancelled'\n | 'completed'\n | 'current'\n | 'neutral'\n | 'waiting';\n\ninterface RuntimeNodeData extends Record<string, unknown> {\n readonly kindLabel: string;\n readonly label: string;\n readonly secondaryLabel: string;\n readonly statusLabel: string;\n readonly tone: RuntimeTone;\n}\n\ntype RuntimeFlowNode = FlowNode<RuntimeNodeData, 'workflowRuntime'>;\ntype RuntimeFlowEdge = FlowEdge<\n Readonly<Record<string, unknown>>,\n 'smoothstep'\n>;\n\ntype TaskRow = Readonly<\n Record<string, unknown> &\n TaskRecord & {\n assigneeLabel: string;\n key: string;\n nodeLabel: string;\n statusLabel: string;\n }\n>;\n\ntype AttachmentRow = Readonly<\n Record<string, unknown> & {\n attachment: AttachmentRecord;\n createdAt: string;\n filename: string;\n id: string;\n key: string;\n mimeType: string;\n sizeLabel: string;\n }\n>;\n\ntype SignatureRow = Readonly<\n Record<string, unknown> & {\n algorithm: string;\n hashLabel: string;\n key: string;\n keyVersion: number;\n signedAtLabel: string;\n signerMemberId: string;\n }\n>;\n\ntype MemberOption = Readonly<{\n email: string | null;\n id: string;\n name: string;\n}>;\n\ninterface ActivityStepRecord {\n readonly descriptionParts: readonly ActivityStepDescriptionPart[];\n readonly error: boolean;\n readonly forcePending?: boolean;\n readonly id: string;\n readonly title: string;\n}\n\ntype ActivityStepDescriptionPart =\n | Readonly<{ text: string; type: 'text' }>\n | Readonly<{ text: string; type: 'dangerText' }>\n | Readonly<{\n email: string | null;\n label: string;\n memberId: string | null;\n prefix: string;\n type: 'member';\n }>;\n\nexport interface InstanceDetailViewProps {\n /** Approval instance id (Next.js `params.id` resolved by the page shim). */\n readonly instanceId: string;\n}\n\n/**\n * Framework-agnostic view for the BPM approval instance detail page.\n * Mechanical port of `apps/client/src/app/instances/[id]/page.tsx`. The\n * `instanceId` is provided by the host page wrapper (typically resolved\n * from `params.id` in the Next.js Server Component shim).\n */\nexport function InstanceDetailView({\n instanceId,\n}: InstanceDetailViewProps): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [activityLogs, setActivityLogs] = useState<\n readonly ActivityLogRecord[]\n >([]);\n const [instance, setInstance] = useState<ApprovalInstanceRecord | null>(null);\n const [taskDecisions, setTaskDecisions] = useState<\n readonly TaskDecisionRecord[]\n >([]);\n const [attachments, setAttachments] = useState<readonly AttachmentRecord[]>(\n [],\n );\n const [signatures, setSignatures] = useState<readonly SignatureRecord[]>([]);\n const [signatureVerification, setSignatureVerification] =\n useState<SignatureVerificationRecord | null>(null);\n const [tasks, setTasks] = useState<readonly TaskRecord[]>([]);\n const [workflowTokens, setWorkflowTokens] = useState<\n readonly WorkflowTokenRecord[]\n >([]);\n const [memberProfiles, setMemberProfiles] = useState<\n readonly MemberProfileRecord[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [deciding, setDeciding] = useState(false);\n const [workflowModalOpen, setWorkflowModalOpen] = useState(false);\n const [cancelComment, setCancelComment] = useState('');\n const [cancelModalOpen, setCancelModalOpen] = useState(false);\n const [rejectReason, setRejectReason] = useState('');\n const [rejectReasonError, setRejectReasonError] = useState<string | null>(\n null,\n );\n const [rejectReasonModalOpen, setRejectReasonModalOpen] = useState(false);\n const [returnComment, setReturnComment] = useState('');\n const [returnModalOpen, setReturnModalOpen] = useState(false);\n const [returnTargetNodeId, setReturnTargetNodeId] = useState<string | null>(\n null,\n );\n const [transferComment, setTransferComment] = useState('');\n const [transferMember, setTransferMember] = useState<MemberOption | null>(\n null,\n );\n const [transferMemberLoading, setTransferMemberLoading] = useState(false);\n const [transferMemberOptions, setTransferMemberOptions] = useState<\n readonly MemberOption[]\n >([]);\n const [transferModalOpen, setTransferModalOpen] = useState(false);\n const [resubmitFormErrors, setResubmitFormErrors] = useState<\n Readonly<Record<string, string>>\n >({});\n const [resubmitFormData, setResubmitFormData] = useState<WorkflowFormData>(\n {},\n );\n const [previewAttachment, setPreviewAttachment] =\n useState<AttachmentRecord | null>(null);\n const [previewUrl, setPreviewUrl] = useState<string | null>(null);\n const trimmedRejectReason = rejectReason.trim();\n const trimmedCancelComment = cancelComment.trim();\n const trimmedReturnComment = returnComment.trim();\n const trimmedTransferComment = transferComment.trim();\n\n useEffect((): void => {\n void refreshInstance();\n }, [currentMemberId, instanceId]);\n\n useEffect((): void => {\n setResubmitFormData(instance?.formData ?? {});\n }, [instance]);\n\n const currentTask = useMemo(\n (): TaskRecord | null =>\n tasks.find(\n (task) =>\n canMemberActOnTask(task, currentMemberId) &&\n (task.status === 'PENDING' || task.status === 'IN_PROGRESS'),\n ) ?? null,\n [currentMemberId, tasks],\n );\n const currentTaskNode = useMemo(\n (): WorkflowNode | null =>\n currentTask && instance\n ? (instance.workflowSnapshot.nodes.find(\n (node) => node.id === currentTask.nodeId,\n ) ?? null)\n : null,\n [currentTask, instance],\n );\n const returnTargetOptions = useMemo(\n (): readonly { readonly id: string; readonly name: string }[] =>\n currentTaskNode && instance\n ? readReturnTargetOptions(instance.workflowSnapshot, currentTaskNode)\n : [],\n [currentTaskNode, instance],\n );\n const canReturnCurrentTask =\n currentTaskNode?.type === 'userTask' &&\n currentTaskNode.data.returnBehavior.allowReturn;\n const selectedReturnTargetOption =\n returnTargetOptions.find((option) => option.id === returnTargetNodeId) ??\n returnTargetOptions[0] ??\n null;\n const canCancelInstance = Boolean(\n instance &&\n instance.initiatorMemberId === currentMemberId &&\n (instance.state === 'RUNNING' || instance.state === 'RETURNED'),\n );\n const canResubmitInstance = Boolean(\n instance &&\n instance.initiatorMemberId === currentMemberId &&\n instance.state === 'RETURNED',\n );\n const memberProfilesById = useMemo(\n (): ReadonlyMap<string, MemberProfileRecord> =>\n new Map(memberProfiles.map((profile) => [profile.memberId, profile])),\n [memberProfiles],\n );\n const taskRows = useMemo(\n (): TaskRow[] =>\n tasks.map((task) => ({\n ...task,\n assigneeLabel: readTaskAssigneeLabel(task, memberProfilesById),\n key: task.id,\n nodeLabel: readNodeDisplayLabel(\n task.nodeId,\n instance?.workflowSnapshot ?? null,\n ),\n statusLabel: readTaskStatusLabel(task.status),\n })),\n [instance, memberProfilesById, tasks],\n );\n const taskDecisionsByTaskId = useMemo(\n (): ReadonlyMap<string, TaskDecisionRecord> =>\n readLatestTaskDecisionsByTaskId(taskDecisions),\n [taskDecisions],\n );\n const signaturesById = useMemo(\n (): ReadonlyMap<string, SignatureRecord> =>\n new Map(signatures.map((signature) => [signature.id, signature])),\n [signatures],\n );\n const activitySteps = useMemo(\n (): ActivityStepRecord[] =>\n readActivityStepRecords(\n activityLogs,\n tasks,\n workflowTokens,\n instance?.workflowSnapshot ?? null,\n instance?.state ?? 'RUNNING',\n memberProfilesById,\n taskDecisionsByTaskId,\n signaturesById,\n signatureVerification,\n ),\n [\n activityLogs,\n instance,\n memberProfilesById,\n signatureVerification,\n signaturesById,\n taskDecisionsByTaskId,\n tasks,\n workflowTokens,\n ],\n );\n const currentActivityStep = useMemo(\n (): number => readCurrentActivityStep(activitySteps),\n [activitySteps],\n );\n const layoutedWorkflowSnapshot = useMemo(\n (): WorkflowDefinition | null =>\n instance\n ? layoutRuntimeWorkflowDefinition(instance.workflowSnapshot)\n : null,\n [instance],\n );\n const flowNodes = useMemo(\n (): RuntimeFlowNode[] =>\n instance && layoutedWorkflowSnapshot\n ? readRuntimeFlowNodes(\n layoutedWorkflowSnapshot,\n tasks,\n workflowTokens,\n instance.state,\n )\n : [],\n [instance, layoutedWorkflowSnapshot, tasks, workflowTokens],\n );\n const flowEdges = useMemo(\n (): RuntimeFlowEdge[] =>\n layoutedWorkflowSnapshot\n ? readRuntimeFlowEdges(layoutedWorkflowSnapshot)\n : [],\n [layoutedWorkflowSnapshot],\n );\n const edgeSummaries = useMemo(\n (): readonly string[] =>\n instance ? readEdgeSummaries(instance.workflowSnapshot) : [],\n [instance],\n );\n const taskColumns = useMemo(\n (): TableColumn<TaskRow>[] => [\n { dataIndex: 'nodeLabel', key: 'nodeLabel', title: '節點', width: 180 },\n {\n key: 'assigneeMemberId',\n title: '處理者',\n render: (record: TaskRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {record.assigneeLabel}\n </Typography>\n ),\n width: 180,\n },\n {\n dataIndex: 'statusLabel',\n key: 'statusLabel',\n title: '狀態',\n width: 120,\n },\n {\n key: 'createdAt',\n render: (record: TaskRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.createdAt)}\n </Typography>\n ),\n title: '建立時間',\n width: 220,\n },\n ],\n [],\n );\n const attachmentRows = useMemo(\n (): AttachmentRow[] =>\n attachments.map((attachment) => ({\n attachment,\n createdAt: attachment.createdAt,\n filename: attachment.filename,\n id: attachment.id,\n key: attachment.id,\n mimeType: attachment.mimeType,\n sizeLabel: formatFileSize(Number(attachment.sizeBytes)),\n })),\n [attachments],\n );\n const attachmentColumns = useMemo(\n (): TableColumn<AttachmentRow>[] => [\n { dataIndex: 'filename', key: 'filename', title: '檔名', width: 260 },\n { dataIndex: 'mimeType', key: 'mimeType', title: '類型', width: 180 },\n { dataIndex: 'sizeLabel', key: 'sizeLabel', title: '大小', width: 120 },\n {\n key: 'createdAt',\n render: (record: AttachmentRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.createdAt)}\n </Typography>\n ),\n title: '上傳時間',\n width: 220,\n },\n ],\n [],\n );\n const attachmentActions = useMemo(\n (): TableActions<AttachmentRow> => ({\n render: (record): ReturnType<TableActions<AttachmentRow>['render']> => [\n ...(record.mimeType === 'application/pdf'\n ? [\n {\n icon: FileSearchIcon,\n iconType: 'leading' as const,\n name: '預覽',\n onClick: (): void => {\n void handlePreviewAttachment(record.attachment);\n },\n },\n ]\n : []),\n {\n icon: DownloadIcon,\n iconType: 'leading',\n name: '下載',\n onClick: (): void => {\n void handleDownloadAttachment(record.attachment);\n },\n },\n ],\n variant: 'base-secondary',\n width: 160,\n }),\n [],\n );\n const signatureRows = useMemo(\n (): SignatureRow[] =>\n signatures.map((signature) => ({\n algorithm: signature.algorithm,\n hashLabel: readShortHash(signature.signedPayloadHash),\n key: signature.id,\n keyVersion: signature.keyVersion,\n signedAtLabel: formatDateTime(signature.signedAt),\n signerMemberId: signature.signerMemberId,\n })),\n [signatures],\n );\n const signatureColumns = useMemo(\n (): TableColumn<SignatureRow>[] => [\n {\n dataIndex: 'signerMemberId',\n key: 'signerMemberId',\n title: '簽章者',\n width: 160,\n },\n { dataIndex: 'algorithm', key: 'algorithm', title: '演算法', width: 150 },\n {\n dataIndex: 'keyVersion',\n key: 'keyVersion',\n title: 'Key 版本',\n width: 100,\n },\n {\n dataIndex: 'hashLabel',\n key: 'hashLabel',\n title: 'Payload Hash',\n width: 180,\n },\n {\n dataIndex: 'signedAtLabel',\n key: 'signedAtLabel',\n title: '簽章時間',\n width: 220,\n },\n ],\n [],\n );\n\n async function refreshInstance(): Promise<void> {\n setLoading(true);\n setError(null);\n\n try {\n const nextRecord = await readApprovalInstance(instanceId);\n setActivityLogs(nextRecord.activityLogs);\n setInstance(nextRecord.instance);\n setTasks(nextRecord.tasks);\n setWorkflowTokens(nextRecord.workflowTokens);\n const [\n nextTaskDecisions,\n nextMemberProfiles,\n nextAttachments,\n nextSignatures,\n ] = await Promise.all([\n readTaskDecisionsForTasks(nextRecord.tasks),\n readMemberProfilesForTimeline(nextRecord),\n listAttachments(nextRecord.instance.id),\n readInstanceSignatures(nextRecord.instance.id),\n ]);\n setTaskDecisions(nextTaskDecisions);\n setMemberProfiles(nextMemberProfiles);\n setAttachments(nextAttachments);\n setSignatures(nextSignatures.signatures);\n setSignatureVerification(nextSignatures.verification);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }\n\n async function handleUploadAttachment(\n field: FormFieldDefinition,\n file: File,\n ): Promise<{ readonly id: string }> {\n if (!currentMemberId) {\n throw new Error('尚未登入,無法上傳附件');\n }\n\n const attachment = await uploadAttachment({\n file,\n formFieldPath: `form.${field.fieldKey}`,\n });\n\n return { id: attachment.id };\n }\n\n async function handleDownloadAttachment(\n attachment: AttachmentRecord,\n ): Promise<void> {\n if (!currentMemberId) {\n return;\n }\n\n const url = await readAttachmentDownloadUrl({\n id: attachment.id,\n });\n\n window.open(url, '_blank', 'noopener,noreferrer');\n }\n\n async function handlePreviewAttachment(\n attachment: AttachmentRecord,\n ): Promise<void> {\n if (!currentMemberId) {\n return;\n }\n\n const url = await readAttachmentPreviewUrl({\n id: attachment.id,\n });\n\n setPreviewAttachment(attachment);\n setPreviewUrl(url);\n }\n\n async function handleDecision({\n action,\n comment,\n returnToNodeId = null,\n transferToMemberId = null,\n }: Readonly<{\n action: 'APPROVED' | 'REJECTED' | 'RETURNED' | 'TRANSFERRED';\n comment: string | null;\n returnToNodeId?: string | null;\n transferToMemberId?: string | null;\n }>): Promise<void> {\n if (!currentMemberId || !currentTask) {\n return;\n }\n\n setDeciding(true);\n setError(null);\n\n try {\n await decideTask({\n action,\n comment,\n decidedByMemberId: currentMemberId,\n returnToNodeId,\n taskId: currentTask.id,\n transferToMemberId,\n });\n setRejectReasonModalOpen(false);\n setReturnModalOpen(false);\n setTransferModalOpen(false);\n setRejectReason('');\n setReturnComment('');\n setTransferComment('');\n setTransferMember(null);\n setReturnTargetNodeId(null);\n setRejectReasonError(null);\n await refreshInstance();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setDeciding(false);\n }\n }\n\n function openRejectReasonModal(): void {\n setRejectReason('');\n setRejectReasonError(null);\n setRejectReasonModalOpen(true);\n }\n\n function closeRejectReasonModal(): void {\n if (deciding) {\n return;\n }\n\n setRejectReasonModalOpen(false);\n setRejectReason('');\n setRejectReasonError(null);\n }\n\n function openReturnModal(): void {\n setReturnComment('');\n setReturnTargetNodeId(returnTargetOptions[0]?.id ?? null);\n setReturnModalOpen(true);\n }\n\n function closeReturnModal(): void {\n if (deciding) {\n return;\n }\n\n setReturnModalOpen(false);\n setReturnComment('');\n setReturnTargetNodeId(null);\n }\n\n function openTransferModal(): void {\n setTransferComment('');\n setTransferMember(null);\n setTransferModalOpen(true);\n void handleSearchTransferMembers('');\n }\n\n function closeTransferModal(): void {\n if (deciding) {\n return;\n }\n\n setTransferModalOpen(false);\n setTransferComment('');\n setTransferMember(null);\n }\n\n async function handleSearchTransferMembers(\n searchText: string,\n ): Promise<void> {\n setTransferMemberLoading(true);\n\n try {\n setTransferMemberOptions(\n (await searchMembers(searchText))\n .filter((searchedMember) => searchedMember.memberId !== currentMemberId)\n .map(readMemberOption),\n );\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setTransferMemberLoading(false);\n }\n }\n\n async function handleRejectConfirm(): Promise<void> {\n if (!trimmedRejectReason) {\n setRejectReasonError('請輸入拒絕原因');\n return;\n }\n\n await handleDecision({\n action: 'REJECTED',\n comment: trimmedRejectReason,\n });\n }\n\n async function handleTransferConfirm(): Promise<void> {\n if (!transferMember) {\n setError('請選擇轉派對象');\n return;\n }\n\n await handleDecision({\n action: 'TRANSFERRED',\n comment: trimmedTransferComment || null,\n transferToMemberId: transferMember.id,\n });\n }\n\n async function handleReturnConfirm(): Promise<void> {\n await handleDecision({\n action: 'RETURNED',\n comment: trimmedReturnComment || null,\n returnToNodeId: selectedReturnTargetOption?.id ?? null,\n });\n }\n\n async function handleCancelInstance(): Promise<void> {\n if (!currentMemberId || !instance || !canCancelInstance) {\n return;\n }\n\n setDeciding(true);\n setError(null);\n setResubmitFormErrors({});\n\n if (\n instance.formDefinitionSnapshot.schema &&\n instance.formDefinitionSnapshot.uiSchema\n ) {\n const validation = validateFormRendererValues({\n schema: instance.formDefinitionSnapshot.schema,\n uiSchema: instance.formDefinitionSnapshot.uiSchema,\n values: resubmitFormData,\n });\n\n if (!validation.valid) {\n setResubmitFormErrors(validation.errors);\n setError('請先補齊必填欄位。');\n\n if (validation.firstInvalidFieldKey) {\n focusFormRendererField(validation.firstInvalidFieldKey);\n }\n\n setDeciding(false);\n\n return;\n }\n }\n\n try {\n await cancelApprovalInstance({\n cancelledByMemberId: currentMemberId,\n comment: trimmedCancelComment || null,\n instanceId: instance.id,\n });\n setCancelComment('');\n setCancelModalOpen(false);\n await refreshInstance();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setDeciding(false);\n }\n }\n\n async function handleResubmitInstance(): Promise<void> {\n if (!currentMemberId || !instance || !canResubmitInstance) {\n return;\n }\n\n setDeciding(true);\n setError(null);\n\n try {\n await resubmitApprovalInstance({\n formData: resubmitFormData,\n initiatorMemberId: currentMemberId,\n instanceId: instance.id,\n title: instance.title,\n });\n await refreshInstance();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setDeciding(false);\n }\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description={\n instance\n ? `${readInstanceStateLabel(instance.state)} · ${formatDateTime(\n instance.startedAt,\n )}`\n : '載入案件內容。'\n }\n title={instance?.title ?? '簽核案件'}\n >\n {instance ? (\n <Button\n aria-label=\"查看流程圖\"\n icon={ShareIcon}\n iconType=\"icon-only\"\n onClick={(): void => setWorkflowModalOpen(true)}\n title=\"查看流程圖\"\n variant=\"base-secondary\"\n >\n 流程圖\n </Button>\n ) : null}\n {canCancelInstance ? (\n <Button\n disabled={deciding}\n icon={DangerousOutlineIcon}\n iconType=\"leading\"\n onClick={(): void => setCancelModalOpen(true)}\n variant=\"destructive-secondary\"\n >\n 取消案件\n </Button>\n ) : null}\n {currentTask ? (\n <>\n {canReturnCurrentTask ? (\n <Button\n disabled={deciding}\n icon={RefreshCcwIcon}\n iconType=\"leading\"\n onClick={openReturnModal}\n variant=\"base-secondary\"\n >\n 退回\n </Button>\n ) : null}\n <Button\n disabled={deciding}\n icon={UserIcon}\n iconType=\"leading\"\n onClick={openTransferModal}\n variant=\"base-secondary\"\n >\n 轉派\n </Button>\n <Button\n disabled={deciding}\n icon={DangerousOutlineIcon}\n iconType=\"leading\"\n onClick={openRejectReasonModal}\n variant=\"destructive-secondary\"\n >\n 拒絕\n </Button>\n <Button\n disabled={deciding}\n icon={CheckedIcon}\n iconType=\"leading\"\n onClick={(): void =>\n void handleDecision({ action: 'APPROVED', comment: null })\n }\n variant=\"base-primary\"\n >\n 同意\n </Button>\n </>\n ) : null}\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section>\n <div style={SECTION_BODY_STYLE}>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n {loading ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n 載入中...\n </Typography>\n ) : null}\n {instance?.formDefinitionSnapshot.schema &&\n instance.formDefinitionSnapshot.uiSchema ? (\n <>\n <FormRenderer\n errors={resubmitFormErrors}\n onChange={(values): void => {\n setResubmitFormData(values);\n setResubmitFormErrors({});\n }}\n onUploadAttachment={\n canResubmitInstance ? handleUploadAttachment : undefined\n }\n readonly={!canResubmitInstance}\n schema={instance.formDefinitionSnapshot.schema}\n uiSchema={instance.formDefinitionSnapshot.uiSchema}\n value={\n canResubmitInstance ? resubmitFormData : instance.formData\n }\n />\n {canResubmitInstance ? (\n <div style={BUTTON_ROW_STYLE}>\n <Button\n disabled={deciding}\n icon={RefreshCcwIcon}\n iconType=\"leading\"\n onClick={(): void => void handleResubmitInstance()}\n variant=\"base-primary\"\n >\n 重新送出\n </Button>\n </div>\n ) : null}\n </>\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 此案件沒有可顯示的表單快照。\n </Typography>\n )}\n </div>\n </Section>\n\n <Section>\n <div style={SECTION_BODY_STYLE}>\n <Typography component=\"h2\" variant=\"h3\">\n 附件\n </Typography>\n {attachmentRows.length > 0 ? (\n <Table\n actions={attachmentActions}\n columns={attachmentColumns}\n dataSource={attachmentRows}\n fullWidth\n />\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 此案件沒有附件。\n </Typography>\n )}\n </div>\n </Section>\n\n <Section>\n <Typography component=\"h2\" variant=\"h3\">\n 任務\n </Typography>\n <Table columns={taskColumns} dataSource={taskRows} fullWidth />\n </Section>\n\n <Section>\n <div style={SECTION_BODY_STYLE}>\n <Typography component=\"h2\" variant=\"h3\">\n 簽章\n </Typography>\n <Typography\n color={\n signatureVerification?.valid ? 'text-success' : 'text-error'\n }\n variant=\"body\"\n >\n {signatureVerification\n ? signatureVerification.valid\n ? `簽章鏈已驗證,共 ${signatureVerification.checkedCount} 筆。`\n : `簽章鏈驗證失敗:${signatureVerification.errors.join('、')}`\n : '尚無簽章紀錄。'}\n </Typography>\n {signatureRows.length > 0 ? (\n <Table\n columns={signatureColumns}\n dataSource={signatureRows}\n fullWidth\n />\n ) : null}\n </div>\n </Section>\n\n <Section>\n <div style={SECTION_BODY_STYLE}>\n <Typography component=\"h2\" variant=\"h3\">\n 歷程\n </Typography>\n {activitySteps.length > 0 ? (\n <Stepper\n currentStep={currentActivityStep}\n orientation=\"vertical\"\n type=\"dot\"\n >\n {activitySteps.map((activityStep) => (\n <ActivityHistoryStep\n descriptionParts={activityStep.descriptionParts}\n error={activityStep.error}\n forcePending={activityStep.forcePending}\n key={activityStep.id}\n title={activityStep.title}\n />\n ))}\n </Stepper>\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 尚無歷程紀錄。\n </Typography>\n )}\n </div>\n </Section>\n </SectionGroup>\n\n {instance ? (\n <Modal\n modalType=\"standard\"\n onClose={(): void => setWorkflowModalOpen(false)}\n open={workflowModalOpen}\n showModalHeader\n size=\"wide\"\n supportingText={`${readInstanceStateLabel(\n instance.state,\n )} · ${formatDateTime(instance.startedAt)}`}\n title=\"流程圖\"\n >\n <div style={FLOW_MODAL_BODY_STYLE}>\n <div style={FLOW_CANVAS_STYLE}>\n <ReactFlow\n edges={flowEdges}\n fitView\n fitViewOptions={{ padding: 0.18 }}\n maxZoom={1.2}\n minZoom={0.2}\n nodes={flowNodes}\n nodesDraggable={false}\n nodesFocusable={false}\n nodeTypes={READONLY_FLOW_NODE_TYPES}\n panOnDrag\n proOptions={{ hideAttribution: true }}\n >\n <Background />\n <Controls showInteractive={false} />\n </ReactFlow>\n </div>\n {edgeSummaries.length > 0 ? (\n <div style={EDGE_SUMMARY_STYLE}>\n {edgeSummaries.map((summary) => (\n <span key={summary} style={EDGE_SUMMARY_ITEM_STYLE}>\n {summary}\n </span>\n ))}\n </div>\n ) : null}\n </div>\n </Modal>\n ) : null}\n <Modal\n modalType=\"standard\"\n onClose={(): void => {\n setPreviewAttachment(null);\n setPreviewUrl(null);\n }}\n open={Boolean(previewAttachment && previewUrl)}\n showModalHeader\n size=\"wide\"\n supportingText={previewAttachment?.filename ?? undefined}\n title=\"PDF 預覽\"\n >\n {previewUrl ? (\n <PDFPreview\n filename={previewAttachment?.filename ?? 'PDF 預覽'}\n fileUrl={previewUrl}\n onDownload={\n previewAttachment\n ? (): void => void handleDownloadAttachment(previewAttachment)\n : undefined\n }\n />\n ) : null}\n </Modal>\n <Modal\n cancelText=\"保留案件\"\n confirmButtonProps={{ variant: 'destructive-primary' }}\n confirmText=\"確認取消\"\n loading={deciding}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={(): void => setCancelModalOpen(false)}\n onClose={(): void => setCancelModalOpen(false)}\n onConfirm={(): void => void handleCancelInstance()}\n open={cancelModalOpen}\n showModalFooter\n showModalHeader\n supportingText=\"取消後會關閉目前待簽任務與候選簽核人。\"\n title=\"取消案件\"\n >\n <div style={SECTION_BODY_STYLE}>\n <Typography variant=\"body\">\n 確定要取消「{instance?.title ?? ''}」嗎?\n </Typography>\n <BPMFormField label=\"取消原因\" name=\"cancelComment\">\n <Textarea\n onChange={(event): void =>\n setCancelComment(event.target.value)\n }\n placeholder=\"可填寫取消原因\"\n resize=\"vertical\"\n rows={3}\n value={cancelComment}\n />\n </BPMFormField>\n </div>\n </Modal>\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{\n disabled: !trimmedRejectReason,\n variant: 'destructive-primary',\n }}\n confirmText=\"送出拒絕\"\n loading={deciding}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={closeRejectReasonModal}\n onClose={closeRejectReasonModal}\n onConfirm={(): void => void handleRejectConfirm()}\n open={rejectReasonModalOpen}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"拒絕案件時必須留下原因,供發起人與後續追蹤查看。\"\n title=\"拒絕原因\"\n >\n <div style={REJECT_REASON_FORM_STYLE}>\n <BPMFormField label=\"拒絕原因\" name=\"rejectReason\" required>\n <Textarea\n autoFocus\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setRejectReason(event.target.value);\n setRejectReasonError(null);\n }}\n placeholder=\"請說明拒絕原因\"\n ref={applyFullWidthTextareaHost}\n resize=\"vertical\"\n rows={4}\n style={REJECT_REASON_TEXTAREA_STYLE}\n type={rejectReasonError ? 'error' : 'default'}\n value={rejectReason}\n />\n </BPMFormField>\n {rejectReasonError ? (\n <Typography color=\"text-error\" variant=\"body\">\n {rejectReasonError}\n </Typography>\n ) : null}\n </div>\n </Modal>\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{\n disabled: !transferMember,\n }}\n confirmText=\"送出轉派\"\n loading={deciding}\n modalType=\"standard\"\n onCancel={closeTransferModal}\n onClose={closeTransferModal}\n onConfirm={(): void => void handleTransferConfirm()}\n open={transferModalOpen}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"轉派後,原任務會保留轉派紀錄,新的待簽任務會指派給指定成員。\"\n title=\"轉派簽核\"\n >\n <div style={MODAL_FORM_STYLE}>\n <BPMFormField label=\"轉派對象\" name=\"transferToMemberId\" required>\n <AutoComplete\n asyncData\n disabledOptionsFilter\n emptyText=\"沒有符合的成員\"\n inputProps={{\n autoCapitalize: 'none',\n autoCorrect: 'off',\n name: 'transfer-member-search',\n spellCheck: false,\n }}\n loading={transferMemberLoading}\n loadingText=\"搜尋成員中...\"\n mode=\"single\"\n onChange={(option): void =>\n setTransferMember(readMemberOptionFromValue(option))\n }\n onSearch={handleSearchTransferMembers}\n onSearchTextChange={(searchText): void =>\n setTransferMember(\n readUniqueMemberOption(searchText, transferMemberOptions),\n )\n }\n onVisibilityChange={(open): void => {\n if (open) {\n void handleSearchTransferMembers('');\n }\n }}\n options={[...transferMemberOptions]}\n placeholder=\"搜尋姓名或信箱\"\n searchDebounceTime={300}\n value={transferMember}\n />\n </BPMFormField>\n <BPMFormField label=\"轉派說明\" name=\"transferComment\">\n <Textarea\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void =>\n setTransferComment(event.target.value)\n }\n placeholder=\"可補充轉派原因\"\n ref={applyFullWidthTextareaHost}\n resize=\"vertical\"\n rows={4}\n style={REJECT_REASON_TEXTAREA_STYLE}\n value={transferComment}\n />\n </BPMFormField>\n </div>\n </Modal>\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{\n disabled: !selectedReturnTargetOption,\n }}\n confirmText=\"送出退回\"\n loading={deciding}\n modalType=\"standard\"\n onCancel={closeReturnModal}\n onClose={closeReturnModal}\n onConfirm={(): void => void handleReturnConfirm()}\n open={returnModalOpen}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"退回後,流程會回到指定節點並等待重新處理。\"\n title=\"退回簽核\"\n >\n <div style={MODAL_FORM_STYLE}>\n <BPMFormField label=\"退回節點\" name=\"returnTargetNodeId\" required>\n <Select\n clearable={false}\n fullWidth\n onChange={(option): void =>\n setReturnTargetNodeId(option?.id ?? null)\n }\n options={[...returnTargetOptions]}\n placeholder=\"選擇退回節點\"\n value={selectedReturnTargetOption}\n />\n </BPMFormField>\n <BPMFormField label=\"退回說明\" name=\"returnComment\">\n <Textarea\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void =>\n setReturnComment(event.target.value)\n }\n placeholder=\"可補充需要修改的內容\"\n ref={applyFullWidthTextareaHost}\n resize=\"vertical\"\n rows={4}\n style={REJECT_REASON_TEXTAREA_STYLE}\n value={returnComment}\n />\n </BPMFormField>\n </div>\n </Modal>\n </>\n );\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n\nfunction formatFileSize(sizeBytes: number): string {\n if (!Number.isFinite(sizeBytes) || sizeBytes <= 0) {\n return '-';\n }\n\n if (sizeBytes < 1024) {\n return `${sizeBytes} B`;\n }\n\n if (sizeBytes < 1024 * 1024) {\n return `${(sizeBytes / 1024).toFixed(1)} KB`;\n }\n\n return `${(sizeBytes / 1024 / 1024).toFixed(1)} MB`;\n}\n\nfunction readShortHash(hash: string): string {\n return hash.length > 16 ? `${hash.slice(0, 12)}...` : hash;\n}\n\nfunction joinClassNames(\n ...classNames: readonly (string | null | undefined)[]\n): string {\n return classNames\n .filter((className): className is string =>\n isPresentText(className ?? null),\n )\n .join(' ');\n}\n\ninterface ActivityHistoryStepProps extends StepProps {\n readonly descriptionParts: readonly ActivityStepDescriptionPart[];\n readonly forcePending?: boolean;\n}\n\nconst ActivityHistoryStep = forwardRef<\n HTMLDivElement,\n ActivityHistoryStepProps\n>(function ActivityHistoryStep(\n {\n className,\n descriptionParts,\n error,\n forcePending = false,\n index = 0,\n orientation,\n status = 'pending',\n title,\n type = 'number',\n ...rest\n },\n ref,\n): ReactElement {\n const displayStatus = forcePending ? 'pending' : status;\n\n return (\n <div\n {...rest}\n className={joinClassNames(\n stepClasses.host,\n type === 'dot' ? stepClasses.dot : null,\n error && displayStatus !== 'processing' ? stepClasses.error : null,\n orientation === 'horizontal' ? stepClasses.horizontal : null,\n type === 'number' ? stepClasses.number : null,\n displayStatus === 'pending' ? stepClasses.pending : null,\n displayStatus === 'processing' ? stepClasses.processing : null,\n error && displayStatus === 'processing'\n ? stepClasses.processingError\n : null,\n !error && displayStatus === 'succeeded' ? stepClasses.succeeded : null,\n orientation === 'vertical' ? stepClasses.vertical : null,\n className,\n )}\n ref={ref}\n >\n {type === 'dot' ? (\n <span\n className={joinClassNames(\n stepClasses.statusIndicator,\n stepClasses.statusIndicatorDot,\n )}\n />\n ) : (\n <span className={stepClasses.statusIndicator}>{index + 1}</span>\n )}\n <div className={stepClasses.textContainer}>\n <Typography\n className={stepClasses.title}\n variant=\"label-primary-highlight\"\n >\n {title}\n <span className={stepClasses.titleConnectLine} />\n </Typography>\n {descriptionParts.length > 0 ? (\n <Typography className={stepClasses.description} variant=\"caption\">\n {descriptionParts.map((part, partIndex) => (\n <Fragment key={`${part.type}-${partIndex}`}>\n {partIndex > 0 ? ' · ' : null}\n {renderActivityDescriptionPart(part)}\n </Fragment>\n ))}\n </Typography>\n ) : null}\n </div>\n </div>\n );\n});\n\nfunction renderActivityDescriptionPart(\n part: ActivityStepDescriptionPart,\n): ReactElement | string {\n if (part.type === 'text') {\n return part.text;\n }\n\n if (part.type === 'dangerText') {\n return <span style={HISTORY_DANGER_TEXT_STYLE}>{part.text}</span>;\n }\n\n if (!part.email) {\n return `${part.prefix}:${part.label}`;\n }\n\n return (\n <>\n {part.prefix}:\n <Tooltip title={part.email}>\n {({ onMouseEnter, onMouseLeave, ref }): ReactElement => (\n <span\n data-testid={\n part.memberId ? `member-tooltip-${part.memberId}` : undefined\n }\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n ref={ref as RefCallback<HTMLSpanElement>}\n style={HISTORY_MEMBER_NAME_STYLE}\n >\n {part.label}\n </span>\n )}\n </Tooltip>\n </>\n );\n}\n\nasync function readMemberProfilesForTimeline({\n activityLogs,\n tasks,\n}: {\n readonly activityLogs: readonly ActivityLogRecord[];\n readonly tasks: readonly TaskRecord[];\n}): Promise<readonly MemberProfileRecord[]> {\n const memberIds = [\n ...new Set(\n [\n ...activityLogs.map((activityLog) => activityLog.actorMemberId),\n ...tasks.map((task) => task.assigneeMemberId),\n ...tasks.map((task) => task.originalAssigneeMemberId),\n ...tasks.flatMap((task) => task.candidateMemberIds),\n ...tasks.flatMap((task) =>\n readDelegationChain(task.delegationChainJson).flatMap((step) => [\n step.from,\n step.to,\n ]),\n ),\n ].filter(isPresentText),\n ),\n ];\n\n try {\n return await resolveMemberProfiles(memberIds);\n } catch {\n return [];\n }\n}\n\nasync function readTaskDecisionsForTasks(\n tasks: readonly TaskRecord[],\n): Promise<readonly TaskDecisionRecord[]> {\n const decisionLists = await Promise.all(\n tasks.map((task) => listTaskDecisions(task.id)),\n );\n\n return decisionLists.flat();\n}\n\nfunction readLatestTaskDecisionsByTaskId(\n taskDecisions: readonly TaskDecisionRecord[],\n): ReadonlyMap<string, TaskDecisionRecord> {\n return taskDecisions.reduce<ReadonlyMap<string, TaskDecisionRecord>>(\n (decisionsByTaskId, decision) => {\n const currentDecision = decisionsByTaskId.get(decision.taskId);\n const nextDecision =\n !currentDecision ||\n new Date(decision.decidedAt).getTime() >\n new Date(currentDecision.decidedAt).getTime()\n ? decision\n : currentDecision;\n\n return new Map(decisionsByTaskId).set(decision.taskId, nextDecision);\n },\n new Map(),\n );\n}\n\nfunction readActivityStepRecords(\n activityLogs: readonly ActivityLogRecord[],\n tasks: readonly TaskRecord[],\n tokens: readonly WorkflowTokenRecord[],\n workflow: WorkflowDefinition | null,\n instanceState: ApprovalInstanceRecord['state'],\n memberProfilesById: ReadonlyMap<string, MemberProfileRecord>,\n taskDecisionsByTaskId: ReadonlyMap<string, TaskDecisionRecord>,\n signaturesById: ReadonlyMap<string, SignatureRecord>,\n signatureVerification: SignatureVerificationRecord | null,\n): ActivityStepRecord[] {\n const historySteps = activityLogs\n .filter(isUserMeaningfulActivity)\n .map((activityLog): ActivityStepRecord => {\n const payload = readActivityPayload(activityLog);\n const nodeLabel = readActivityNodeLabel(activityLog.nodeId, workflow);\n const descriptionParts = [\n readTextDescriptionPart(\n nodeLabel ? `節點:${nodeLabel}` : '節點:全流程',\n ),\n readMemberDescriptionPart(\n '操作者',\n activityLog.actorMemberId,\n memberProfilesById,\n '系統',\n ),\n readTextDescriptionPart(\n `時間:${formatActivityDateTime(activityLog.createdAt)}`,\n ),\n ...readActivityDetailParts(\n activityLog,\n payload,\n workflow,\n taskDecisionsByTaskId,\n signaturesById,\n signatureVerification,\n memberProfilesById,\n ),\n ].filter(isActivityDescriptionPart);\n\n return {\n descriptionParts,\n error: isActivityError(activityLog, payload),\n id: activityLog.id,\n title: readActivityEventLabel(activityLog.eventType, payload),\n };\n });\n const pendingTaskSteps = tasks.filter(isPendingTask).map(\n (task): ActivityStepRecord => ({\n descriptionParts: [\n readTextDescriptionPart(\n `節點:${readNodeDisplayLabel(task.nodeId, workflow)}`,\n ),\n readMemberDescriptionPart(\n '處理者',\n task.assigneeMemberId,\n memberProfilesById,\n '未指定',\n ),\n readTextDescriptionPart(\n `建立時間:${formatActivityDateTime(task.createdAt)}`,\n ),\n ].filter(isActivityDescriptionPart),\n error: false,\n id: `pending-task-${task.id}`,\n title: task.status === 'IN_PROGRESS' ? '簽核處理中' : '等待簽核處理',\n }),\n );\n const representedNodeIds = new Set(\n [\n ...activityLogs\n .filter(isUserMeaningfulActivity)\n .map((activityLog) => activityLog.nodeId),\n ...tasks.map((task) => task.nodeId),\n ].filter(isPresentText),\n );\n const futureNodeSteps = workflow\n ? readFutureTimelineNodes(\n workflow,\n tasks,\n tokens,\n instanceState,\n representedNodeIds,\n ).map(\n (node): ActivityStepRecord => ({\n descriptionParts: [\n readTextDescriptionPart(\n `${readNodeKindLabel(node.type)} · 尚未抵達`,\n ),\n ].filter(isActivityDescriptionPart),\n error: false,\n forcePending: true,\n id: `future-node-${node.id}`,\n title: readFutureNodeStepTitle(node),\n }),\n )\n : [];\n\n return [...historySteps, ...pendingTaskSteps, ...futureNodeSteps];\n}\n\nfunction readTextDescriptionPart(\n text: string | null,\n): ActivityStepDescriptionPart | null {\n return isPresentText(text) ? { text, type: 'text' } : null;\n}\n\nfunction readDangerTextDescriptionPart(\n text: string | null,\n): ActivityStepDescriptionPart | null {\n return isPresentText(text) ? { text, type: 'dangerText' } : null;\n}\n\nfunction readMemberDescriptionPart(\n prefix: string,\n memberId: string | null,\n memberProfilesById: ReadonlyMap<string, MemberProfileRecord>,\n fallbackLabel: string,\n): ActivityStepDescriptionPart {\n const profile = memberId ? memberProfilesById.get(memberId) : null;\n\n return {\n email: profile?.email ?? null,\n label: profile?.name ?? fallbackLabel,\n memberId,\n prefix,\n type: 'member',\n };\n}\n\nfunction readMemberDisplayText(\n memberId: string | null,\n memberProfilesById: ReadonlyMap<string, MemberProfileRecord>,\n): string {\n if (!memberId) {\n return '-';\n }\n\n const profile = memberProfilesById.get(memberId);\n\n return profile ? `${profile.name}(${profile.email})` : memberId;\n}\n\nfunction isActivityDescriptionPart(\n part: ActivityStepDescriptionPart | null,\n): part is ActivityStepDescriptionPart {\n return Boolean(part);\n}\n\nfunction readCurrentActivityStep(\n activitySteps: readonly ActivityStepRecord[],\n): number {\n const firstPendingStepIndex = activitySteps.findIndex(\n (activityStep) =>\n activityStep.id.startsWith('pending-task-') ||\n activityStep.id.startsWith('future-node-'),\n );\n\n return firstPendingStepIndex === -1\n ? activitySteps.length\n : firstPendingStepIndex;\n}\n\nfunction isUserMeaningfulActivity(activityLog: ActivityLogRecord): boolean {\n return (\n activityLog.eventType === 'INSTANCE_STARTED' ||\n activityLog.eventType === 'TASK_DECIDED' ||\n activityLog.eventType === 'SLA_TRIGGERED'\n );\n}\n\nfunction isFutureTimelineNode(\n node: WorkflowNode,\n tasks: readonly TaskRecord[],\n tokens: readonly WorkflowTokenRecord[],\n instanceState: ApprovalInstanceRecord['state'],\n representedNodeIds: ReadonlySet<string>,\n): boolean {\n if (node.type === 'startEvent' || representedNodeIds.has(node.id)) {\n return false;\n }\n\n if (instanceState === 'REJECTED') {\n return true;\n }\n\n const state = readNodeRuntimeState(node, tasks, tokens, instanceState);\n\n return state.tone === 'neutral' || state.tone === 'waiting';\n}\n\nfunction readFutureTimelineNodes(\n workflow: WorkflowDefinition,\n tasks: readonly TaskRecord[],\n tokens: readonly WorkflowTokenRecord[],\n instanceState: ApprovalInstanceRecord['state'],\n representedNodeIds: ReadonlySet<string>,\n): readonly WorkflowNode[] {\n if (instanceState !== 'RUNNING' && instanceState !== 'REJECTED') {\n return [];\n }\n\n const futureNodes = workflow.nodes.filter((node) =>\n isFutureTimelineNode(\n node,\n tasks,\n tokens,\n instanceState,\n representedNodeIds,\n ),\n );\n const reachableDistances = readReachableFutureNodeDistances(\n workflow,\n futureNodes,\n tasks,\n tokens,\n representedNodeIds,\n );\n const originalNodeIndexes = new Map(\n workflow.nodes.map((node, index) => [node.id, index]),\n );\n\n return futureNodes\n .filter((node) => reachableDistances.has(node.id))\n .sort((left, right) => {\n const leftDistance = reachableDistances.get(left.id) ?? 0;\n const rightDistance = reachableDistances.get(right.id) ?? 0;\n\n if (leftDistance !== rightDistance) {\n return leftDistance - rightDistance;\n }\n\n if (left.position.x !== right.position.x) {\n return left.position.x - right.position.x;\n }\n\n if (left.position.y !== right.position.y) {\n return left.position.y - right.position.y;\n }\n\n return (\n (originalNodeIndexes.get(left.id) ?? 0) -\n (originalNodeIndexes.get(right.id) ?? 0)\n );\n });\n}\n\nfunction readReachableFutureNodeDistances(\n workflow: WorkflowDefinition,\n futureNodes: readonly WorkflowNode[],\n tasks: readonly TaskRecord[],\n tokens: readonly WorkflowTokenRecord[],\n representedNodeIds: ReadonlySet<string>,\n): ReadonlyMap<string, number> {\n const futureNodeIds = new Set(futureNodes.map((node) => node.id));\n const outgoingNodeIds = workflow.edges.reduce<\n ReadonlyMap<string, readonly string[]>\n >((groups, edge) => {\n const nextTargets = [...(groups.get(edge.source) ?? []), edge.target];\n\n return new Map(groups).set(edge.source, nextTargets);\n }, new Map());\n const frontierNodeIds = readFutureTimelineFrontierNodeIds(\n workflow,\n tasks,\n tokens,\n representedNodeIds,\n );\n\n return frontierNodeIds.reduce<ReadonlyMap<string, number>>(\n (distances, nodeId) =>\n mergeFutureNodeDistances(\n distances,\n readFutureNodeDistancesFrom(nodeId, outgoingNodeIds, futureNodeIds),\n ),\n new Map(),\n );\n}\n\nfunction readFutureTimelineFrontierNodeIds(\n workflow: WorkflowDefinition,\n tasks: readonly TaskRecord[],\n tokens: readonly WorkflowTokenRecord[],\n representedNodeIds: ReadonlySet<string>,\n): readonly string[] {\n const tokenNodeIds = tokens\n .filter((token) => token.status === 'ACTIVE' || token.status === 'WAITING')\n .map((token) => token.currentNodeId);\n const pendingTaskNodeIds = tasks\n .filter(isPendingTask)\n .map((task) => task.nodeId);\n const representedFrontierNodeIds = workflow.nodes\n .filter((node) => representedNodeIds.has(node.id))\n .map((node) => node.id);\n const activeFrontierNodeIds = [\n ...new Set([\n ...tokenNodeIds,\n ...pendingTaskNodeIds,\n ...representedFrontierNodeIds,\n ]),\n ];\n const startNodeIds = workflow.nodes\n .filter((node) => node.type === 'startEvent')\n .map((node) => node.id);\n\n return activeFrontierNodeIds.length > 0\n ? activeFrontierNodeIds\n : startNodeIds;\n}\n\nfunction readFutureNodeDistancesFrom(\n startNodeId: string,\n outgoingNodeIds: ReadonlyMap<string, readonly string[]>,\n futureNodeIds: ReadonlySet<string>,\n): ReadonlyMap<string, number> {\n const initialQueue: readonly {\n readonly distance: number;\n readonly nodeId: string;\n }[] = [{ distance: 0, nodeId: startNodeId }];\n\n return walkFutureNodeDistances(initialQueue, outgoingNodeIds, futureNodeIds);\n}\n\nfunction walkFutureNodeDistances(\n queue: readonly { readonly distance: number; readonly nodeId: string }[],\n outgoingNodeIds: ReadonlyMap<string, readonly string[]>,\n futureNodeIds: ReadonlySet<string>,\n visitedNodeIds: ReadonlySet<string> = new Set(),\n distances: ReadonlyMap<string, number> = new Map(),\n): ReadonlyMap<string, number> {\n const [current, ...restQueue] = queue;\n\n if (!current) {\n return distances;\n }\n\n if (visitedNodeIds.has(current.nodeId)) {\n return walkFutureNodeDistances(\n restQueue,\n outgoingNodeIds,\n futureNodeIds,\n visitedNodeIds,\n distances,\n );\n }\n\n const nextVisitedNodeIds = new Set(visitedNodeIds).add(current.nodeId);\n const nextDistances = futureNodeIds.has(current.nodeId)\n ? new Map(distances).set(\n current.nodeId,\n Math.min(\n distances.get(current.nodeId) ?? current.distance,\n current.distance,\n ),\n )\n : distances;\n const nextQueue = [\n ...restQueue,\n ...(outgoingNodeIds.get(current.nodeId) ?? []).map((nodeId) => ({\n distance: current.distance + 1,\n nodeId,\n })),\n ];\n\n return walkFutureNodeDistances(\n nextQueue,\n outgoingNodeIds,\n futureNodeIds,\n nextVisitedNodeIds,\n nextDistances,\n );\n}\n\nfunction mergeFutureNodeDistances(\n currentDistances: ReadonlyMap<string, number>,\n nextDistances: ReadonlyMap<string, number>,\n): ReadonlyMap<string, number> {\n return [...nextDistances.entries()].reduce<ReadonlyMap<string, number>>(\n (mergedDistances, [nodeId, distance]) =>\n new Map(mergedDistances).set(\n nodeId,\n Math.min(mergedDistances.get(nodeId) ?? distance, distance),\n ),\n currentDistances,\n );\n}\n\nfunction readFutureNodeStepTitle(node: WorkflowNode): string {\n if (node.type === 'userTask') {\n return `未來簽核:${node.data.label}`;\n }\n\n if (node.type === 'serviceTask') {\n return `未來知會:${node.data.label}`;\n }\n\n if (node.type === 'exclusiveGateway') {\n return `未來分流:${node.data.label}`;\n }\n\n if (node.type === 'parallelGateway') {\n return `未來匯合:${node.data.label}`;\n }\n\n if (node.type === 'endEvent') {\n return `流程完成:${node.data.label}`;\n }\n\n return `未來節點:${node.data.label}`;\n}\n\nfunction readActivityPayload(\n activityLog: ActivityLogRecord,\n): Readonly<Record<string, unknown>> {\n try {\n const payload = JSON.parse(activityLog.payloadJson) as unknown;\n\n return isRecord(payload) ? payload : {};\n } catch {\n return {};\n }\n}\n\nfunction readActivityEventLabel(\n eventType: string,\n payload: Readonly<Record<string, unknown>>,\n): string {\n if (eventType === 'INSTANCE_STARTED') {\n return '案件已發起';\n }\n\n if (eventType === 'TOKEN_CREATED') {\n return '流程路徑已建立';\n }\n\n if (eventType === 'ENGINE_PROCESS_REQUESTED') {\n return '流程引擎已處理';\n }\n\n if (eventType === 'TOKEN_ADVANCED') {\n return '流程已前進';\n }\n\n if (eventType === 'TASK_CREATED') {\n return '待簽任務已建立';\n }\n\n if (eventType === 'TASK_DECIDED') {\n return readTaskDecisionEventLabel(readStringField(payload, 'action'));\n }\n\n if (eventType === 'SLA_TRIGGERED') {\n return '時限提醒已觸發';\n }\n\n return eventType;\n}\n\nfunction readTaskDecisionEventLabel(action: string | null): string {\n if (action === 'APPROVED') {\n return '已同意';\n }\n\n if (action === 'REJECTED') {\n return '已拒絕';\n }\n\n if (action === 'RETURNED') {\n return '已退回';\n }\n\n if (action === 'TRANSFERRED') {\n return '已轉派';\n }\n\n return '簽核已決議';\n}\n\nfunction readActivityDetail(\n activityLog: ActivityLogRecord,\n payload: Readonly<Record<string, unknown>>,\n workflow: WorkflowDefinition | null,\n memberProfilesById: ReadonlyMap<string, MemberProfileRecord>,\n): string | null {\n if (activityLog.eventType === 'TASK_CREATED') {\n const assigneeMemberId = readStringField(payload, 'assigneeMemberId');\n const originalAssigneeMemberId = readStringField(\n payload,\n 'originalAssigneeMemberId',\n );\n\n if (!assigneeMemberId) {\n const candidateMemberIds = readStringArrayField(\n payload,\n 'candidateMemberIds',\n );\n\n return candidateMemberIds.length\n ? `候選簽核人:${candidateMemberIds\n .map((memberId) =>\n readMemberDisplayText(memberId, memberProfilesById),\n )\n .join('、')}`\n : null;\n }\n\n const assigneeLabel = readMemberDisplayText(\n assigneeMemberId,\n memberProfilesById,\n );\n const originalAssigneeLabel = readMemberDisplayText(\n originalAssigneeMemberId,\n memberProfilesById,\n );\n\n return originalAssigneeMemberId &&\n originalAssigneeMemberId !== assigneeMemberId\n ? `待簽人:${assigneeLabel}(原簽核人:${originalAssigneeLabel})`\n : `待簽人:${assigneeLabel}`;\n }\n\n if (activityLog.eventType === 'TASK_DECIDED') {\n const action = readStringField(payload, 'action');\n const comment = readStringField(payload, 'comment');\n const decisionLabel = action\n ? `決議:${readTaskDecisionActionLabel(action)}`\n : null;\n\n const transferToMemberId = readStringField(payload, 'transferToMemberId');\n\n return action === 'REJECTED' && comment\n ? [decisionLabel, `拒絕原因:${comment}`]\n .filter(isPresentText)\n .join(' · ')\n : action === 'TRANSFERRED'\n ? [\n decisionLabel,\n `轉派給:${readMemberDisplayText(\n transferToMemberId,\n memberProfilesById,\n )}`,\n ]\n .filter(isPresentText)\n .join(' · ')\n : decisionLabel;\n }\n\n if (activityLog.eventType === 'TOKEN_ADVANCED') {\n const action = readStringField(payload, 'action');\n\n if (action) {\n return `流程結果:${readTaskDecisionActionLabel(action)}`;\n }\n\n const arrivedCount = readNumberField(payload, 'arrivedCount');\n const requiredCount = readNumberField(payload, 'requiredCount');\n\n if (arrivedCount !== null && requiredCount !== null) {\n return `等待匯合:${arrivedCount}/${requiredCount}`;\n }\n\n const fromNodeId = readStringField(payload, 'fromNodeId');\n const toNodeId = readStringField(payload, 'toNodeId');\n\n if (fromNodeId && toNodeId) {\n return `由 ${readNodeDisplayLabel(fromNodeId, workflow)} 前進至 ${readNodeDisplayLabel(toNodeId, workflow)}`;\n }\n }\n\n if (activityLog.eventType === 'ENGINE_PROCESS_REQUESTED') {\n const state = readStringField(payload, 'state');\n\n return state ? `案件狀態:${readInstanceStateLabel(state)}` : null;\n }\n\n return null;\n}\n\nfunction readActivityDetailParts(\n activityLog: ActivityLogRecord,\n payload: Readonly<Record<string, unknown>>,\n workflow: WorkflowDefinition | null,\n taskDecisionsByTaskId: ReadonlyMap<string, TaskDecisionRecord>,\n signaturesById: ReadonlyMap<string, SignatureRecord>,\n signatureVerification: SignatureVerificationRecord | null,\n memberProfilesById: ReadonlyMap<string, MemberProfileRecord>,\n): readonly ActivityStepDescriptionPart[] {\n if (activityLog.eventType !== 'TASK_DECIDED') {\n return [\n readTextDescriptionPart(\n readActivityDetail(\n activityLog,\n payload,\n workflow,\n memberProfilesById,\n ),\n ),\n ].filter(isActivityDescriptionPart);\n }\n\n const taskDecision = activityLog.taskId\n ? taskDecisionsByTaskId.get(activityLog.taskId)\n : null;\n const action =\n readStringField(payload, 'action') ?? taskDecision?.action ?? null;\n const comment =\n readStringField(payload, 'comment') ?? taskDecision?.comment ?? null;\n const transferToMemberId =\n readStringField(payload, 'transferToMemberId') ??\n taskDecision?.transferToMemberId ??\n null;\n const signatureId =\n readStringField(payload, 'signatureId') ??\n taskDecision?.signatureId ??\n null;\n const signature = signatureId ? signaturesById.get(signatureId) : null;\n const decisionLabel = action\n ? `決議:${readTaskDecisionActionLabel(action)}`\n : null;\n\n return [\n readTextDescriptionPart(decisionLabel),\n action === 'REJECTED'\n ? readDangerTextDescriptionPart(`拒絕原因:${comment ?? '-'}`)\n : null,\n action === 'RETURNED'\n ? readTextDescriptionPart(`退回說明:${comment ?? '-'}`)\n : null,\n action === 'TRANSFERRED'\n ? readTextDescriptionPart(\n `轉派給:${readMemberDisplayText(\n transferToMemberId,\n memberProfilesById,\n )}`,\n )\n : null,\n action === 'TRANSFERRED'\n ? readTextDescriptionPart(`轉派說明:${comment ?? '-'}`)\n : null,\n signature\n ? readTextDescriptionPart(\n signatureVerification?.valid\n ? `簽章:已驗證(${readShortHash(signature.signedPayloadHash)})`\n : `簽章:待檢查(${readShortHash(signature.signedPayloadHash)})`,\n )\n : null,\n ].filter(isActivityDescriptionPart);\n}\n\nfunction readActivityNodeLabel(\n nodeId: string | null,\n workflow: WorkflowDefinition | null,\n): string | null {\n return nodeId ? readNodeDisplayLabel(nodeId, workflow) : null;\n}\n\nfunction readNodeDisplayLabel(\n nodeId: string,\n workflow: WorkflowDefinition | null,\n): string {\n return (\n workflow?.nodes.find((node) => node.id === nodeId)?.data.label ?? nodeId\n );\n}\n\nfunction readReturnTargetOptions(\n workflow: WorkflowDefinition,\n node: WorkflowNode,\n): readonly { readonly id: string; readonly name: string }[] {\n if (node.type !== 'userTask' || !node.data.returnBehavior.allowReturn) {\n return [];\n }\n\n if (node.data.returnBehavior.allowedTargets === 'ANY') {\n return workflow.nodes\n .filter((candidate) => candidate.id !== node.id)\n .map((candidate) => ({\n id: candidate.id,\n name: `${candidate.data.label}(${readNodeKindLabel(candidate.type)})`,\n }));\n }\n\n const targetNodeId =\n node.data.returnBehavior.allowedTargets === 'INITIATOR'\n ? workflow.nodes.find((candidate) => candidate.type === 'startEvent')?.id\n : workflow.edges.find((edge) => edge.target === node.id)?.source;\n const targetNode = workflow.nodes.find(\n (candidate) => candidate.id === targetNodeId,\n );\n\n return targetNode\n ? [\n {\n id: targetNode.id,\n name: `${targetNode.data.label}(${readNodeKindLabel(\n targetNode.type,\n )})`,\n },\n ]\n : [];\n}\n\nfunction readTaskDecisionActionLabel(action: string): string {\n if (action === 'APPROVED') {\n return '同意';\n }\n\n if (action === 'REJECTED') {\n return '拒絕';\n }\n\n if (action === 'RETURNED') {\n return '退回';\n }\n\n if (action === 'TRANSFERRED') {\n return '轉派';\n }\n\n return action;\n}\n\nfunction readTaskStatusLabel(status: TaskRecord['status']): string {\n if (status === 'PENDING') {\n return '待處理';\n }\n\n if (status === 'IN_PROGRESS') {\n return '處理中';\n }\n\n if (status === 'COMPLETED') {\n return '已完成';\n }\n\n if (status === 'CANCELLED') {\n return '已取消';\n }\n\n if (status === 'TRANSFERRED') {\n return '已轉派';\n }\n\n return status;\n}\n\nfunction readTaskAssigneeLabel(\n task: TaskRecord,\n memberProfilesById: ReadonlyMap<string, MemberProfileRecord> = new Map(),\n): string {\n const delegationChain = readDelegationChain(task.delegationChainJson);\n\n if (!task.assigneeMemberId) {\n return task.candidateMemberIds.length\n ? `候選 ${task.candidateMemberIds\n .map((memberId) => readMemberDisplayText(memberId, memberProfilesById))\n .join('、')}`\n : '未指定';\n }\n\n const assigneeLabel = readMemberDisplayText(\n task.assigneeMemberId,\n memberProfilesById,\n );\n const originalAssigneeLabel = readMemberDisplayText(\n task.originalAssigneeMemberId,\n memberProfilesById,\n );\n\n if (\n delegationChain.length === 0 ||\n task.originalAssigneeMemberId === task.assigneeMemberId\n ) {\n return assigneeLabel;\n }\n\n return `${assigneeLabel}(原:${originalAssigneeLabel})`;\n}\n\nfunction canMemberActOnTask(\n task: TaskRecord,\n memberId: string | null,\n): boolean {\n if (!memberId) {\n return false;\n }\n\n return (\n task.assigneeMemberId === memberId ||\n task.candidateMemberIds.includes(memberId)\n );\n}\n\nfunction readInstanceStateLabel(state: string): string {\n if (state === 'APPROVED') {\n return '已同意';\n }\n\n if (state === 'CANCELLED') {\n return '已取消';\n }\n\n if (state === 'DRAFT') {\n return '草稿';\n }\n\n if (state === 'EXPIRED') {\n return '已逾期';\n }\n\n if (state === 'REJECTED') {\n return '已拒絕';\n }\n\n if (state === 'RETURNED') {\n return '已退回';\n }\n\n if (state === 'RUNNING') {\n return '進行中';\n }\n\n return state;\n}\n\nfunction isActivityError(\n activityLog: ActivityLogRecord,\n payload: Readonly<Record<string, unknown>>,\n): boolean {\n return (\n activityLog.eventType === 'SLA_TRIGGERED' ||\n readStringField(payload, 'action') === 'REJECTED' ||\n readStringField(payload, 'instanceState') === 'REJECTED'\n );\n}\n\nfunction formatActivityDateTime(value: string): string {\n return formatDateTime(value);\n}\n\nfunction readStringField(\n record: Readonly<Record<string, unknown>>,\n key: string,\n): string | null {\n const value = record[key];\n\n return typeof value === 'string' ? value : null;\n}\n\nfunction readStringArrayField(\n record: Readonly<Record<string, unknown>>,\n key: string,\n): readonly string[] {\n const value = record[key];\n\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === 'string')\n : [];\n}\n\nfunction readMemberOption(profile: MemberProfileRecord): MemberOption {\n return {\n email: profile.email,\n id: profile.memberId,\n name: `${profile.name} · ${profile.email}`,\n };\n}\n\nfunction readMemberOptionFromValue(value: unknown): MemberOption | null {\n if (!isRecord(value)) {\n return null;\n }\n\n const email = value.email;\n const id = value.id;\n const name = value.name;\n\n return typeof id === 'string' && typeof name === 'string'\n ? { email: typeof email === 'string' ? email : null, id, name }\n : null;\n}\n\nfunction readUniqueMemberOption(\n searchText: string,\n options: readonly MemberOption[],\n): MemberOption | null {\n const normalizedSearchText = searchText.trim().toLocaleLowerCase();\n\n if (!normalizedSearchText) {\n return null;\n }\n\n const matches = options.filter((option) =>\n [option.id, option.name, option.email ?? ''].some((value) =>\n value.toLocaleLowerCase().includes(normalizedSearchText),\n ),\n );\n\n return matches.length === 1 ? (matches[0] ?? null) : null;\n}\n\ninterface DelegationChainStep {\n readonly from: string;\n readonly reason: string;\n readonly ruleId: string | null;\n readonly to: string;\n}\n\nfunction readDelegationChain(value: string): readonly DelegationChainStep[] {\n try {\n const parsed = JSON.parse(value) as unknown;\n\n return Array.isArray(parsed)\n ? parsed\n .map((item): DelegationChainStep | null =>\n isRecord(item) ? readDelegationChainStep(item) : null,\n )\n .filter((item): item is DelegationChainStep => item !== null)\n : [];\n } catch {\n return [];\n }\n}\n\nfunction readDelegationChainStep(\n item: Readonly<Record<string, unknown>>,\n): DelegationChainStep | null {\n const from = readStringField(item, 'from');\n const to = readStringField(item, 'to');\n const reason = readStringField(item, 'reason');\n\n if (!from || !to || !reason) {\n return null;\n }\n\n return {\n from,\n reason,\n ruleId: readStringField(item, 'ruleId'),\n to,\n };\n}\n\nfunction readNumberField(\n record: Readonly<Record<string, unknown>>,\n key: string,\n): number | null {\n const value = record[key];\n\n return typeof value === 'number' ? value : null;\n}\n\nfunction isPresentText(value: string | null): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isPendingTask(task: TaskRecord): boolean {\n return task.status === 'PENDING' || task.status === 'IN_PROGRESS';\n}\n\nfunction WorkflowRuntimeNodeCard({\n data,\n}: NodeProps<RuntimeFlowNode>): ReactElement {\n return (\n <div style={readNodeStyle(data.tone)}>\n <Handle\n isConnectable={false}\n position={Position.Left}\n style={NODE_HANDLE_STYLE}\n type=\"target\"\n />\n <Typography\n component=\"span\"\n ellipsis\n title={data.label}\n variant=\"label-primary\"\n >\n {data.label}\n </Typography>\n <span style={readNodeStatusStyle(data.tone)}>{data.statusLabel}</span>\n <span title={data.secondaryLabel} style={NODE_SECONDARY_STYLE}>\n {data.secondaryLabel || data.kindLabel}\n </span>\n <Handle\n isConnectable={false}\n position={Position.Right}\n style={NODE_HANDLE_STYLE}\n type=\"source\"\n />\n </div>\n );\n}\n\nfunction readRuntimeFlowNodes(\n workflow: WorkflowDefinition,\n tasks: readonly TaskRecord[],\n tokens: readonly WorkflowTokenRecord[],\n instanceState: string,\n): RuntimeFlowNode[] {\n return workflow.nodes.map((node): RuntimeFlowNode => {\n const state = readNodeRuntimeState(node, tasks, tokens, instanceState);\n\n return {\n data: {\n kindLabel: readNodeKindLabel(node.type),\n label: node.data.label,\n secondaryLabel: state.secondaryLabel,\n statusLabel: state.statusLabel,\n tone: state.tone,\n },\n id: node.id,\n position: node.position,\n sourcePosition: Position.Right,\n targetPosition: Position.Left,\n type: 'workflowRuntime',\n };\n });\n}\n\nfunction layoutRuntimeWorkflowDefinition(\n workflow: WorkflowDefinition,\n): WorkflowDefinition {\n const graph = new dagre.graphlib.Graph();\n graph.setDefaultEdgeLabel(() => ({}));\n graph.setGraph({\n nodesep: 56,\n rankdir: 'LR',\n ranksep: 120,\n });\n workflow.nodes.forEach((node): void => {\n graph.setNode(node.id, {\n height: FLOW_NODE_LAYOUT_HEIGHT,\n width: FLOW_NODE_LAYOUT_WIDTH,\n });\n });\n workflow.edges.forEach((edge): void => {\n graph.setEdge(edge.source, edge.target);\n });\n dagre.layout(graph);\n\n return {\n ...workflow,\n nodes: workflow.nodes.map((node): WorkflowNode => {\n const positionedNode = graph.node(node.id) as\n | { readonly x: number; readonly y: number }\n | undefined;\n\n return positionedNode\n ? {\n ...node,\n position: {\n x: positionedNode.x - FLOW_NODE_LAYOUT_WIDTH / 2,\n y: positionedNode.y - FLOW_NODE_LAYOUT_HEIGHT / 2,\n },\n }\n : node;\n }),\n };\n}\n\nfunction readRuntimeFlowEdges(workflow: WorkflowDefinition): RuntimeFlowEdge[] {\n return workflow.edges.map((edge): RuntimeFlowEdge => {\n const label = readEdgeLabel(edge);\n\n return {\n animated: false,\n id: edge.id,\n label,\n labelBgBorderRadius: 6,\n labelBgPadding: [8, 4],\n labelBgStyle: {\n fill: edge.data.isDefault ? '#f8fafc' : '#eff6ff',\n stroke: edge.data.isDefault ? '#64748b' : '#2563eb',\n strokeWidth: 1,\n },\n labelShowBg: Boolean(label),\n labelStyle: {\n fill: edge.data.isDefault ? '#475569' : '#2563eb',\n fontSize: 12,\n fontWeight: 600,\n },\n source: edge.source,\n style: {\n stroke: '#475569',\n strokeWidth: 1.5,\n },\n target: edge.target,\n type: edge.type ?? 'smoothstep',\n };\n });\n}\n\nfunction readNodeRuntimeState(\n node: WorkflowNode,\n tasks: readonly TaskRecord[],\n tokens: readonly WorkflowTokenRecord[],\n instanceState: string,\n): Readonly<{\n secondaryLabel: string;\n statusLabel: string;\n tone: RuntimeTone;\n}> {\n const nodeTasks = tasks.filter((task) => task.nodeId === node.id);\n const pendingTask = nodeTasks.find(\n (task) => task.status === 'PENDING' || task.status === 'IN_PROGRESS',\n );\n const cancelledTask = nodeTasks.find((task) => task.status === 'CANCELLED');\n const completedTask = nodeTasks.find((task) => task.status === 'COMPLETED');\n const nodeTokens = tokens.filter((token) => token.currentNodeId === node.id);\n const activeToken = nodeTokens.find((token) => token.status === 'ACTIVE');\n const waitingToken = nodeTokens.find((token) => token.status === 'WAITING');\n\n if (pendingTask) {\n return {\n secondaryLabel: `處理者 ${readTaskAssigneeLabel(pendingTask)}`,\n statusLabel: '待處理',\n tone: 'current',\n };\n }\n\n if (cancelledTask) {\n return {\n secondaryLabel: `已取消 ${readTaskAssigneeLabel(cancelledTask)}`,\n statusLabel: '已取消',\n tone: 'cancelled',\n };\n }\n\n if (completedTask) {\n return {\n secondaryLabel: `已完成 ${readTaskAssigneeLabel(completedTask)}`,\n statusLabel: '已完成',\n tone: 'completed',\n };\n }\n\n if (activeToken) {\n return {\n secondaryLabel: `token ${activeToken.id}`,\n statusLabel: '執行中',\n tone: 'current',\n };\n }\n\n if (waitingToken) {\n return {\n secondaryLabel: `token ${waitingToken.id}`,\n statusLabel: '等待前置',\n tone: 'waiting',\n };\n }\n\n if (node.type === 'startEvent') {\n return {\n secondaryLabel: '流程已發起',\n statusLabel: '已發起',\n tone: 'completed',\n };\n }\n\n if (node.type === 'endEvent' && instanceState !== 'RUNNING') {\n return {\n secondaryLabel: instanceState,\n statusLabel: instanceState === 'REJECTED' ? '已拒絕' : '已完成',\n tone: instanceState === 'REJECTED' ? 'cancelled' : 'completed',\n };\n }\n\n return {\n secondaryLabel: readNodeKindLabel(node.type),\n statusLabel: '未抵達',\n tone: 'neutral',\n };\n}\n\nfunction readNodeKindLabel(type: WorkflowNode['type']): string {\n if (type === 'startEvent') {\n return '開始';\n }\n\n if (type === 'endEvent') {\n return '完成';\n }\n\n if (type === 'userTask') {\n return '簽核節點';\n }\n\n if (type === 'serviceTask') {\n return '知會節點';\n }\n\n if (type === 'exclusiveGateway') {\n return '條件分流';\n }\n\n return '平行處理';\n}\n\nfunction readNodeStyle(tone: RuntimeTone): CSSProperties {\n if (tone === 'current') {\n return {\n ...NODE_STYLE,\n border: '1px solid var(--mzn-color-primary, #0057ff)',\n boxShadow: '0 0 0 3px rgba(0, 87, 255, 0.14)',\n };\n }\n\n if (tone === 'completed') {\n return {\n ...NODE_STYLE,\n border: '1px solid #16a34a',\n };\n }\n\n if (tone === 'cancelled') {\n return {\n ...NODE_STYLE,\n border: '1px solid #dc2626',\n opacity: 0.72,\n };\n }\n\n if (tone === 'waiting') {\n return {\n ...NODE_STYLE,\n border: '1px dashed #64748b',\n };\n }\n\n return NODE_STYLE;\n}\n\nfunction readNodeStatusStyle(tone: RuntimeTone): CSSProperties {\n const baseStyle = NODE_STATUS_STYLE;\n\n if (tone === 'current') {\n return { ...baseStyle, background: '#eff6ff', color: '#2563eb' };\n }\n\n if (tone === 'completed') {\n return { ...baseStyle, background: '#f0fdf4', color: '#15803d' };\n }\n\n if (tone === 'cancelled') {\n return { ...baseStyle, background: '#fef2f2', color: '#dc2626' };\n }\n\n if (tone === 'waiting') {\n return { ...baseStyle, background: '#f8fafc', color: '#475569' };\n }\n\n return { ...baseStyle, background: '#f1f5f9', color: '#64748b' };\n}\n\nfunction readEdgeLabel(edge: WorkflowDefinition['edges'][number]): string {\n if (edge.data.label) {\n return edge.data.label;\n }\n\n if (edge.data.isDefault) {\n return '其他情況';\n }\n\n return edge.data.condition ?? '';\n}\n\nfunction readEdgeSummaries(workflow: WorkflowDefinition): readonly string[] {\n return workflow.edges\n .map(readEdgeLabel)\n .filter((label) => label.trim().length > 0);\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("./chunk-CMqjfN_6.cjs");let t=require("moment");t=e.t(t,1);function n(e){return e?(0,t.default)(e).format(`YYYY-MM-DD HH:mm:ss`):`-`}Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return n}});
2
+ //# sourceMappingURL=format-date-time-XxBzF0F5.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"format-date-time-26_pFvv4.cjs","names":[],"sources":["../../src/lib/format-date-time.ts"],"sourcesContent":["import moment from 'moment';\n\n/**\n * Default BPM datetime formatter: `YYYY-MM-DD HH:mm:ss`. Returns `'-'` for\n * `null` / `undefined` so list cells render a consistent placeholder.\n */\nexport function formatDateTime(value: string | null | undefined): string {\n return value ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-';\n}\n"],"mappings":"oFAMA,SAAgB,EAAe,EAA0C,CACvE,OAAO,GAAA,EAAA,EAAA,SAAe,CAAK,EAAE,OAAO,qBAAqB,EAAI,GAC/D"}
1
+ {"version":3,"file":"format-date-time-XxBzF0F5.cjs","names":[],"sources":["../../src/lib/format-date-time.ts"],"sourcesContent":["import moment from 'moment';\n\n/**\n * Default BPM datetime formatter: `YYYY-MM-DD HH:mm:ss`. Returns `'-'` for\n * `null` / `undefined` so list cells render a consistent placeholder.\n */\nexport function formatDateTime(value: string | null | undefined): string {\n return value ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-';\n}\n"],"mappings":"2EAMA,SAAgB,EAAe,EAA0C,CACvE,OAAO,GAAA,EAAA,EAAA,SAAe,CAAK,EAAE,OAAO,qBAAqB,EAAI,GAC/D"}
@@ -0,0 +1,2 @@
1
+ "use client";require('../login.css');const e=require("./router-adapter-BybHrCNP.cjs"),t=require("./auth-provider-D2P-qWmY.cjs");let n=require("react"),r=require("@mezzanine-ui/react"),i=require("@rytass/bpm-core-client"),a=require("react/jsx-runtime"),o=require("@mezzanine-ui/icons");var s={loginPage:`bpm_loginPage_BjzaI`,loginPanel:`bpm_loginPanel_xo0TW`,brand:`bpm_brand_KVmvZ`,brandLogo:`bpm_brandLogo_jxEAG`,form:`bpm_form_rKKP2`,demoUsers:`bpm_demoUsers_g-5Hd`,demoUserList:`bpm_demoUserList_IxtKp`,demoUserButton:`bpm_demoUserButton_YdkSa`},c=`/rytass-logo.png`,l=`lin.ceo@example.internal`,u=`demo`;function d({logoSrc:d=c,defaultIdentifier:p=l,defaultPassword:h=u,defaultNextPath:g,brandTitle:_=`BPM Admin`,brandSubtitle:v=`BPM API 登入`}={}){let y=e.r(),{loading:b,login:x,member:S}=t.n(),[C,w]=(0,n.useState)([]),[T,E]=(0,n.useState)(null),[D,O]=(0,n.useState)(p),[k,A]=(0,n.useState)(h),[j,M]=(0,n.useState)(!1);(0,n.useEffect)(()=>{b||!S||y.replace(f(g))},[g,b,S,y]),(0,n.useEffect)(()=>{(async()=>{try{w(await(0,i.listApiTestMembers)())}catch{w([])}})()},[]);let N=(0,n.useCallback)(async e=>{e.preventDefault(),E(null),M(!0);try{await x({identifier:D,password:k}),y.replace(f(g))}catch(e){E(m(e))}finally{M(!1)}},[g,D,x,k,y]);function P(e){O(e.target.value)}function F(e){A(e.target.value)}return(0,a.jsx)(`main`,{className:s.loginPage,children:(0,a.jsxs)(`section`,{className:s.loginPanel,children:[(0,a.jsxs)(`div`,{className:s.brand,children:[(0,a.jsx)(`img`,{alt:``,className:s.brandLogo,src:d}),(0,a.jsxs)(`div`,{children:[(0,a.jsx)(r.Typography,{variant:`h3`,children:_}),(0,a.jsx)(r.Typography,{color:`text-neutral`,variant:`body`,children:v})]})]}),(0,a.jsxs)(`form`,{className:s.form,onSubmit:N,children:[(0,a.jsxs)(`label`,{children:[(0,a.jsx)(r.Typography,{color:`text-neutral`,variant:`caption`,children:`帳號`}),(0,a.jsx)(r.Input,{fullWidth:!0,name:`identifier`,onChange:P,placeholder:`member id 或 email`,value:D})]}),(0,a.jsxs)(`label`,{children:[(0,a.jsx)(r.Typography,{color:`text-neutral`,variant:`caption`,children:`密碼`}),(0,a.jsx)(r.Input,{fullWidth:!0,inputType:`password`,name:`password`,onChange:F,value:k,variant:`password`})]}),T?(0,a.jsx)(r.Typography,{color:`text-error`,variant:`body`,children:T}):null,(0,a.jsx)(r.Button,{disabled:j,icon:o.LoginIcon,iconType:`leading`,type:`submit`,variant:`base-primary`,children:`登入`})]}),C.length?(0,a.jsxs)(`div`,{className:s.demoUsers,children:[(0,a.jsx)(r.Typography,{color:`text-neutral`,variant:`caption`,children:`測試帳號`}),(0,a.jsx)(`div`,{className:s.demoUserList,children:C.map(e=>(0,a.jsxs)(`button`,{className:s.demoUserButton,onClick:()=>O(e.email),type:`button`,children:[(0,a.jsx)(`span`,{children:e.name}),(0,a.jsx)(`span`,{children:e.email})]},e.memberId))})]}):null]})})}function f(e){if(e&&e.startsWith(`/`)&&!e.startsWith(`//`))return e;let t=p().get(`next`);return!t||!t.startsWith(`/`)||t.startsWith(`//`)?`/`:t}function p(){return typeof window>`u`?new URLSearchParams:new URLSearchParams(window.location.search)}function m(e){return e instanceof Error?e.message:`登入失敗`}Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return d}});
2
+ //# sourceMappingURL=login-BfmfCclF.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"login-CQ9MfwcC.cjs","names":[],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '登入失敗';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"login-BfmfCclF.cjs","names":[],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '登入失敗';\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
- import { a as e, n as t } from "./auth-provider-Bnox5gsx.js";
2
+ import { r as e } from "./router-adapter-BdHZXLS3.js";
3
+ import { n as t } from "./auth-provider-TTO9eNZV.js";
3
4
  import { useCallback as n, useEffect as r, useState as i } from "react";
4
5
  import { Button as a, Input as o, Typography as s } from "@mezzanine-ui/react";
5
6
  import { listApiTestMembers as c } from "@rytass/bpm-core-client";
@@ -154,4 +155,4 @@ function y(e) {
154
155
  //#endregion
155
156
  export { g as t };
156
157
 
157
- //# sourceMappingURL=login-C20yVxbc.js.map
158
+ //# sourceMappingURL=login-xgI4wLHe.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"login-C20yVxbc.js","names":[],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '登入失敗';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"login-xgI4wLHe.js","names":[],"sources":["../../src/views/login/login.module.scss","../../src/views/login/LoginView.tsx"],"sourcesContent":[".loginPage {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n\n.loginPanel {\n border: 1px solid var(--mzn-color-border);\n border-radius: 8px;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n gap: 28px;\n max-width: 440px;\n padding: 32px;\n width: 100%;\n}\n\n.brand {\n align-items: center;\n display: flex;\n gap: 14px;\n}\n\n.brandLogo {\n border-radius: 8px;\n height: 40px;\n width: 40px;\n object-fit: contain;\n}\n\n.form {\n display: flex;\n flex-direction: column;\n gap: 18px;\n}\n\n.demoUsers {\n border-top: 1px solid var(--mzn-color-border);\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding-top: 20px;\n}\n\n.demoUserList {\n display: grid;\n gap: 8px;\n}\n\n.demoUserButton {\n background: transparent;\n border: 1px solid var(--mzn-color-border);\n border-radius: 6px;\n color: inherit;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n font: inherit;\n gap: 2px;\n padding: 10px 12px;\n text-align: left;\n}\n\n.demoUserButton:hover {\n border-color: var(--mzn-color-primary);\n}\n\n.demoUserButton span:last-child {\n color: var(--mzn-color-text-secondary);\n font-size: 12px;\n}\n","'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ChangeEvent,\n type FormEvent,\n type ReactElement,\n} from 'react';\nimport { Button, Input, Typography } from '@mezzanine-ui/react';\nimport { LoginIcon } from '@mezzanine-ui/icons';\nimport {\n listApiTestMembers,\n type ApiPublicMember,\n} from '@rytass/bpm-core-client';\nimport { useAuth } from '../../lib/auth-provider';\nimport { useRouterAdapter } from '../../lib/router-adapter';\nimport styles from './login.module.scss';\n\nexport interface LoginViewProps {\n /**\n * Logo image URL. Renders an inline `<img>` so the view stays framework\n * agnostic (no `next/image` dependency). Defaults to\n * `/rytass-logo.png` — host should serve a static asset at that path or\n * override via this prop.\n */\n readonly logoSrc?: string;\n /**\n * Pre-fill the identifier input. Defaults to the seeded demo account\n * email so the form is usable out of the box.\n */\n readonly defaultIdentifier?: string;\n /**\n * Pre-fill the password input. Defaults to the seeded demo password\n * (`'demo'`). Production hosts should pass an empty string.\n */\n readonly defaultPassword?: string;\n /**\n * Custom redirect target after a successful login. Defaults to reading\n * `?next=` from the host router's search params, falling back to `'/'`.\n */\n readonly defaultNextPath?: string;\n /**\n * Override the BPM admin brand title shown above the form. Defaults to\n * `'BPM Admin'`.\n */\n readonly brandTitle?: string;\n /**\n * Override the brand subtitle shown above the form. Defaults to\n * `'BPM API 登入'`.\n */\n readonly brandSubtitle?: string;\n}\n\nconst DEFAULT_LOGO = '/rytass-logo.png';\nconst DEFAULT_IDENTIFIER = 'lin.ceo@example.internal';\nconst DEFAULT_PASSWORD = 'demo';\n\n/**\n * Login UI for the BPM admin host. Renders the brand mark, identifier /\n * password fields, and a \"test members\" picker fed by\n * `listApiTestMembers()`. Self-contained: composes Mezzanine UI primitives,\n * reads the auth context, and uses the host router adapter to redirect on\n * success.\n *\n * Wrap with `<AuthProvider>` (and indirectly `<RouterAdapterProvider>`)\n * higher in the tree. The `pages/login` subpath ships a thin Next.js\n * wrapper that exports `default` (Server Component) and `metadata`.\n */\nexport function LoginView({\n logoSrc = DEFAULT_LOGO,\n defaultIdentifier = DEFAULT_IDENTIFIER,\n defaultPassword = DEFAULT_PASSWORD,\n defaultNextPath,\n brandTitle = 'BPM Admin',\n brandSubtitle = 'BPM API 登入',\n}: LoginViewProps = {}): ReactElement {\n const router = useRouterAdapter();\n const { loading, login, member } = useAuth();\n const [testMembers, setTestMembers] = useState<readonly ApiPublicMember[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const [identifier, setIdentifier] = useState(defaultIdentifier);\n const [password, setPassword] = useState(defaultPassword);\n const [submitting, setSubmitting] = useState(false);\n\n useEffect((): void => {\n if (loading || !member) return;\n router.replace(resolveNextPath(defaultNextPath));\n }, [defaultNextPath, loading, member, router]);\n\n useEffect((): void => {\n void (async () => {\n try {\n setTestMembers(await listApiTestMembers());\n } catch {\n setTestMembers([]);\n }\n })();\n }, []);\n\n const handleSubmit = useCallback(\n async (event: FormEvent<HTMLFormElement>): Promise<void> => {\n event.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n await login({ identifier, password });\n router.replace(resolveNextPath(defaultNextPath));\n } catch (loginError: unknown) {\n setError(readErrorMessage(loginError));\n } finally {\n setSubmitting(false);\n }\n },\n [defaultNextPath, identifier, login, password, router],\n );\n\n function handleIdentifierChange(event: ChangeEvent<HTMLInputElement>): void {\n setIdentifier(event.target.value);\n }\n function handlePasswordChange(event: ChangeEvent<HTMLInputElement>): void {\n setPassword(event.target.value);\n }\n\n return (\n <main className={styles.loginPage}>\n <section className={styles.loginPanel}>\n <div className={styles.brand}>\n <img alt=\"\" className={styles.brandLogo} src={logoSrc} />\n <div>\n <Typography variant=\"h3\">{brandTitle}</Typography>\n <Typography color=\"text-neutral\" variant=\"body\">\n {brandSubtitle}\n </Typography>\n </div>\n </div>\n\n <form className={styles.form} onSubmit={handleSubmit}>\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 帳號\n </Typography>\n <Input\n fullWidth\n name=\"identifier\"\n onChange={handleIdentifierChange}\n placeholder=\"member id 或 email\"\n value={identifier}\n />\n </label>\n\n <label>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 密碼\n </Typography>\n <Input\n fullWidth\n inputType=\"password\"\n name=\"password\"\n onChange={handlePasswordChange}\n value={password}\n variant=\"password\"\n />\n </label>\n\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n\n <Button\n disabled={submitting}\n icon={LoginIcon}\n iconType=\"leading\"\n type=\"submit\"\n variant=\"base-primary\"\n >\n 登入\n </Button>\n </form>\n\n {testMembers.length ? (\n <div className={styles.demoUsers}>\n <Typography color=\"text-neutral\" variant=\"caption\">\n 測試帳號\n </Typography>\n <div className={styles.demoUserList}>\n {testMembers.map((testMember) => (\n <button\n className={styles.demoUserButton}\n key={testMember.memberId}\n onClick={(): void => setIdentifier(testMember.email)}\n type=\"button\"\n >\n <span>{testMember.name}</span>\n <span>{testMember.email}</span>\n </button>\n ))}\n </div>\n </div>\n ) : null}\n </section>\n </main>\n );\n}\n\nfunction resolveNextPath(fallback: string | undefined): string {\n if (fallback && fallback.startsWith('/') && !fallback.startsWith('//')) {\n return fallback;\n }\n const params = readBrowserSearchParams();\n const next = params.get('next');\n if (!next || !next.startsWith('/') || next.startsWith('//')) {\n return '/';\n }\n return next;\n}\n\nfunction readBrowserSearchParams(): URLSearchParams {\n if (typeof window === 'undefined') return new URLSearchParams();\n return new URLSearchParams(window.location.search);\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '登入失敗';\n}\n"],"mappings":"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"}