@rytass/bpm-core-react 0.10.0 → 0.11.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.
- package/CHANGELOG.md +15 -0
- package/dist/chunks/FormBuilderView-CAqjOYAP.cjs +3 -0
- package/dist/chunks/{FormBuilderView-D6baVqBY.cjs.map → FormBuilderView-CAqjOYAP.cjs.map} +1 -1
- package/dist/chunks/{FormBuilderView-QtREqV4j.js → FormBuilderView-TzSezeqC.js} +18 -16
- package/dist/chunks/{FormBuilderView-QtREqV4j.js.map → FormBuilderView-TzSezeqC.js.map} +1 -1
- package/dist/chunks/{FormRendererView-D8QHLvd0.js → FormRendererView-BurlEDUu.js} +44 -41
- package/dist/chunks/{FormRendererView-D8QHLvd0.js.map → FormRendererView-BurlEDUu.js.map} +1 -1
- package/dist/chunks/FormRendererView-Dde1s3I0.cjs +2 -0
- package/dist/chunks/{FormRendererView-Cw9LiidV.cjs.map → FormRendererView-Dde1s3I0.cjs.map} +1 -1
- package/dist/chunks/admin-pickers-Btvij1at.cjs.map +1 -1
- package/dist/chunks/admin-pickers-DLlG_1du.js.map +1 -1
- package/dist/chunks/approval-instance-list-page-BjX2kaoF.js.map +1 -1
- package/dist/chunks/approval-instance-list-page-CWiQvnfE.cjs +2 -0
- package/dist/chunks/{approval-instance-list-page-C408vxE2.cjs.map → approval-instance-list-page-CWiQvnfE.cjs.map} +1 -1
- package/dist/chunks/auth-provider-4BeCw7cI.cjs.map +1 -1
- package/dist/chunks/auth-provider-B5oPmvk2.js.map +1 -1
- package/dist/chunks/bpm-form-field-Bc6k4ZEO.cjs.map +1 -1
- package/dist/chunks/categories-DGlxuC6X.js.map +1 -1
- package/dist/chunks/categories-Yid05WAX.cjs +2 -0
- package/dist/chunks/{categories-DUfldZ_9.cjs.map → categories-Yid05WAX.cjs.map} +1 -1
- package/dist/chunks/{compose-D-OS-Ngs.js → compose-CUOe5Ia9.js} +4 -4
- package/dist/chunks/{compose-D-OS-Ngs.js.map → compose-CUOe5Ia9.js.map} +1 -1
- package/dist/chunks/compose-CyRkXdeu.cjs +2 -0
- package/dist/chunks/{compose-CODnd9-2.cjs.map → compose-CyRkXdeu.cjs.map} +1 -1
- package/dist/chunks/dashboard-page-CH87L5nx.cjs +2 -0
- package/dist/chunks/{dashboard-page-DwHQY6Ki.cjs.map → dashboard-page-CH87L5nx.cjs.map} +1 -1
- package/dist/chunks/dashboard-page-DJ9vOPga.js.map +1 -1
- package/dist/chunks/{delegations-DLEEFBlH.js → delegations-0MbJ7CWj.js} +3 -2
- package/dist/chunks/{delegations-DLEEFBlH.js.map → delegations-0MbJ7CWj.js.map} +1 -1
- package/dist/chunks/delegations-Bc9psOZ3.cjs +2 -0
- package/dist/chunks/{delegations-CGj-tZNO.cjs.map → delegations-Bc9psOZ3.cjs.map} +1 -1
- package/dist/chunks/{delegations-DqMpI_Nk.js → delegations-DyMGIDpp.js} +3 -2
- package/dist/chunks/{delegations-DqMpI_Nk.js.map → delegations-DyMGIDpp.js.map} +1 -1
- package/dist/chunks/delegations-vpLtus9g.cjs +2 -0
- package/dist/chunks/{delegations-kbMOSEif.cjs.map → delegations-vpLtus9g.cjs.map} +1 -1
- package/dist/chunks/{designer-DDSxwtjV.js → designer-Cb3WAC46.js} +39 -30
- package/dist/chunks/{designer-DDSxwtjV.js.map → designer-Cb3WAC46.js.map} +1 -1
- package/dist/chunks/designer-zLliyrHu.cjs +65 -0
- package/dist/chunks/{designer-Cdn10NNl.cjs.map → designer-zLliyrHu.cjs.map} +1 -1
- package/dist/chunks/detail-Bp2Nv0cs.cjs +2 -0
- package/dist/chunks/{detail-CtVu5NTi.cjs.map → detail-Bp2Nv0cs.cjs.map} +1 -1
- package/dist/chunks/{detail-DAPz2qyn.js → detail-Cmw-aQcu.js} +4 -4
- package/dist/chunks/{detail-DAPz2qyn.js.map → detail-Cmw-aQcu.js.map} +1 -1
- package/dist/chunks/format-date-time-CB-LxzqT.js.map +1 -1
- package/dist/chunks/format-date-time-D-cx5M8z.cjs +2 -0
- package/dist/chunks/{format-date-time-XxBzF0F5.cjs.map → format-date-time-D-cx5M8z.cjs.map} +1 -1
- package/dist/chunks/login-9bCXyjbX.cjs.map +1 -1
- package/dist/chunks/login-BKxpLibd.js.map +1 -1
- package/dist/chunks/notifications-Bc4mgSds.cjs +2 -0
- package/dist/chunks/{notifications--hwl-DR0.cjs.map → notifications-Bc4mgSds.cjs.map} +1 -1
- package/dist/chunks/notifications-DJ5wzCye.js.map +1 -1
- package/dist/chunks/{orgs-DytLvZ_X.js → orgs-B930facb.js} +11 -10
- package/dist/chunks/{orgs-DytLvZ_X.js.map → orgs-B930facb.js.map} +1 -1
- package/dist/chunks/orgs-BefGgeyc.cjs +2 -0
- package/dist/chunks/{orgs-Ni22buHs.cjs.map → orgs-BefGgeyc.cjs.map} +1 -1
- package/dist/chunks/providers-BsEn3ikK.cjs +2 -0
- package/dist/chunks/{providers-D_zTbkQH.cjs.map → providers-BsEn3ikK.cjs.map} +1 -1
- package/dist/chunks/{providers-DEqcuXw9.js → providers-DjLh4oLY.js} +3 -3
- package/dist/chunks/{providers-DEqcuXw9.js.map → providers-DjLh4oLY.js.map} +1 -1
- package/dist/chunks/{chunk-CMqjfN_6.cjs → rolldown-runtime-BocRIvOZ.cjs} +1 -1
- package/dist/chunks/router-adapter--gYs13E8.cjs.map +1 -1
- package/dist/chunks/routes-config-fDVHmvXi.cjs.map +1 -1
- package/dist/chunks/users-C9LA-Rd7.cjs +2 -0
- package/dist/chunks/{users-0HqlM_VQ.cjs.map → users-C9LA-Rd7.cjs.map} +1 -1
- package/dist/chunks/users-FWrb3ehW.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/next/index.cjs +1 -1
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.js +9 -9
- package/dist/next/index.js.map +1 -1
- package/dist/next/workflow-chat-route.cjs +1 -1
- package/dist/next/workflow-chat-route.cjs.map +1 -1
- package/dist/next/workflow-chat-route.js +2 -1
- package/dist/next/workflow-chat-route.js.map +1 -1
- package/dist/pages/admin/delegations/index.cjs +1 -1
- package/dist/pages/admin/delegations/index.cjs.map +1 -1
- package/dist/pages/admin/delegations/index.js +1 -1
- package/dist/pages/admin/orgs/index.cjs +1 -1
- package/dist/pages/admin/orgs/index.cjs.map +1 -1
- package/dist/pages/admin/orgs/index.js +1 -1
- package/dist/pages/admin/users/index.cjs +1 -1
- package/dist/pages/admin/users/index.cjs.map +1 -1
- package/dist/pages/cc/index.cjs.map +1 -1
- package/dist/pages/dashboard/index.cjs.map +1 -1
- package/dist/pages/delegations/index.cjs +1 -1
- package/dist/pages/delegations/index.cjs.map +1 -1
- package/dist/pages/delegations/index.js +1 -1
- package/dist/pages/inbox/index.cjs.map +1 -1
- package/dist/pages/instances/detail/index.cjs +1 -1
- package/dist/pages/instances/detail/index.cjs.map +1 -1
- package/dist/pages/instances/detail/index.js +1 -1
- package/dist/pages/instances/new/index.cjs.map +1 -1
- package/dist/pages/login/index.cjs.map +1 -1
- package/dist/pages/root/index.cjs.map +1 -1
- package/dist/pages/search/index.cjs.map +1 -1
- package/dist/pages/sent/index.cjs.map +1 -1
- package/dist/pages/settings/notifications/index.cjs +1 -1
- package/dist/pages/settings/notifications/index.cjs.map +1 -1
- package/dist/pages/templates/categories/index.cjs +1 -1
- package/dist/pages/templates/categories/index.cjs.map +1 -1
- package/dist/pages/templates/compose/index.cjs +1 -1
- package/dist/pages/templates/compose/index.cjs.map +1 -1
- package/dist/pages/templates/compose/index.js +1 -1
- package/dist/pages/templates/designer/index.cjs +1 -1
- package/dist/pages/templates/designer/index.cjs.map +1 -1
- package/dist/pages/templates/designer/index.js +1 -1
- package/dist/pages/templates/index.cjs.map +1 -1
- package/dist/pages/templates/versions/index.cjs.map +1 -1
- package/dist/views/admin/delegations/index.cjs +1 -1
- package/dist/views/admin/delegations/index.js +1 -1
- package/dist/views/admin/index.cjs +1 -1
- package/dist/views/admin/index.js +2 -2
- package/dist/views/admin/orgs/index.cjs +1 -1
- package/dist/views/admin/orgs/index.js +1 -1
- package/dist/views/admin/users/index.cjs +1 -1
- package/dist/views/cc/index.cjs +1 -1
- package/dist/views/cc/index.cjs.map +1 -1
- package/dist/views/dashboard/index.cjs +1 -1
- package/dist/views/dashboard/index.cjs.map +1 -1
- package/dist/views/delegations/index.cjs +1 -1
- package/dist/views/delegations/index.js +1 -1
- package/dist/views/forms/builder/index.cjs +1 -1
- package/dist/views/forms/builder/index.js +1 -1
- package/dist/views/forms/renderer/index.cjs +1 -1
- package/dist/views/forms/renderer/index.js +1 -1
- package/dist/views/inbox/index.cjs +1 -1
- package/dist/views/inbox/index.cjs.map +1 -1
- package/dist/views/inbox/index.js.map +1 -1
- package/dist/views/instances/detail/index.cjs +1 -1
- package/dist/views/instances/detail/index.js +1 -1
- package/dist/views/instances/new/index.cjs +1 -1
- package/dist/views/instances/new/index.cjs.map +1 -1
- package/dist/views/instances/new/index.js +1 -1
- package/dist/views/instances/new/index.js.map +1 -1
- package/dist/views/search/index.cjs +1 -1
- package/dist/views/search/index.cjs.map +1 -1
- package/dist/views/sent/index.cjs +1 -1
- package/dist/views/sent/index.cjs.map +1 -1
- package/dist/views/settings/index.cjs +1 -1
- package/dist/views/settings/notifications/index.cjs +1 -1
- package/dist/views/templates/categories/index.cjs +1 -1
- package/dist/views/templates/compose/index.cjs +1 -1
- package/dist/views/templates/compose/index.js +1 -1
- package/dist/views/templates/designer/index.cjs +1 -1
- package/dist/views/templates/designer/index.js +1 -1
- package/dist/views/templates/index.cjs +1 -1
- package/dist/views/templates/index.cjs.map +1 -1
- package/dist/views/templates/index.js.map +1 -1
- package/dist/views/templates/versions/index.cjs +1 -1
- package/dist/views/templates/versions/index.cjs.map +1 -1
- package/dist/views/templates/versions/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunks/FormBuilderView-D6baVqBY.cjs +0 -3
- package/dist/chunks/FormRendererView-Cw9LiidV.cjs +0 -2
- package/dist/chunks/approval-instance-list-page-C408vxE2.cjs +0 -2
- package/dist/chunks/categories-DUfldZ_9.cjs +0 -2
- package/dist/chunks/compose-CODnd9-2.cjs +0 -2
- package/dist/chunks/dashboard-page-DwHQY6Ki.cjs +0 -2
- package/dist/chunks/delegations-CGj-tZNO.cjs +0 -2
- package/dist/chunks/delegations-kbMOSEif.cjs +0 -2
- package/dist/chunks/designer-Cdn10NNl.cjs +0 -65
- package/dist/chunks/detail-CtVu5NTi.cjs +0 -2
- package/dist/chunks/format-date-time-XxBzF0F5.cjs +0 -2
- package/dist/chunks/notifications--hwl-DR0.cjs +0 -2
- package/dist/chunks/orgs-Ni22buHs.cjs +0 -2
- package/dist/chunks/providers-D_zTbkQH.cjs +0 -2
- package/dist/chunks/users-0HqlM_VQ.cjs +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users-0HqlM_VQ.cjs","names":[],"sources":["../../src/views/admin/users/users.module.scss","../../src/views/admin/users/AdminUsersView.tsx"],"sourcesContent":[".memberFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n\n :global(.mzn-input),\n :global(.mzn-input-container),\n :global(.mzn-text-field) {\n width: 100%;\n min-width: 0 !important;\n }\n}\n\n.header {\n display: grid;\n gap: 4px;\n padding: 24px 24px 0;\n}\n\n.detailFields {\n display: grid;\n gap: 16px;\n}\n\n.detailSection {\n display: grid;\n gap: 8px;\n}\n\n.membershipList {\n display: grid;\n gap: 8px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n ReactElement,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n BaseCard,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport styles from './users.module.scss';\nimport {\n listMemberDirectoryPage,\n MemberProfileRecord,\n resolveMembers,\n} from '@rytass/bpm-core-client';\nimport {\n listMemberships,\n MembershipRecord,\n OrgUnitRecord,\n PositionRecord,\n readOrganizationDashboard,\n readResolvedManager,\n ResolvedManagerRecord,\n} from '@rytass/bpm-core-client/organization';\n\ntype MemberRow = Readonly<\n Record<string, unknown> &\n MemberProfileRecord & {\n key: string;\n }\n>;\n\nconst MEMBER_PAGE_SIZE_OPTIONS = [10, 20, 50];\n\n\nexport function AdminUsersView(): ReactElement {\n const [detailMember, setDetailMember] = useState<MemberProfileRecord | null>(\n null,\n );\n const [detailMemberships, setDetailMemberships] = useState<\n readonly MembershipRecord[]\n >([]);\n const [detailManagerProfile, setDetailManagerProfile] =\n useState<MemberProfileRecord | null>(null);\n const [detailResolvedManager, setDetailResolvedManager] =\n useState<ResolvedManagerRecord | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [memberPage, setMemberPage] = useState(1);\n const [memberPageSize, setMemberPageSize] = useState(10);\n const [memberTotalCount, setMemberTotalCount] = useState(0);\n const [members, setMembers] = useState<readonly MemberProfileRecord[]>([]);\n const [orgUnits, setOrgUnits] = useState<readonly OrgUnitRecord[]>([]);\n const [positions, setPositions] = useState<readonly PositionRecord[]>([]);\n const [searchText, setSearchText] = useState('');\n\n const orgUnitsById = useMemo(\n (): ReadonlyMap<string, OrgUnitRecord> =>\n new Map(orgUnits.map((orgUnit) => [orgUnit.id, orgUnit])),\n [orgUnits],\n );\n const positionsById = useMemo(\n (): ReadonlyMap<string, PositionRecord> =>\n new Map(positions.map((position) => [position.id, position])),\n [positions],\n );\n\n const refreshMembers = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const [memberPageResult, organization] = await Promise.all([\n listMemberDirectoryPage({\n page: memberPage,\n pageSize: memberPageSize,\n searchText,\n }),\n readOrganizationDashboard(),\n ]);\n\n setMembers(memberPageResult.members);\n setMemberTotalCount(memberPageResult.totalCount);\n setOrgUnits(organization.orgUnits);\n setPositions(organization.positions);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [memberPage, memberPageSize, searchText]);\n\n useEffect((): void => {\n void refreshMembers();\n }, [refreshMembers]);\n\n const rows = useMemo(\n (): MemberRow[] =>\n members.map((member) => ({\n ...member,\n key: member.memberId,\n })),\n [members],\n );\n\n const openDetail = useCallback(\n async (member: MemberProfileRecord): Promise<void> => {\n setDetailMember(member);\n setDetailManagerProfile(null);\n setError(null);\n\n try {\n const [memberships, resolvedManager] = await Promise.all([\n listMemberships({ memberId: member.memberId }),\n readResolvedManager(member.memberId),\n ]);\n\n setDetailMemberships(memberships);\n setDetailResolvedManager(resolvedManager);\n setDetailManagerProfile(\n (\n await resolveMembers(\n resolvedManager.managerMemberId\n ? [resolvedManager.managerMemberId]\n : [],\n )\n )[0] ?? null,\n );\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n }\n },\n [],\n );\n\n const columns = useMemo(\n (): TableColumn<MemberRow>[] => [\n { dataIndex: 'name', key: 'name', title: '姓名', width: 160 },\n { dataIndex: 'email', key: 'email', title: '信箱', width: 260 },\n ],\n [],\n );\n const tableActions = useMemo(\n (): TableActions<MemberRow> => ({\n render: (): ReturnType<TableActions<MemberRow>['render']> => [\n {\n name: '檢視',\n onClick: (record): void => {\n void openDetail(record);\n },\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [openDetail],\n );\n\n function closeDetail(): void {\n setDetailMember(null);\n setDetailManagerProfile(null);\n setDetailMemberships([]);\n setDetailResolvedManager(null);\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"會員資料由 host member resolver 提供,BPM 僅維護組織歸屬與主管解析。\"\n title=\"會員對照\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.memberFilterArea}>\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"memberSearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setMemberPage(1);\n }}\n placeholder=\"搜尋姓名或信箱\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n loading={loading}\n pagination={{\n current: memberPage,\n onChange: (page): void => {\n setMemberPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setMemberPage(1);\n setMemberPageSize(pageSize);\n },\n pageSize: memberPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: MEMBER_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: memberTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n\n <MemberDetailModal\n managerProfile={detailManagerProfile}\n member={detailMember}\n memberships={detailMemberships}\n onClose={closeDetail}\n orgUnitsById={orgUnitsById}\n positionsById={positionsById}\n resolvedManager={detailResolvedManager}\n />\n </>\n );\n}\n\nfunction MemberDetailModal({\n managerProfile,\n member,\n memberships,\n onClose,\n orgUnitsById,\n positionsById,\n resolvedManager,\n}: {\n readonly managerProfile: MemberProfileRecord | null;\n readonly member: MemberProfileRecord | null;\n readonly memberships: readonly MembershipRecord[];\n readonly onClose: () => void;\n readonly orgUnitsById: ReadonlyMap<string, OrgUnitRecord>;\n readonly positionsById: ReadonlyMap<string, PositionRecord>;\n readonly resolvedManager: ResolvedManagerRecord | null;\n}): ReactElement {\n return (\n <Modal\n cancelText=\"關閉\"\n confirmText=\"關閉\"\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={onClose}\n open={Boolean(member)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={member?.name ?? '會員明細'}\n >\n {member ? (\n <div className={styles.detailFields}>\n <BaseCard title=\"基本資料\">\n <div className={styles.detailSection}>\n <Typography variant=\"body\">信箱:{member.email}</Typography>\n </div>\n </BaseCard>\n <BaseCard title=\"BPM 組織歸屬\">\n <div className={styles.membershipList}>\n {memberships.length ? (\n memberships.map((membership) => (\n <Typography key={membership.id} variant=\"body\">\n {readOrgUnitLabel(orgUnitsById.get(membership.orgUnitId))}\n {' / '}\n {membership.positionId\n ? readPositionLabel(\n positionsById.get(membership.positionId),\n )\n : '未指定職位'}\n {' / '}\n {membership.isPrimary ? '主要' : '一般'}\n </Typography>\n ))\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 尚未建立 BPM 組織歸屬\n </Typography>\n )}\n </div>\n </BaseCard>\n <BaseCard title=\"主管解析\">\n <Typography variant=\"body\">\n {resolvedManager?.managerMemberId\n ? readMemberLabel(managerProfile)\n : '尚未解析到主管'}\n </Typography>\n </BaseCard>\n </div>\n ) : null}\n </Modal>\n );\n}\n\nfunction readOrgUnitLabel(orgUnit: OrgUnitRecord | undefined): string {\n return orgUnit ? `${orgUnit.name} · ${orgUnit.code}` : '未知組織';\n}\n\nfunction readPositionLabel(position: PositionRecord | undefined): string {\n return position ? `${position.name} · ${position.code}` : '未知職位';\n}\n\nfunction readMemberLabel(member: MemberProfileRecord | null): string {\n return member ? `${member.name} · ${member.email}` : '主管資料尚未載入';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '讀取會員資料失敗。';\n}\n"],"mappings":"8gBCkDM,EAA2B,CAAC,GAAI,GAAI,EAAE,EAG5C,SAAgB,GAA+B,CAC7C,GAAM,CAAC,EAAc,IAAA,EAAA,EAAA,UACnB,IACF,EACM,CAAC,EAAmB,IAAA,EAAA,EAAA,UAExB,CAAC,CAAC,EACE,CAAC,EAAsB,IAAA,EAAA,EAAA,UACU,IAAI,EACrC,CAAC,EAAuB,IAAA,EAAA,EAAA,UACW,IAAI,EACvC,CAAC,EAAO,IAAA,EAAA,EAAA,UAAoC,IAAI,EAChD,CAAC,EAAS,IAAA,EAAA,EAAA,UAAuB,EAAI,EACrC,CAAC,EAAY,IAAA,EAAA,EAAA,UAA0B,CAAC,EACxC,CAAC,EAAgB,IAAA,EAAA,EAAA,UAA8B,EAAE,EACjD,CAAC,EAAkB,IAAA,EAAA,EAAA,UAAgC,CAAC,EACpD,CAAC,EAAS,IAAA,EAAA,EAAA,UAAuD,CAAC,CAAC,EACnE,CAAC,EAAU,IAAA,EAAA,EAAA,UAAkD,CAAC,CAAC,EAC/D,CAAC,EAAW,IAAA,EAAA,EAAA,UAAoD,CAAC,CAAC,EAClE,CAAC,EAAY,IAAA,EAAA,EAAA,UAA0B,EAAE,EAEzC,GAAA,EAAA,EAAA,aAEF,IAAI,IAAI,EAAS,IAAK,GAAY,CAAC,EAAQ,GAAI,CAAO,CAAC,CAAC,EAC1D,CAAC,CAAQ,CACX,EACM,GAAA,EAAA,EAAA,aAEF,IAAI,IAAI,EAAU,IAAK,GAAa,CAAC,EAAS,GAAI,CAAQ,CAAC,CAAC,EAC9D,CAAC,CAAS,CACZ,EAEM,GAAA,EAAA,EAAA,aAA6B,SAA2B,CAC5D,EAAW,EAAI,EACf,EAAS,IAAI,EAEb,GAAI,CACF,GAAM,CAAC,EAAkB,GAAgB,MAAM,QAAQ,IAAI,EAAA,EAAA,EAAA,yBACjC,CACtB,KAAM,EACN,SAAU,EACV,YACF,CAAC,GAAA,EAAA,EAAA,2BACyB,CAC5B,CAAC,EAED,EAAW,EAAiB,OAAO,EACnC,EAAoB,EAAiB,UAAU,EAC/C,EAAY,EAAa,QAAQ,EACjC,EAAa,EAAa,SAAS,CACrC,OAAS,EAAuB,CAC9B,EAAS,EAAiB,CAAY,CAAC,CACzC,QAAU,CACR,EAAW,EAAK,CAClB,CACF,EAAG,CAAC,EAAY,EAAgB,CAAU,CAAC,GAE3C,EAAA,EAAA,eAAsB,CACpB,EAAoB,CACtB,EAAG,CAAC,CAAc,CAAC,EAEnB,IAAM,GAAA,EAAA,EAAA,aAEF,EAAQ,IAAK,IAAY,CACvB,GAAG,EACH,IAAK,EAAO,QACd,EAAE,EACJ,CAAC,CAAO,CACV,EAEM,GAAA,EAAA,EAAA,aACJ,KAAO,IAA+C,CACpD,EAAgB,CAAM,EACtB,EAAwB,IAAI,EAC5B,EAAS,IAAI,EAEb,GAAI,CACF,GAAM,CAAC,EAAa,GAAmB,MAAM,QAAQ,IAAI,EAAA,EAAA,EAAA,iBACvC,CAAE,SAAU,EAAO,QAAS,CAAC,GAAA,EAAA,EAAA,qBACzB,EAAO,QAAQ,CACrC,CAAC,EAED,EAAqB,CAAW,EAChC,EAAyB,CAAe,EACxC,GAEI,MAAA,EAAA,EAAA,gBACE,EAAgB,gBACZ,CAAC,EAAgB,eAAe,EAChC,CAAC,CACP,GACA,IAAM,IACV,CACF,OAAS,EAAuB,CAC9B,EAAS,EAAiB,CAAY,CAAC,CACzC,CACF,EACA,CAAC,CACH,EAEM,GAAA,EAAA,EAAA,aAC4B,CAC9B,CAAE,UAAW,OAAQ,IAAK,OAAQ,MAAO,KAAM,MAAO,GAAI,EAC1D,CAAE,UAAW,QAAS,IAAK,QAAS,MAAO,KAAM,MAAO,GAAI,CAC9D,EACA,CAAC,CACH,EACM,GAAA,EAAA,EAAA,cAC4B,CAC9B,WAA6D,CAC3D,CACE,KAAM,KACN,QAAU,GAAiB,CACzB,EAAgB,CAAM,CACxB,CACF,CACF,EACA,QAAS,iBACT,MAAO,EACT,GACA,CAAC,CAAU,CACb,EAEA,SAAS,GAAoB,CAC3B,EAAgB,IAAI,EACpB,EAAwB,IAAI,EAC5B,EAAqB,CAAC,CAAC,EACvB,EAAyB,IAAI,CAC/B,CAEA,OACE,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,EACI,EAAA,EAAA,KAAC,EAAA,WAAD,CAAA,UACE,EAAA,EAAA,KAAC,EAAA,QAAD,CACE,YAAY,kDACZ,MAAM,MACP,CAAA,CACS,CAAA,GAEZ,EAAA,EAAA,KAAC,EAAA,aAAD,CAAA,UACE,EAAA,EAAA,MAAC,EAAA,QAAD,CACE,YACE,EAAA,EAAA,KAAC,EAAA,WAAD,CAAY,UAAW,EAAO,2BAC5B,EAAA,EAAA,KAAC,EAAA,WAAD,CAAA,UACE,EAAA,EAAA,KAAC,EAAA,OAAD,CAAQ,KAAM,YACZ,EAAA,EAAA,KAAC,EAAA,UAAD,CACE,UAAA,GACA,OAAQ,EAAA,gBAAgB,SACxB,KAAK,6BAEL,EAAA,EAAA,KAAC,EAAA,MAAD,CACE,UAAA,GACA,SACE,GACS,CACT,EAAc,EAAM,OAAO,KAAK,EAChC,EAAc,CAAC,CACjB,EACA,YAAY,UACZ,KAAK,MACL,MAAO,EACP,QAAQ,MACT,CAAA,CACQ,CAAA,CACL,CAAA,CACE,CAAA,CACF,CAAA,WA1BhB,CA6BG,GACC,EAAA,EAAA,KAAC,EAAA,WAAD,CAAY,MAAM,aAAa,QAAQ,gBACpC,CACS,CAAA,EACV,MACJ,EAAA,EAAA,KAAC,EAAA,MAAD,CACE,QAAS,EACA,UACT,WAAY,EACZ,UAAA,GACS,UACT,WAAY,CACV,QAAS,EACT,SAAW,GAAe,CACxB,EAAc,CAAI,CACpB,EACA,iBAAmB,GAAmB,CACpC,EAAc,CAAC,EACf,EAAkB,CAAQ,CAC5B,EACA,SAAU,EACV,cAAe,OACf,gBAAiB,EACjB,qBAAsB,EAAM,EAAI,IAC9B,MAAM,EAAK,GAAG,EAAG,OAAO,EAAM,IAChC,oBAAqB,GACrB,MAAO,CACT,CACD,CAAA,CACM,GACG,CAAA,GAEd,EAAA,EAAA,KAAC,EAAD,CACE,eAAgB,EAChB,OAAQ,EACR,YAAa,EACb,QAAS,EACK,eACC,gBACf,gBAAiB,CAClB,CAAA,CACD,CAAA,CAAA,CAER,CAEA,SAAS,EAAkB,CACzB,iBACA,SACA,cACA,UACA,eACA,gBACA,mBASe,CACf,OACE,EAAA,EAAA,KAAC,EAAA,MAAD,CACE,WAAW,KACX,YAAY,KACZ,UAAU,WACV,SAAU,EACD,UACT,UAAW,EACX,KAAM,EAAQ,EACd,gBAAA,GACA,gBAAA,GACA,KAAK,UACL,MAAO,GAAQ,MAAQ,gBAEtB,GACC,EAAA,EAAA,MAAC,MAAD,CAAK,UAAW,EAAO,sBAAvB,EACE,EAAA,EAAA,KAAC,EAAA,SAAD,CAAU,MAAM,iBACd,EAAA,EAAA,KAAC,MAAD,CAAK,UAAW,EAAO,wBACrB,EAAA,EAAA,MAAC,EAAA,WAAD,CAAY,QAAQ,gBAApB,CAA2B,MAAI,EAAO,KAAkB,GACrD,CAAA,CACG,CAAA,GACV,EAAA,EAAA,KAAC,EAAA,SAAD,CAAU,MAAM,qBACd,EAAA,EAAA,KAAC,MAAD,CAAK,UAAW,EAAO,wBACpB,EAAY,OACX,EAAY,IAAK,IACf,EAAA,EAAA,MAAC,EAAA,WAAD,CAAgC,QAAQ,gBAAxC,CACG,EAAiB,EAAa,IAAI,EAAW,SAAS,CAAC,EACvD,MACA,EAAW,WACR,EACE,EAAc,IAAI,EAAW,UAAU,CACzC,EACA,QACH,MACA,EAAW,UAAY,KAAO,IACrB,GAVK,EAAW,EAUhB,CACb,GAED,EAAA,EAAA,KAAC,EAAA,WAAD,CAAY,MAAM,eAAe,QAAQ,gBAAO,eAEpC,CAAA,CAEX,CAAA,CACG,CAAA,GACV,EAAA,EAAA,KAAC,EAAA,SAAD,CAAU,MAAM,iBACd,EAAA,EAAA,KAAC,EAAA,WAAD,CAAY,QAAQ,gBACjB,GAAiB,gBACd,EAAgB,CAAc,EAC9B,SACM,CAAA,CACJ,CAAA,CACP,IACH,IACC,CAAA,CAEX,CAEA,SAAS,EAAiB,EAA4C,CACpE,OAAO,EAAU,GAAG,EAAQ,KAAK,KAAK,EAAQ,OAAS,MACzD,CAEA,SAAS,EAAkB,EAA8C,CACvE,OAAO,EAAW,GAAG,EAAS,KAAK,KAAK,EAAS,OAAS,MAC5D,CAEA,SAAS,EAAgB,EAA4C,CACnE,OAAO,EAAS,GAAG,EAAO,KAAK,KAAK,EAAO,QAAU,UACvD,CAEA,SAAS,EAAiB,EAAwB,CAChD,OAAO,aAAiB,MAAQ,EAAM,QAAU,WAClD"}
|
|
1
|
+
{"version":3,"file":"users-C9LA-Rd7.cjs","names":[],"sources":["../../src/views/admin/users/users.module.scss","../../src/views/admin/users/AdminUsersView.tsx"],"sourcesContent":[".memberFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n\n :global(.mzn-input),\n :global(.mzn-input-container),\n :global(.mzn-text-field) {\n width: 100%;\n min-width: 0 !important;\n }\n}\n\n.header {\n display: grid;\n gap: 4px;\n padding: 24px 24px 0;\n}\n\n.detailFields {\n display: grid;\n gap: 16px;\n}\n\n.detailSection {\n display: grid;\n gap: 8px;\n}\n\n.membershipList {\n display: grid;\n gap: 8px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n ReactElement,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n BaseCard,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport styles from './users.module.scss';\nimport {\n listMemberDirectoryPage,\n MemberProfileRecord,\n resolveMembers,\n} from '@rytass/bpm-core-client';\nimport {\n listMemberships,\n MembershipRecord,\n OrgUnitRecord,\n PositionRecord,\n readOrganizationDashboard,\n readResolvedManager,\n ResolvedManagerRecord,\n} from '@rytass/bpm-core-client/organization';\n\ntype MemberRow = Readonly<\n Record<string, unknown> &\n MemberProfileRecord & {\n key: string;\n }\n>;\n\nconst MEMBER_PAGE_SIZE_OPTIONS = [10, 20, 50];\n\n\nexport function AdminUsersView(): ReactElement {\n const [detailMember, setDetailMember] = useState<MemberProfileRecord | null>(\n null,\n );\n const [detailMemberships, setDetailMemberships] = useState<\n readonly MembershipRecord[]\n >([]);\n const [detailManagerProfile, setDetailManagerProfile] =\n useState<MemberProfileRecord | null>(null);\n const [detailResolvedManager, setDetailResolvedManager] =\n useState<ResolvedManagerRecord | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [memberPage, setMemberPage] = useState(1);\n const [memberPageSize, setMemberPageSize] = useState(10);\n const [memberTotalCount, setMemberTotalCount] = useState(0);\n const [members, setMembers] = useState<readonly MemberProfileRecord[]>([]);\n const [orgUnits, setOrgUnits] = useState<readonly OrgUnitRecord[]>([]);\n const [positions, setPositions] = useState<readonly PositionRecord[]>([]);\n const [searchText, setSearchText] = useState('');\n\n const orgUnitsById = useMemo(\n (): ReadonlyMap<string, OrgUnitRecord> =>\n new Map(orgUnits.map((orgUnit) => [orgUnit.id, orgUnit])),\n [orgUnits],\n );\n const positionsById = useMemo(\n (): ReadonlyMap<string, PositionRecord> =>\n new Map(positions.map((position) => [position.id, position])),\n [positions],\n );\n\n const refreshMembers = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const [memberPageResult, organization] = await Promise.all([\n listMemberDirectoryPage({\n page: memberPage,\n pageSize: memberPageSize,\n searchText,\n }),\n readOrganizationDashboard(),\n ]);\n\n setMembers(memberPageResult.members);\n setMemberTotalCount(memberPageResult.totalCount);\n setOrgUnits(organization.orgUnits);\n setPositions(organization.positions);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [memberPage, memberPageSize, searchText]);\n\n useEffect((): void => {\n void refreshMembers();\n }, [refreshMembers]);\n\n const rows = useMemo(\n (): MemberRow[] =>\n members.map((member) => ({\n ...member,\n key: member.memberId,\n })),\n [members],\n );\n\n const openDetail = useCallback(\n async (member: MemberProfileRecord): Promise<void> => {\n setDetailMember(member);\n setDetailManagerProfile(null);\n setError(null);\n\n try {\n const [memberships, resolvedManager] = await Promise.all([\n listMemberships({ memberId: member.memberId }),\n readResolvedManager(member.memberId),\n ]);\n\n setDetailMemberships(memberships);\n setDetailResolvedManager(resolvedManager);\n setDetailManagerProfile(\n (\n await resolveMembers(\n resolvedManager.managerMemberId\n ? [resolvedManager.managerMemberId]\n : [],\n )\n )[0] ?? null,\n );\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n }\n },\n [],\n );\n\n const columns = useMemo(\n (): TableColumn<MemberRow>[] => [\n { dataIndex: 'name', key: 'name', title: '姓名', width: 160 },\n { dataIndex: 'email', key: 'email', title: '信箱', width: 260 },\n ],\n [],\n );\n const tableActions = useMemo(\n (): TableActions<MemberRow> => ({\n render: (): ReturnType<TableActions<MemberRow>['render']> => [\n {\n name: '檢視',\n onClick: (record): void => {\n void openDetail(record);\n },\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [openDetail],\n );\n\n function closeDetail(): void {\n setDetailMember(null);\n setDetailManagerProfile(null);\n setDetailMemberships([]);\n setDetailResolvedManager(null);\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"會員資料由 host member resolver 提供,BPM 僅維護組織歸屬與主管解析。\"\n title=\"會員對照\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.memberFilterArea}>\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"memberSearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setMemberPage(1);\n }}\n placeholder=\"搜尋姓名或信箱\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n loading={loading}\n pagination={{\n current: memberPage,\n onChange: (page): void => {\n setMemberPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setMemberPage(1);\n setMemberPageSize(pageSize);\n },\n pageSize: memberPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: MEMBER_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: memberTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n\n <MemberDetailModal\n managerProfile={detailManagerProfile}\n member={detailMember}\n memberships={detailMemberships}\n onClose={closeDetail}\n orgUnitsById={orgUnitsById}\n positionsById={positionsById}\n resolvedManager={detailResolvedManager}\n />\n </>\n );\n}\n\nfunction MemberDetailModal({\n managerProfile,\n member,\n memberships,\n onClose,\n orgUnitsById,\n positionsById,\n resolvedManager,\n}: {\n readonly managerProfile: MemberProfileRecord | null;\n readonly member: MemberProfileRecord | null;\n readonly memberships: readonly MembershipRecord[];\n readonly onClose: () => void;\n readonly orgUnitsById: ReadonlyMap<string, OrgUnitRecord>;\n readonly positionsById: ReadonlyMap<string, PositionRecord>;\n readonly resolvedManager: ResolvedManagerRecord | null;\n}): ReactElement {\n return (\n <Modal\n cancelText=\"關閉\"\n confirmText=\"關閉\"\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={onClose}\n open={Boolean(member)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={member?.name ?? '會員明細'}\n >\n {member ? (\n <div className={styles.detailFields}>\n <BaseCard title=\"基本資料\">\n <div className={styles.detailSection}>\n <Typography variant=\"body\">信箱:{member.email}</Typography>\n </div>\n </BaseCard>\n <BaseCard title=\"BPM 組織歸屬\">\n <div className={styles.membershipList}>\n {memberships.length ? (\n memberships.map((membership) => (\n <Typography key={membership.id} variant=\"body\">\n {readOrgUnitLabel(orgUnitsById.get(membership.orgUnitId))}\n {' / '}\n {membership.positionId\n ? readPositionLabel(\n positionsById.get(membership.positionId),\n )\n : '未指定職位'}\n {' / '}\n {membership.isPrimary ? '主要' : '一般'}\n </Typography>\n ))\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 尚未建立 BPM 組織歸屬\n </Typography>\n )}\n </div>\n </BaseCard>\n <BaseCard title=\"主管解析\">\n <Typography variant=\"body\">\n {resolvedManager?.managerMemberId\n ? readMemberLabel(managerProfile)\n : '尚未解析到主管'}\n </Typography>\n </BaseCard>\n </div>\n ) : null}\n </Modal>\n );\n}\n\nfunction readOrgUnitLabel(orgUnit: OrgUnitRecord | undefined): string {\n return orgUnit ? `${orgUnit.name} · ${orgUnit.code}` : '未知組織';\n}\n\nfunction readPositionLabel(position: PositionRecord | undefined): string {\n return position ? `${position.name} · ${position.code}` : '未知職位';\n}\n\nfunction readMemberLabel(member: MemberProfileRecord | null): string {\n return member ? `${member.name} · ${member.email}` : '主管資料尚未載入';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '讀取會員資料失敗。';\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users-FWrb3ehW.js","names":[],"sources":["../../src/views/admin/users/users.module.scss","../../src/views/admin/users/AdminUsersView.tsx"],"sourcesContent":[".memberFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n\n :global(.mzn-input),\n :global(.mzn-input-container),\n :global(.mzn-text-field) {\n width: 100%;\n min-width: 0 !important;\n }\n}\n\n.header {\n display: grid;\n gap: 4px;\n padding: 24px 24px 0;\n}\n\n.detailFields {\n display: grid;\n gap: 16px;\n}\n\n.detailSection {\n display: grid;\n gap: 8px;\n}\n\n.membershipList {\n display: grid;\n gap: 8px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n ReactElement,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n BaseCard,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport styles from './users.module.scss';\nimport {\n listMemberDirectoryPage,\n MemberProfileRecord,\n resolveMembers,\n} from '@rytass/bpm-core-client';\nimport {\n listMemberships,\n MembershipRecord,\n OrgUnitRecord,\n PositionRecord,\n readOrganizationDashboard,\n readResolvedManager,\n ResolvedManagerRecord,\n} from '@rytass/bpm-core-client/organization';\n\ntype MemberRow = Readonly<\n Record<string, unknown> &\n MemberProfileRecord & {\n key: string;\n }\n>;\n\nconst MEMBER_PAGE_SIZE_OPTIONS = [10, 20, 50];\n\n\nexport function AdminUsersView(): ReactElement {\n const [detailMember, setDetailMember] = useState<MemberProfileRecord | null>(\n null,\n );\n const [detailMemberships, setDetailMemberships] = useState<\n readonly MembershipRecord[]\n >([]);\n const [detailManagerProfile, setDetailManagerProfile] =\n useState<MemberProfileRecord | null>(null);\n const [detailResolvedManager, setDetailResolvedManager] =\n useState<ResolvedManagerRecord | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [memberPage, setMemberPage] = useState(1);\n const [memberPageSize, setMemberPageSize] = useState(10);\n const [memberTotalCount, setMemberTotalCount] = useState(0);\n const [members, setMembers] = useState<readonly MemberProfileRecord[]>([]);\n const [orgUnits, setOrgUnits] = useState<readonly OrgUnitRecord[]>([]);\n const [positions, setPositions] = useState<readonly PositionRecord[]>([]);\n const [searchText, setSearchText] = useState('');\n\n const orgUnitsById = useMemo(\n (): ReadonlyMap<string, OrgUnitRecord> =>\n new Map(orgUnits.map((orgUnit) => [orgUnit.id, orgUnit])),\n [orgUnits],\n );\n const positionsById = useMemo(\n (): ReadonlyMap<string, PositionRecord> =>\n new Map(positions.map((position) => [position.id, position])),\n [positions],\n );\n\n const refreshMembers = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const [memberPageResult, organization] = await Promise.all([\n listMemberDirectoryPage({\n page: memberPage,\n pageSize: memberPageSize,\n searchText,\n }),\n readOrganizationDashboard(),\n ]);\n\n setMembers(memberPageResult.members);\n setMemberTotalCount(memberPageResult.totalCount);\n setOrgUnits(organization.orgUnits);\n setPositions(organization.positions);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [memberPage, memberPageSize, searchText]);\n\n useEffect((): void => {\n void refreshMembers();\n }, [refreshMembers]);\n\n const rows = useMemo(\n (): MemberRow[] =>\n members.map((member) => ({\n ...member,\n key: member.memberId,\n })),\n [members],\n );\n\n const openDetail = useCallback(\n async (member: MemberProfileRecord): Promise<void> => {\n setDetailMember(member);\n setDetailManagerProfile(null);\n setError(null);\n\n try {\n const [memberships, resolvedManager] = await Promise.all([\n listMemberships({ memberId: member.memberId }),\n readResolvedManager(member.memberId),\n ]);\n\n setDetailMemberships(memberships);\n setDetailResolvedManager(resolvedManager);\n setDetailManagerProfile(\n (\n await resolveMembers(\n resolvedManager.managerMemberId\n ? [resolvedManager.managerMemberId]\n : [],\n )\n )[0] ?? null,\n );\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n }\n },\n [],\n );\n\n const columns = useMemo(\n (): TableColumn<MemberRow>[] => [\n { dataIndex: 'name', key: 'name', title: '姓名', width: 160 },\n { dataIndex: 'email', key: 'email', title: '信箱', width: 260 },\n ],\n [],\n );\n const tableActions = useMemo(\n (): TableActions<MemberRow> => ({\n render: (): ReturnType<TableActions<MemberRow>['render']> => [\n {\n name: '檢視',\n onClick: (record): void => {\n void openDetail(record);\n },\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [openDetail],\n );\n\n function closeDetail(): void {\n setDetailMember(null);\n setDetailManagerProfile(null);\n setDetailMemberships([]);\n setDetailResolvedManager(null);\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"會員資料由 host member resolver 提供,BPM 僅維護組織歸屬與主管解析。\"\n title=\"會員對照\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.memberFilterArea}>\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"memberSearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setMemberPage(1);\n }}\n placeholder=\"搜尋姓名或信箱\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n loading={loading}\n pagination={{\n current: memberPage,\n onChange: (page): void => {\n setMemberPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setMemberPage(1);\n setMemberPageSize(pageSize);\n },\n pageSize: memberPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: MEMBER_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: memberTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n\n <MemberDetailModal\n managerProfile={detailManagerProfile}\n member={detailMember}\n memberships={detailMemberships}\n onClose={closeDetail}\n orgUnitsById={orgUnitsById}\n positionsById={positionsById}\n resolvedManager={detailResolvedManager}\n />\n </>\n );\n}\n\nfunction MemberDetailModal({\n managerProfile,\n member,\n memberships,\n onClose,\n orgUnitsById,\n positionsById,\n resolvedManager,\n}: {\n readonly managerProfile: MemberProfileRecord | null;\n readonly member: MemberProfileRecord | null;\n readonly memberships: readonly MembershipRecord[];\n readonly onClose: () => void;\n readonly orgUnitsById: ReadonlyMap<string, OrgUnitRecord>;\n readonly positionsById: ReadonlyMap<string, PositionRecord>;\n readonly resolvedManager: ResolvedManagerRecord | null;\n}): ReactElement {\n return (\n <Modal\n cancelText=\"關閉\"\n confirmText=\"關閉\"\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={onClose}\n open={Boolean(member)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={member?.name ?? '會員明細'}\n >\n {member ? (\n <div className={styles.detailFields}>\n <BaseCard title=\"基本資料\">\n <div className={styles.detailSection}>\n <Typography variant=\"body\">信箱:{member.email}</Typography>\n </div>\n </BaseCard>\n <BaseCard title=\"BPM 組織歸屬\">\n <div className={styles.membershipList}>\n {memberships.length ? (\n memberships.map((membership) => (\n <Typography key={membership.id} variant=\"body\">\n {readOrgUnitLabel(orgUnitsById.get(membership.orgUnitId))}\n {' / '}\n {membership.positionId\n ? readPositionLabel(\n positionsById.get(membership.positionId),\n )\n : '未指定職位'}\n {' / '}\n {membership.isPrimary ? '主要' : '一般'}\n </Typography>\n ))\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 尚未建立 BPM 組織歸屬\n </Typography>\n )}\n </div>\n </BaseCard>\n <BaseCard title=\"主管解析\">\n <Typography variant=\"body\">\n {resolvedManager?.managerMemberId\n ? readMemberLabel(managerProfile)\n : '尚未解析到主管'}\n </Typography>\n </BaseCard>\n </div>\n ) : null}\n </Modal>\n );\n}\n\nfunction readOrgUnitLabel(orgUnit: OrgUnitRecord | undefined): string {\n return orgUnit ? `${orgUnit.name} · ${orgUnit.code}` : '未知組織';\n}\n\nfunction readPositionLabel(position: PositionRecord | undefined): string {\n return position ? `${position.name} · ${position.code}` : '未知職位';\n}\n\nfunction readMemberLabel(member: MemberProfileRecord | null): string {\n return member ? `${member.name} · ${member.email}` : '主管資料尚未載入';\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":"users-FWrb3ehW.js","names":[],"sources":["../../src/views/admin/users/users.module.scss","../../src/views/admin/users/AdminUsersView.tsx"],"sourcesContent":[".memberFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n\n :global(.mzn-input),\n :global(.mzn-input-container),\n :global(.mzn-text-field) {\n width: 100%;\n min-width: 0 !important;\n }\n}\n\n.header {\n display: grid;\n gap: 4px;\n padding: 24px 24px 0;\n}\n\n.detailFields {\n display: grid;\n gap: 16px;\n}\n\n.detailSection {\n display: grid;\n gap: 8px;\n}\n\n.membershipList {\n display: grid;\n gap: 8px;\n}\n","'use client';\n\nimport {\n ChangeEvent,\n ReactElement,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'react';\nimport {\n BaseCard,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport styles from './users.module.scss';\nimport {\n listMemberDirectoryPage,\n MemberProfileRecord,\n resolveMembers,\n} from '@rytass/bpm-core-client';\nimport {\n listMemberships,\n MembershipRecord,\n OrgUnitRecord,\n PositionRecord,\n readOrganizationDashboard,\n readResolvedManager,\n ResolvedManagerRecord,\n} from '@rytass/bpm-core-client/organization';\n\ntype MemberRow = Readonly<\n Record<string, unknown> &\n MemberProfileRecord & {\n key: string;\n }\n>;\n\nconst MEMBER_PAGE_SIZE_OPTIONS = [10, 20, 50];\n\n\nexport function AdminUsersView(): ReactElement {\n const [detailMember, setDetailMember] = useState<MemberProfileRecord | null>(\n null,\n );\n const [detailMemberships, setDetailMemberships] = useState<\n readonly MembershipRecord[]\n >([]);\n const [detailManagerProfile, setDetailManagerProfile] =\n useState<MemberProfileRecord | null>(null);\n const [detailResolvedManager, setDetailResolvedManager] =\n useState<ResolvedManagerRecord | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [memberPage, setMemberPage] = useState(1);\n const [memberPageSize, setMemberPageSize] = useState(10);\n const [memberTotalCount, setMemberTotalCount] = useState(0);\n const [members, setMembers] = useState<readonly MemberProfileRecord[]>([]);\n const [orgUnits, setOrgUnits] = useState<readonly OrgUnitRecord[]>([]);\n const [positions, setPositions] = useState<readonly PositionRecord[]>([]);\n const [searchText, setSearchText] = useState('');\n\n const orgUnitsById = useMemo(\n (): ReadonlyMap<string, OrgUnitRecord> =>\n new Map(orgUnits.map((orgUnit) => [orgUnit.id, orgUnit])),\n [orgUnits],\n );\n const positionsById = useMemo(\n (): ReadonlyMap<string, PositionRecord> =>\n new Map(positions.map((position) => [position.id, position])),\n [positions],\n );\n\n const refreshMembers = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const [memberPageResult, organization] = await Promise.all([\n listMemberDirectoryPage({\n page: memberPage,\n pageSize: memberPageSize,\n searchText,\n }),\n readOrganizationDashboard(),\n ]);\n\n setMembers(memberPageResult.members);\n setMemberTotalCount(memberPageResult.totalCount);\n setOrgUnits(organization.orgUnits);\n setPositions(organization.positions);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [memberPage, memberPageSize, searchText]);\n\n useEffect((): void => {\n void refreshMembers();\n }, [refreshMembers]);\n\n const rows = useMemo(\n (): MemberRow[] =>\n members.map((member) => ({\n ...member,\n key: member.memberId,\n })),\n [members],\n );\n\n const openDetail = useCallback(\n async (member: MemberProfileRecord): Promise<void> => {\n setDetailMember(member);\n setDetailManagerProfile(null);\n setError(null);\n\n try {\n const [memberships, resolvedManager] = await Promise.all([\n listMemberships({ memberId: member.memberId }),\n readResolvedManager(member.memberId),\n ]);\n\n setDetailMemberships(memberships);\n setDetailResolvedManager(resolvedManager);\n setDetailManagerProfile(\n (\n await resolveMembers(\n resolvedManager.managerMemberId\n ? [resolvedManager.managerMemberId]\n : [],\n )\n )[0] ?? null,\n );\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n }\n },\n [],\n );\n\n const columns = useMemo(\n (): TableColumn<MemberRow>[] => [\n { dataIndex: 'name', key: 'name', title: '姓名', width: 160 },\n { dataIndex: 'email', key: 'email', title: '信箱', width: 260 },\n ],\n [],\n );\n const tableActions = useMemo(\n (): TableActions<MemberRow> => ({\n render: (): ReturnType<TableActions<MemberRow>['render']> => [\n {\n name: '檢視',\n onClick: (record): void => {\n void openDetail(record);\n },\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [openDetail],\n );\n\n function closeDetail(): void {\n setDetailMember(null);\n setDetailManagerProfile(null);\n setDetailMemberships([]);\n setDetailResolvedManager(null);\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"會員資料由 host member resolver 提供,BPM 僅維護組織歸屬與主管解析。\"\n title=\"會員對照\"\n />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.memberFilterArea}>\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"memberSearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setMemberPage(1);\n }}\n placeholder=\"搜尋姓名或信箱\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n loading={loading}\n pagination={{\n current: memberPage,\n onChange: (page): void => {\n setMemberPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setMemberPage(1);\n setMemberPageSize(pageSize);\n },\n pageSize: memberPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: MEMBER_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: memberTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n\n <MemberDetailModal\n managerProfile={detailManagerProfile}\n member={detailMember}\n memberships={detailMemberships}\n onClose={closeDetail}\n orgUnitsById={orgUnitsById}\n positionsById={positionsById}\n resolvedManager={detailResolvedManager}\n />\n </>\n );\n}\n\nfunction MemberDetailModal({\n managerProfile,\n member,\n memberships,\n onClose,\n orgUnitsById,\n positionsById,\n resolvedManager,\n}: {\n readonly managerProfile: MemberProfileRecord | null;\n readonly member: MemberProfileRecord | null;\n readonly memberships: readonly MembershipRecord[];\n readonly onClose: () => void;\n readonly orgUnitsById: ReadonlyMap<string, OrgUnitRecord>;\n readonly positionsById: ReadonlyMap<string, PositionRecord>;\n readonly resolvedManager: ResolvedManagerRecord | null;\n}): ReactElement {\n return (\n <Modal\n cancelText=\"關閉\"\n confirmText=\"關閉\"\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={onClose}\n open={Boolean(member)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={member?.name ?? '會員明細'}\n >\n {member ? (\n <div className={styles.detailFields}>\n <BaseCard title=\"基本資料\">\n <div className={styles.detailSection}>\n <Typography variant=\"body\">信箱:{member.email}</Typography>\n </div>\n </BaseCard>\n <BaseCard title=\"BPM 組織歸屬\">\n <div className={styles.membershipList}>\n {memberships.length ? (\n memberships.map((membership) => (\n <Typography key={membership.id} variant=\"body\">\n {readOrgUnitLabel(orgUnitsById.get(membership.orgUnitId))}\n {' / '}\n {membership.positionId\n ? readPositionLabel(\n positionsById.get(membership.positionId),\n )\n : '未指定職位'}\n {' / '}\n {membership.isPrimary ? '主要' : '一般'}\n </Typography>\n ))\n ) : (\n <Typography color=\"text-neutral\" variant=\"body\">\n 尚未建立 BPM 組織歸屬\n </Typography>\n )}\n </div>\n </BaseCard>\n <BaseCard title=\"主管解析\">\n <Typography variant=\"body\">\n {resolvedManager?.managerMemberId\n ? readMemberLabel(managerProfile)\n : '尚未解析到主管'}\n </Typography>\n </BaseCard>\n </div>\n ) : null}\n </Modal>\n );\n}\n\nfunction readOrgUnitLabel(orgUnit: OrgUnitRecord | undefined): string {\n return orgUnit ? `${orgUnit.name} · ${orgUnit.code}` : '未知組織';\n}\n\nfunction readPositionLabel(position: PositionRecord | undefined): string {\n return position ? `${position.name} · ${position.code}` : '未知職位';\n}\n\nfunction readMemberLabel(member: MemberProfileRecord | null): string {\n return member ? `${member.name} · ${member.email}` : '主管資料尚未載入';\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '讀取會員資料失敗。';\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});require('./index.css');const e=require("./chunks/router-adapter--gYs13E8.cjs"),t=require("./chunks/auth-provider-4BeCw7cI.cjs"),n=require("./chunks/format-date-time-
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});require('./index.css');const e=require("./chunks/router-adapter--gYs13E8.cjs"),t=require("./chunks/auth-provider-4BeCw7cI.cjs"),n=require("./chunks/format-date-time-D-cx5M8z.cjs"),r=require("./chunks/providers-BsEn3ikK.cjs"),i=require("./chunks/routes-config-fDVHmvXi.cjs"),a=require("./chunks/bpm-form-field-Bc6k4ZEO.cjs"),o=require("./chunks/admin-pickers-Btvij1at.cjs"),s=require("./chunks/approval-instance-list-page-CWiQvnfE.cjs"),c=require("./chunks/dashboard-page-CH87L5nx.cjs");let l=require("@mezzanine-ui/react"),u=require("react/jsx-runtime"),d=require("@mezzanine-ui/icons");function f(){let{logout:e}=t.n();return e}function p(){let{member:e}=t.n();return e}var m={root:`bpm_root_jl5S-`,badge:`bpm_badge_SIKJk`};function h({label:e=`通知中心`}={}){let{open:t}=r.o(),{unreadCount:n}=r.i(),i=n>0?`${e},${n} 則未讀`:e;return(0,u.jsxs)(`span`,{className:m.root,children:[(0,u.jsx)(l.NavigationIconButton,{"aria-label":i,icon:d.NotificationUnreadIcon,onClick:()=>{t()},title:e,type:`button`}),n>0?(0,u.jsx)(`span`,{className:m.badge,children:n>99?`99+`:n}):null]})}exports.ApprovalInstanceListPage=s.t,exports.AuthProvider=t.t,exports.BPMFormField=a.t,exports.BPMNotificationBellButton=h,exports.BPMRoutesProvider=i.t,exports.DashboardPage=c.t,exports.MemberPicker=o.t,exports.NotificationDrawer=r.n,exports.NotificationDrawerProvider=r.a,exports.NotificationUnreadProvider=r.r,exports.OrgUnitPicker=o.n,exports.PositionPicker=o.r,exports.Providers=r.t,exports.RouterAdapterProvider=e.t,exports.createDefaultBPMRoutes=i.n,exports.defaultBrowserSearchParams=e.n,exports.formatDateTime=n.t,exports.readMemberOption=o.i,exports.readOrgUnitOption=o.a,exports.readPositionOption=o.o,exports.useAuth=t.n,exports.useBPMLogout=f,exports.useBPMMember=p,exports.useBPMRoutes=i.r,exports.useNotificationDrawer=r.o,exports.useNotificationUnread=r.i,exports.useRouterAdapter=e.r;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/lib/use-bpm-logout.ts","../src/lib/use-bpm-member.ts","../src/components/bpm-notification-bell-button.module.scss","../src/components/bpm-notification-bell-button.tsx"],"sourcesContent":["'use client';\n\nimport { useAuth } from './auth-provider';\n\n/**\n * Drop-in logout handler for host navigation menus. Wraps the BPM auth\n * context's logout flow — calls `logoutApi()` against the BPM REST\n * session endpoint, clears the in-memory member, then redirects to the\n * configured `loginPath`.\n *\n * Hosts mount this on their own logout buttons / menu items so they do\n * not need to touch `useAuth()` directly. To customize the post-logout\n * destination, override the `loginPath` prop on `<BPMNextProviders>` or\n * `<AuthProvider>`.\n */\nexport function useBPMLogout(): () => Promise<void> {\n const { logout } = useAuth();\n return logout;\n}\n","'use client';\n\nimport type { ApiMember } from '@rytass/bpm-core-client';\nimport { useAuth } from './auth-provider';\n\n/**\n * Read the currently authenticated BPM member. Returns `null` when there\n * is no active session — typically only seen on the login page or during\n * the brief loading window before `<AuthProvider>` resolves the cookie.\n *\n * Convenience alias for `useAuth().member` aimed at host navigations\n * (avatar, display name, role-based menu visibility) that should not\n * depend on the broader `useAuth()` surface.\n */\nexport function useBPMMember(): ApiMember | null {\n const { member } = useAuth();\n return member;\n}\n",".root {\n display: inline-flex;\n position: relative;\n}\n\n.badge {\n align-items: center;\n background: #d92d20;\n border: 1px solid #fff;\n border-radius: 999px;\n color: #fff;\n display: inline-flex;\n font-size: 10px;\n font-weight: 600;\n height: 16px;\n justify-content: center;\n line-height: 1;\n min-width: 16px;\n padding: 0 4px;\n pointer-events: none;\n position: absolute;\n right: -4px;\n top: -4px;\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport { NavigationIconButton } from '@mezzanine-ui/react';\nimport { NotificationUnreadIcon } from '@mezzanine-ui/icons';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport styles from './bpm-notification-bell-button.module.scss';\n\nexport interface BPMNotificationBellButtonProps {\n /** Override the aria-label / tooltip. Defaults to `通知中心`. */\n readonly label?: string;\n}\n\n/**\n * Drop-in notification bell. Reads the unread count from\n * `<NotificationUnreadProvider>`, opens the BPM `<NotificationDrawer />`\n * mounted by `<Providers>` (or `<BPMNextProviders>`) on click, and\n * renders a small red badge with the unread count.\n *\n * Use this when the host navigation wants the BPM notification UX with\n * minimum wiring. Hosts that need a fully custom button can skip this\n * widget and consume `useNotificationDrawer()` + `useNotificationUnread()`\n * directly to wire their own trigger.\n *\n * Visual: uses Mezzanine `NavigationIconButton` so the bell aligns with\n * surrounding Mezzanine navigation chrome. The button is decoupled from\n * the `<Navigation>` container — it does not require a Mezzanine\n * navigation tree to render.\n */\nexport function BPMNotificationBellButton({\n label = '通知中心',\n}: BPMNotificationBellButtonProps = {}): ReactElement {\n const { open } = useNotificationDrawer();\n const { unreadCount } = useNotificationUnread();\n const ariaLabel = unreadCount > 0 ? `${label},${unreadCount} 則未讀` : label;\n return (\n <span className={styles.root}>\n <NavigationIconButton\n aria-label={ariaLabel}\n icon={NotificationUnreadIcon}\n onClick={(): void => {\n open();\n }}\n title={label}\n type=\"button\"\n />\n {unreadCount > 0 ? (\n <span className={styles.badge}>\n {unreadCount > 99 ? '99+' : unreadCount}\n </span>\n ) : null}\n </span>\n );\n}\n"],"mappings":"ooBAeA,SAAgB,GAAoC,CAClD,GAAM,CAAE,UAAW,EAAA,EAAQ,EAC3B,OAAO,CACT,CCJA,SAAgB,GAAiC,CAC/C,GAAM,CAAE,UAAW,EAAA,EAAQ,EAC3B,OAAO,CACT,uDEaA,SAAgB,EAA0B,CACxC,QAAQ,QAC0B,CAAC,EAAiB,CACpD,GAAM,CAAE,QAAS,EAAA,EAAsB,EACjC,CAAE,eAAgB,EAAA,EAAsB,EACxC,EAAY,EAAc,EAAI,GAAG,EAAM,GAAG,EAAY,MAAQ,EACpE,OACE,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/lib/use-bpm-logout.ts","../src/lib/use-bpm-member.ts","../src/components/bpm-notification-bell-button.module.scss","../src/components/bpm-notification-bell-button.tsx"],"sourcesContent":["'use client';\n\nimport { useAuth } from './auth-provider';\n\n/**\n * Drop-in logout handler for host navigation menus. Wraps the BPM auth\n * context's logout flow — calls `logoutApi()` against the BPM REST\n * session endpoint, clears the in-memory member, then redirects to the\n * configured `loginPath`.\n *\n * Hosts mount this on their own logout buttons / menu items so they do\n * not need to touch `useAuth()` directly. To customize the post-logout\n * destination, override the `loginPath` prop on `<BPMNextProviders>` or\n * `<AuthProvider>`.\n */\nexport function useBPMLogout(): () => Promise<void> {\n const { logout } = useAuth();\n return logout;\n}\n","'use client';\n\nimport type { ApiMember } from '@rytass/bpm-core-client';\nimport { useAuth } from './auth-provider';\n\n/**\n * Read the currently authenticated BPM member. Returns `null` when there\n * is no active session — typically only seen on the login page or during\n * the brief loading window before `<AuthProvider>` resolves the cookie.\n *\n * Convenience alias for `useAuth().member` aimed at host navigations\n * (avatar, display name, role-based menu visibility) that should not\n * depend on the broader `useAuth()` surface.\n */\nexport function useBPMMember(): ApiMember | null {\n const { member } = useAuth();\n return member;\n}\n",".root {\n display: inline-flex;\n position: relative;\n}\n\n.badge {\n align-items: center;\n background: #d92d20;\n border: 1px solid #fff;\n border-radius: 999px;\n color: #fff;\n display: inline-flex;\n font-size: 10px;\n font-weight: 600;\n height: 16px;\n justify-content: center;\n line-height: 1;\n min-width: 16px;\n padding: 0 4px;\n pointer-events: none;\n position: absolute;\n right: -4px;\n top: -4px;\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport { NavigationIconButton } from '@mezzanine-ui/react';\nimport { NotificationUnreadIcon } from '@mezzanine-ui/icons';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport styles from './bpm-notification-bell-button.module.scss';\n\nexport interface BPMNotificationBellButtonProps {\n /** Override the aria-label / tooltip. Defaults to `通知中心`. */\n readonly label?: string;\n}\n\n/**\n * Drop-in notification bell. Reads the unread count from\n * `<NotificationUnreadProvider>`, opens the BPM `<NotificationDrawer />`\n * mounted by `<Providers>` (or `<BPMNextProviders>`) on click, and\n * renders a small red badge with the unread count.\n *\n * Use this when the host navigation wants the BPM notification UX with\n * minimum wiring. Hosts that need a fully custom button can skip this\n * widget and consume `useNotificationDrawer()` + `useNotificationUnread()`\n * directly to wire their own trigger.\n *\n * Visual: uses Mezzanine `NavigationIconButton` so the bell aligns with\n * surrounding Mezzanine navigation chrome. The button is decoupled from\n * the `<Navigation>` container — it does not require a Mezzanine\n * navigation tree to render.\n */\nexport function BPMNotificationBellButton({\n label = '通知中心',\n}: BPMNotificationBellButtonProps = {}): ReactElement {\n const { open } = useNotificationDrawer();\n const { unreadCount } = useNotificationUnread();\n const ariaLabel = unreadCount > 0 ? `${label},${unreadCount} 則未讀` : label;\n return (\n <span className={styles.root}>\n <NavigationIconButton\n aria-label={ariaLabel}\n icon={NotificationUnreadIcon}\n onClick={(): void => {\n open();\n }}\n title={label}\n type=\"button\"\n />\n {unreadCount > 0 ? (\n <span className={styles.badge}>\n {unreadCount > 99 ? '99+' : unreadCount}\n </span>\n ) : null}\n </span>\n );\n}\n"],"mappings":"ooBAeA,SAAgB,GAAoC,CAClD,GAAM,CAAE,UAAW,EAAA,EAAQ,EAC3B,OAAO,CACT,CCJA,SAAgB,GAAiC,CAC/C,GAAM,CAAE,UAAW,EAAA,EAAQ,EAC3B,OAAO,CACT,uDEaA,SAAgB,EAA0B,CACxC,QAAQ,QAC0B,CAAC,EAAiB,CACpD,GAAM,CAAE,QAAS,EAAA,EAAsB,EACjC,CAAE,eAAgB,EAAA,EAAsB,EACxC,EAAY,EAAc,EAAI,GAAG,EAAM,GAAG,EAAY,MAAQ,EACpE,OACE,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAO,KAAxB,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,EAAA,qBAAD,CACE,aAAY,EACZ,KAAM,EAAA,uBACN,YAAqB,CACnB,EAAK,CACP,EACA,MAAO,EACP,KAAK,QACN,CAAA,EACA,EAAc,GACb,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAO,MACrB,SAAA,EAAc,GAAK,MAAQ,CACxB,CAAA,EACJ,IACA,GAEV"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { n as e, r as t, t as n } from "./chunks/router-adapter-DftlFTOd.js";
|
|
3
3
|
import { n as r, t as i } from "./chunks/auth-provider-B5oPmvk2.js";
|
|
4
4
|
import { t as a } from "./chunks/format-date-time-CB-LxzqT.js";
|
|
5
|
-
import { a as o, i as s, n as c, o as l, r as u, t as d } from "./chunks/providers-
|
|
5
|
+
import { a as o, i as s, n as c, o as l, r as u, t as d } from "./chunks/providers-DjLh4oLY.js";
|
|
6
6
|
import { n as f, r as p, t as m } from "./chunks/routes-config-RBYQtUd0.js";
|
|
7
7
|
import { t as h } from "./chunks/bpm-form-field-Cao0rMol.js";
|
|
8
8
|
import { a as g, i as _, n as v, o as y, r as b, t as x } from "./chunks/admin-pickers-DLlG_1du.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/lib/use-bpm-logout.ts","../src/lib/use-bpm-member.ts","../src/components/bpm-notification-bell-button.module.scss","../src/components/bpm-notification-bell-button.tsx"],"sourcesContent":["'use client';\n\nimport { useAuth } from './auth-provider';\n\n/**\n * Drop-in logout handler for host navigation menus. Wraps the BPM auth\n * context's logout flow — calls `logoutApi()` against the BPM REST\n * session endpoint, clears the in-memory member, then redirects to the\n * configured `loginPath`.\n *\n * Hosts mount this on their own logout buttons / menu items so they do\n * not need to touch `useAuth()` directly. To customize the post-logout\n * destination, override the `loginPath` prop on `<BPMNextProviders>` or\n * `<AuthProvider>`.\n */\nexport function useBPMLogout(): () => Promise<void> {\n const { logout } = useAuth();\n return logout;\n}\n","'use client';\n\nimport type { ApiMember } from '@rytass/bpm-core-client';\nimport { useAuth } from './auth-provider';\n\n/**\n * Read the currently authenticated BPM member. Returns `null` when there\n * is no active session — typically only seen on the login page or during\n * the brief loading window before `<AuthProvider>` resolves the cookie.\n *\n * Convenience alias for `useAuth().member` aimed at host navigations\n * (avatar, display name, role-based menu visibility) that should not\n * depend on the broader `useAuth()` surface.\n */\nexport function useBPMMember(): ApiMember | null {\n const { member } = useAuth();\n return member;\n}\n",".root {\n display: inline-flex;\n position: relative;\n}\n\n.badge {\n align-items: center;\n background: #d92d20;\n border: 1px solid #fff;\n border-radius: 999px;\n color: #fff;\n display: inline-flex;\n font-size: 10px;\n font-weight: 600;\n height: 16px;\n justify-content: center;\n line-height: 1;\n min-width: 16px;\n padding: 0 4px;\n pointer-events: none;\n position: absolute;\n right: -4px;\n top: -4px;\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport { NavigationIconButton } from '@mezzanine-ui/react';\nimport { NotificationUnreadIcon } from '@mezzanine-ui/icons';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport styles from './bpm-notification-bell-button.module.scss';\n\nexport interface BPMNotificationBellButtonProps {\n /** Override the aria-label / tooltip. Defaults to `通知中心`. */\n readonly label?: string;\n}\n\n/**\n * Drop-in notification bell. Reads the unread count from\n * `<NotificationUnreadProvider>`, opens the BPM `<NotificationDrawer />`\n * mounted by `<Providers>` (or `<BPMNextProviders>`) on click, and\n * renders a small red badge with the unread count.\n *\n * Use this when the host navigation wants the BPM notification UX with\n * minimum wiring. Hosts that need a fully custom button can skip this\n * widget and consume `useNotificationDrawer()` + `useNotificationUnread()`\n * directly to wire their own trigger.\n *\n * Visual: uses Mezzanine `NavigationIconButton` so the bell aligns with\n * surrounding Mezzanine navigation chrome. The button is decoupled from\n * the `<Navigation>` container — it does not require a Mezzanine\n * navigation tree to render.\n */\nexport function BPMNotificationBellButton({\n label = '通知中心',\n}: BPMNotificationBellButtonProps = {}): ReactElement {\n const { open } = useNotificationDrawer();\n const { unreadCount } = useNotificationUnread();\n const ariaLabel = unreadCount > 0 ? `${label},${unreadCount} 則未讀` : label;\n return (\n <span className={styles.root}>\n <NavigationIconButton\n aria-label={ariaLabel}\n icon={NotificationUnreadIcon}\n onClick={(): void => {\n open();\n }}\n title={label}\n type=\"button\"\n />\n {unreadCount > 0 ? (\n <span className={styles.badge}>\n {unreadCount > 99 ? '99+' : unreadCount}\n </span>\n ) : null}\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,SAAgB,IAAoC;CAClD,IAAM,EAAE,cAAW,EAAQ;CAC3B,OAAO;AACT;;;ACJA,SAAgB,IAAiC;CAC/C,IAAM,EAAE,cAAW,EAAQ;CAC3B,OAAO;AACT;;;;;;;AEaA,SAAgB,EAA0B,EACxC,WAAQ,WAC0B,CAAC,GAAiB;CACpD,IAAM,EAAE,YAAS,EAAsB,GACjC,EAAE,mBAAgB,EAAsB,GACxC,IAAY,IAAc,IAAI,GAAG,EAAM,GAAG,EAAY,QAAQ;CACpE,OACE,kBAAC,QAAD;EAAM,WAAW,EAAO;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/lib/use-bpm-logout.ts","../src/lib/use-bpm-member.ts","../src/components/bpm-notification-bell-button.module.scss","../src/components/bpm-notification-bell-button.tsx"],"sourcesContent":["'use client';\n\nimport { useAuth } from './auth-provider';\n\n/**\n * Drop-in logout handler for host navigation menus. Wraps the BPM auth\n * context's logout flow — calls `logoutApi()` against the BPM REST\n * session endpoint, clears the in-memory member, then redirects to the\n * configured `loginPath`.\n *\n * Hosts mount this on their own logout buttons / menu items so they do\n * not need to touch `useAuth()` directly. To customize the post-logout\n * destination, override the `loginPath` prop on `<BPMNextProviders>` or\n * `<AuthProvider>`.\n */\nexport function useBPMLogout(): () => Promise<void> {\n const { logout } = useAuth();\n return logout;\n}\n","'use client';\n\nimport type { ApiMember } from '@rytass/bpm-core-client';\nimport { useAuth } from './auth-provider';\n\n/**\n * Read the currently authenticated BPM member. Returns `null` when there\n * is no active session — typically only seen on the login page or during\n * the brief loading window before `<AuthProvider>` resolves the cookie.\n *\n * Convenience alias for `useAuth().member` aimed at host navigations\n * (avatar, display name, role-based menu visibility) that should not\n * depend on the broader `useAuth()` surface.\n */\nexport function useBPMMember(): ApiMember | null {\n const { member } = useAuth();\n return member;\n}\n",".root {\n display: inline-flex;\n position: relative;\n}\n\n.badge {\n align-items: center;\n background: #d92d20;\n border: 1px solid #fff;\n border-radius: 999px;\n color: #fff;\n display: inline-flex;\n font-size: 10px;\n font-weight: 600;\n height: 16px;\n justify-content: center;\n line-height: 1;\n min-width: 16px;\n padding: 0 4px;\n pointer-events: none;\n position: absolute;\n right: -4px;\n top: -4px;\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport { NavigationIconButton } from '@mezzanine-ui/react';\nimport { NotificationUnreadIcon } from '@mezzanine-ui/icons';\nimport { useNotificationDrawer } from '../lib/notification-drawer-provider';\nimport { useNotificationUnread } from '../lib/notification-unread-provider';\nimport styles from './bpm-notification-bell-button.module.scss';\n\nexport interface BPMNotificationBellButtonProps {\n /** Override the aria-label / tooltip. Defaults to `通知中心`. */\n readonly label?: string;\n}\n\n/**\n * Drop-in notification bell. Reads the unread count from\n * `<NotificationUnreadProvider>`, opens the BPM `<NotificationDrawer />`\n * mounted by `<Providers>` (or `<BPMNextProviders>`) on click, and\n * renders a small red badge with the unread count.\n *\n * Use this when the host navigation wants the BPM notification UX with\n * minimum wiring. Hosts that need a fully custom button can skip this\n * widget and consume `useNotificationDrawer()` + `useNotificationUnread()`\n * directly to wire their own trigger.\n *\n * Visual: uses Mezzanine `NavigationIconButton` so the bell aligns with\n * surrounding Mezzanine navigation chrome. The button is decoupled from\n * the `<Navigation>` container — it does not require a Mezzanine\n * navigation tree to render.\n */\nexport function BPMNotificationBellButton({\n label = '通知中心',\n}: BPMNotificationBellButtonProps = {}): ReactElement {\n const { open } = useNotificationDrawer();\n const { unreadCount } = useNotificationUnread();\n const ariaLabel = unreadCount > 0 ? `${label},${unreadCount} 則未讀` : label;\n return (\n <span className={styles.root}>\n <NavigationIconButton\n aria-label={ariaLabel}\n icon={NotificationUnreadIcon}\n onClick={(): void => {\n open();\n }}\n title={label}\n type=\"button\"\n />\n {unreadCount > 0 ? (\n <span className={styles.badge}>\n {unreadCount > 99 ? '99+' : unreadCount}\n </span>\n ) : null}\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAeA,SAAgB,IAAoC;CAClD,IAAM,EAAE,cAAW,EAAQ;CAC3B,OAAO;AACT;;;ACJA,SAAgB,IAAiC;CAC/C,IAAM,EAAE,cAAW,EAAQ;CAC3B,OAAO;AACT;;;;;;;AEaA,SAAgB,EAA0B,EACxC,WAAQ,WAC0B,CAAC,GAAiB;CACpD,IAAM,EAAE,YAAS,EAAsB,GACjC,EAAE,mBAAgB,EAAsB,GACxC,IAAY,IAAc,IAAI,GAAG,EAAM,GAAG,EAAY,QAAQ;CACpE,OACE,kBAAC,QAAD;EAAM,WAAW,EAAO;EAAxB,UAAA,CACE,kBAAC,GAAD;GACE,cAAY;GACZ,MAAM;GACN,eAAqB;IACnB,EAAK;GACP;GACA,OAAO;GACP,MAAK;EACN,CAAA,GACA,IAAc,IACb,kBAAC,QAAD;GAAM,WAAW,EAAO;GACrB,UAAA,IAAc,KAAK,QAAQ;EACxB,CAAA,IACJ,IACA;;AAEV"}
|
package/dist/next/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../chunks/router-adapter--gYs13E8.cjs"),t=require("../chunks/providers-
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../chunks/router-adapter--gYs13E8.cjs"),t=require("../chunks/providers-BsEn3ikK.cjs"),n=require("../chunks/routes-config-fDVHmvXi.cjs");let r=require("react"),i=require("react/jsx-runtime"),a=require("next/navigation");function o({children:n,locale:o,publicPaths:s,loginPath:c}){let l=(0,a.useRouter)(),u=(0,a.usePathname)(),d=(0,r.useMemo)(()=>({pathname:u,push:e=>l.push(e),replace:e=>l.replace(e),back:()=>l.back(),searchParams:()=>e.n()}),[l,u]);return(0,i.jsx)(e.t,{value:d,children:(0,i.jsx)(t.t,{locale:o,publicPaths:s,loginPath:c,children:n})})}exports.BPMNextProviders=o,exports.BPMRoutesProvider=n.t,exports.createDefaultBPMRoutes=n.n,exports.useBPMRoutes=n.r;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/next/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/next/BPMNextProviders.tsx"],"sourcesContent":["'use client';\n\n// One-line Next.js App Router shim for BPM. Reads `next/navigation`\n// router/pathname hooks, builds a `RouterAdapter`, then composes\n// `<RouterAdapterProvider>` and the shared `<BPMProviders>`.\n//\n// IMPORTANT: This component intentionally does NOT call\n// `useSearchParams()`. Doing so triggers Next.js's CSR bailout at static\n// prerender, and any `<Suspense fallback={null}>` we wrap around it then\n// erases `<RouterAdapterProvider>` from the SSR/hydration tree — leaving\n// every BPM view to throw \"must be used inside <RouterAdapterProvider>\"\n// when it mounts. None of the shipped BPM views consume\n// `RouterAdapter.searchParams()` anyway; hosts that need reactive query\n// string state should call Next's `useSearchParams()` directly in their\n// page (with their own Suspense). The lazy default getter below is\n// good enough for read-on-demand callers.\n\nimport { useMemo, type ReactElement, type ReactNode } from 'react';\nimport { usePathname, useRouter } from 'next/navigation';\nimport type { CalendarLocale } from '@mezzanine-ui/react/moment';\nimport { Providers as BPMProviders } from '../lib/providers';\nimport {\n RouterAdapterProvider,\n defaultBrowserSearchParams,\n type RouterAdapter,\n} from '../lib/router-adapter';\n\nexport interface BPMNextProvidersProps {\n readonly children: ReactNode;\n /**\n * Override the Mezzanine calendar locale. Forwarded to the inner\n * `<Providers>`. Defaults to `CalendarLocale.ZH_TW`.\n */\n readonly locale?: CalendarLocale;\n /**\n * Routes that must remain accessible without a BPM session — visiting\n * these does not redirect to `loginPath` even when `member` is null.\n * Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n *\n * Hosts that mount BPM under a non-root prefix (see\n * `<BPMRoutesProvider>`) should typically expand this to include their\n * own auth-bypass paths.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to redirect unauthenticated users. Forwarded to\n * `<AuthProvider>`. Defaults to `'/login'`. Hosts owning their own\n * login route (the recommended case) override this to their host\n * route, e.g. `'/auth/sign-in'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * One-line Next.js App Router shim for the full BPM provider stack.\n * Mounts in the host's root layout (or layout for the BPM sub-tree):\n *\n * ```tsx\n * import { BPMNextProviders } from '@rytass/bpm-core-react/next';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * <BPMNextProviders loginPath=\"/auth/sign-in\">\n * {children}\n * </BPMNextProviders>\n * </body></html>\n * );\n * }\n * ```\n *\n * Forwards every prop on {@link BPMNextProvidersProps} to the inner\n * `<Providers>` and `<AuthProvider>`. For per-feature path remapping\n * (BPM internal navigation), additionally wrap with\n * `<BPMRoutesProvider>` exported from the same subpath.\n */\nexport function BPMNextProviders({\n children,\n locale,\n publicPaths,\n loginPath,\n}: BPMNextProvidersProps): ReactElement {\n const nextRouter = useRouter();\n const pathname = usePathname();\n\n const adapter = useMemo<RouterAdapter>(\n () => ({\n pathname,\n push: (href: string): void => nextRouter.push(href),\n replace: (href: string): void => nextRouter.replace(href),\n back: (): void => nextRouter.back(),\n searchParams: (): URLSearchParams => defaultBrowserSearchParams(),\n }),\n [nextRouter, pathname],\n );\n\n return (\n <RouterAdapterProvider value={adapter}>\n <BPMProviders\n locale={locale}\n publicPaths={publicPaths}\n loginPath={loginPath}\n >\n {children}\n </BPMProviders>\n </RouterAdapterProvider>\n );\n}\n"],"mappings":"4TA4EA,SAAgB,EAAiB,CAC/B,WACA,SACA,cACA,aACsC,CACtC,IAAM,GAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/next/BPMNextProviders.tsx"],"sourcesContent":["'use client';\n\n// One-line Next.js App Router shim for BPM. Reads `next/navigation`\n// router/pathname hooks, builds a `RouterAdapter`, then composes\n// `<RouterAdapterProvider>` and the shared `<BPMProviders>`.\n//\n// IMPORTANT: This component intentionally does NOT call\n// `useSearchParams()`. Doing so triggers Next.js's CSR bailout at static\n// prerender, and any `<Suspense fallback={null}>` we wrap around it then\n// erases `<RouterAdapterProvider>` from the SSR/hydration tree — leaving\n// every BPM view to throw \"must be used inside <RouterAdapterProvider>\"\n// when it mounts. None of the shipped BPM views consume\n// `RouterAdapter.searchParams()` anyway; hosts that need reactive query\n// string state should call Next's `useSearchParams()` directly in their\n// page (with their own Suspense). The lazy default getter below is\n// good enough for read-on-demand callers.\n\nimport { useMemo, type ReactElement, type ReactNode } from 'react';\nimport { usePathname, useRouter } from 'next/navigation';\nimport type { CalendarLocale } from '@mezzanine-ui/react/moment';\nimport { Providers as BPMProviders } from '../lib/providers';\nimport {\n RouterAdapterProvider,\n defaultBrowserSearchParams,\n type RouterAdapter,\n} from '../lib/router-adapter';\n\nexport interface BPMNextProvidersProps {\n readonly children: ReactNode;\n /**\n * Override the Mezzanine calendar locale. Forwarded to the inner\n * `<Providers>`. Defaults to `CalendarLocale.ZH_TW`.\n */\n readonly locale?: CalendarLocale;\n /**\n * Routes that must remain accessible without a BPM session — visiting\n * these does not redirect to `loginPath` even when `member` is null.\n * Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n *\n * Hosts that mount BPM under a non-root prefix (see\n * `<BPMRoutesProvider>`) should typically expand this to include their\n * own auth-bypass paths.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to redirect unauthenticated users. Forwarded to\n * `<AuthProvider>`. Defaults to `'/login'`. Hosts owning their own\n * login route (the recommended case) override this to their host\n * route, e.g. `'/auth/sign-in'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * One-line Next.js App Router shim for the full BPM provider stack.\n * Mounts in the host's root layout (or layout for the BPM sub-tree):\n *\n * ```tsx\n * import { BPMNextProviders } from '@rytass/bpm-core-react/next';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * <BPMNextProviders loginPath=\"/auth/sign-in\">\n * {children}\n * </BPMNextProviders>\n * </body></html>\n * );\n * }\n * ```\n *\n * Forwards every prop on {@link BPMNextProvidersProps} to the inner\n * `<Providers>` and `<AuthProvider>`. For per-feature path remapping\n * (BPM internal navigation), additionally wrap with\n * `<BPMRoutesProvider>` exported from the same subpath.\n */\nexport function BPMNextProviders({\n children,\n locale,\n publicPaths,\n loginPath,\n}: BPMNextProvidersProps): ReactElement {\n const nextRouter = useRouter();\n const pathname = usePathname();\n\n const adapter = useMemo<RouterAdapter>(\n () => ({\n pathname,\n push: (href: string): void => nextRouter.push(href),\n replace: (href: string): void => nextRouter.replace(href),\n back: (): void => nextRouter.back(),\n searchParams: (): URLSearchParams => defaultBrowserSearchParams(),\n }),\n [nextRouter, pathname],\n );\n\n return (\n <RouterAdapterProvider value={adapter}>\n <BPMProviders\n locale={locale}\n publicPaths={publicPaths}\n loginPath={loginPath}\n >\n {children}\n </BPMProviders>\n </RouterAdapterProvider>\n );\n}\n"],"mappings":"4TA4EA,SAAgB,EAAiB,CAC/B,WACA,SACA,cACA,aACsC,CACtC,IAAM,GAAA,EAAa,EAAA,UAAA,CAAU,EACvB,GAAA,EAAW,EAAA,YAAA,CAAY,EAEvB,GAAA,EAAU,EAAA,QAAA,MACP,CACL,WACA,KAAO,GAAuB,EAAW,KAAK,CAAI,EAClD,QAAU,GAAuB,EAAW,QAAQ,CAAI,EACxD,SAAkB,EAAW,KAAK,EAClC,iBAAqC,EAAA,EAA2B,CAClE,GACA,CAAC,EAAY,CAAQ,CACvB,EAEA,OACE,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CAAuB,MAAO,EAC5B,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CACU,SACK,cACF,YAEV,UACW,CAAA,CACO,CAAA,CAE3B"}
|
package/dist/next/index.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { n as e, t } from "../chunks/router-adapter-DftlFTOd.js";
|
|
3
|
-
import { t as n } from "../chunks/providers-
|
|
3
|
+
import { t as n } from "../chunks/providers-DjLh4oLY.js";
|
|
4
4
|
import { n as r, r as i, t as a } from "../chunks/routes-config-RBYQtUd0.js";
|
|
5
5
|
import { useMemo as o } from "react";
|
|
6
6
|
import { jsx as s } from "react/jsx-runtime";
|
|
7
7
|
import { usePathname as c, useRouter as l } from "next/navigation";
|
|
8
8
|
//#region src/next/BPMNextProviders.tsx
|
|
9
9
|
function u({ children: r, locale: i, publicPaths: a, loginPath: u }) {
|
|
10
|
-
let d = l(), f = c()
|
|
10
|
+
let d = l(), f = c(), p = o(() => ({
|
|
11
|
+
pathname: f,
|
|
12
|
+
push: (e) => d.push(e),
|
|
13
|
+
replace: (e) => d.replace(e),
|
|
14
|
+
back: () => d.back(),
|
|
15
|
+
searchParams: () => e()
|
|
16
|
+
}), [d, f]);
|
|
11
17
|
return /* @__PURE__ */ s(t, {
|
|
12
|
-
value:
|
|
13
|
-
pathname: f,
|
|
14
|
-
push: (e) => d.push(e),
|
|
15
|
-
replace: (e) => d.replace(e),
|
|
16
|
-
back: () => d.back(),
|
|
17
|
-
searchParams: () => e()
|
|
18
|
-
}), [d, f]),
|
|
18
|
+
value: p,
|
|
19
19
|
children: /* @__PURE__ */ s(n, {
|
|
20
20
|
locale: i,
|
|
21
21
|
publicPaths: a,
|
package/dist/next/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/next/BPMNextProviders.tsx"],"sourcesContent":["'use client';\n\n// One-line Next.js App Router shim for BPM. Reads `next/navigation`\n// router/pathname hooks, builds a `RouterAdapter`, then composes\n// `<RouterAdapterProvider>` and the shared `<BPMProviders>`.\n//\n// IMPORTANT: This component intentionally does NOT call\n// `useSearchParams()`. Doing so triggers Next.js's CSR bailout at static\n// prerender, and any `<Suspense fallback={null}>` we wrap around it then\n// erases `<RouterAdapterProvider>` from the SSR/hydration tree — leaving\n// every BPM view to throw \"must be used inside <RouterAdapterProvider>\"\n// when it mounts. None of the shipped BPM views consume\n// `RouterAdapter.searchParams()` anyway; hosts that need reactive query\n// string state should call Next's `useSearchParams()` directly in their\n// page (with their own Suspense). The lazy default getter below is\n// good enough for read-on-demand callers.\n\nimport { useMemo, type ReactElement, type ReactNode } from 'react';\nimport { usePathname, useRouter } from 'next/navigation';\nimport type { CalendarLocale } from '@mezzanine-ui/react/moment';\nimport { Providers as BPMProviders } from '../lib/providers';\nimport {\n RouterAdapterProvider,\n defaultBrowserSearchParams,\n type RouterAdapter,\n} from '../lib/router-adapter';\n\nexport interface BPMNextProvidersProps {\n readonly children: ReactNode;\n /**\n * Override the Mezzanine calendar locale. Forwarded to the inner\n * `<Providers>`. Defaults to `CalendarLocale.ZH_TW`.\n */\n readonly locale?: CalendarLocale;\n /**\n * Routes that must remain accessible without a BPM session — visiting\n * these does not redirect to `loginPath` even when `member` is null.\n * Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n *\n * Hosts that mount BPM under a non-root prefix (see\n * `<BPMRoutesProvider>`) should typically expand this to include their\n * own auth-bypass paths.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to redirect unauthenticated users. Forwarded to\n * `<AuthProvider>`. Defaults to `'/login'`. Hosts owning their own\n * login route (the recommended case) override this to their host\n * route, e.g. `'/auth/sign-in'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * One-line Next.js App Router shim for the full BPM provider stack.\n * Mounts in the host's root layout (or layout for the BPM sub-tree):\n *\n * ```tsx\n * import { BPMNextProviders } from '@rytass/bpm-core-react/next';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * <BPMNextProviders loginPath=\"/auth/sign-in\">\n * {children}\n * </BPMNextProviders>\n * </body></html>\n * );\n * }\n * ```\n *\n * Forwards every prop on {@link BPMNextProvidersProps} to the inner\n * `<Providers>` and `<AuthProvider>`. For per-feature path remapping\n * (BPM internal navigation), additionally wrap with\n * `<BPMRoutesProvider>` exported from the same subpath.\n */\nexport function BPMNextProviders({\n children,\n locale,\n publicPaths,\n loginPath,\n}: BPMNextProvidersProps): ReactElement {\n const nextRouter = useRouter();\n const pathname = usePathname();\n\n const adapter = useMemo<RouterAdapter>(\n () => ({\n pathname,\n push: (href: string): void => nextRouter.push(href),\n replace: (href: string): void => nextRouter.replace(href),\n back: (): void => nextRouter.back(),\n searchParams: (): URLSearchParams => defaultBrowserSearchParams(),\n }),\n [nextRouter, pathname],\n );\n\n return (\n <RouterAdapterProvider value={adapter}>\n <BPMProviders\n locale={locale}\n publicPaths={publicPaths}\n loginPath={loginPath}\n >\n {children}\n </BPMProviders>\n </RouterAdapterProvider>\n );\n}\n"],"mappings":";;;;;;;;AA4EA,SAAgB,EAAiB,EAC/B,aACA,WACA,gBACA,gBACsC;CACtC,IAAM,IAAa,EAAU,GACvB,IAAW,EAAY
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/next/BPMNextProviders.tsx"],"sourcesContent":["'use client';\n\n// One-line Next.js App Router shim for BPM. Reads `next/navigation`\n// router/pathname hooks, builds a `RouterAdapter`, then composes\n// `<RouterAdapterProvider>` and the shared `<BPMProviders>`.\n//\n// IMPORTANT: This component intentionally does NOT call\n// `useSearchParams()`. Doing so triggers Next.js's CSR bailout at static\n// prerender, and any `<Suspense fallback={null}>` we wrap around it then\n// erases `<RouterAdapterProvider>` from the SSR/hydration tree — leaving\n// every BPM view to throw \"must be used inside <RouterAdapterProvider>\"\n// when it mounts. None of the shipped BPM views consume\n// `RouterAdapter.searchParams()` anyway; hosts that need reactive query\n// string state should call Next's `useSearchParams()` directly in their\n// page (with their own Suspense). The lazy default getter below is\n// good enough for read-on-demand callers.\n\nimport { useMemo, type ReactElement, type ReactNode } from 'react';\nimport { usePathname, useRouter } from 'next/navigation';\nimport type { CalendarLocale } from '@mezzanine-ui/react/moment';\nimport { Providers as BPMProviders } from '../lib/providers';\nimport {\n RouterAdapterProvider,\n defaultBrowserSearchParams,\n type RouterAdapter,\n} from '../lib/router-adapter';\n\nexport interface BPMNextProvidersProps {\n readonly children: ReactNode;\n /**\n * Override the Mezzanine calendar locale. Forwarded to the inner\n * `<Providers>`. Defaults to `CalendarLocale.ZH_TW`.\n */\n readonly locale?: CalendarLocale;\n /**\n * Routes that must remain accessible without a BPM session — visiting\n * these does not redirect to `loginPath` even when `member` is null.\n * Forwarded to `<AuthProvider>`. Defaults to `['/login']`.\n *\n * Hosts that mount BPM under a non-root prefix (see\n * `<BPMRoutesProvider>`) should typically expand this to include their\n * own auth-bypass paths.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to redirect unauthenticated users. Forwarded to\n * `<AuthProvider>`. Defaults to `'/login'`. Hosts owning their own\n * login route (the recommended case) override this to their host\n * route, e.g. `'/auth/sign-in'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * One-line Next.js App Router shim for the full BPM provider stack.\n * Mounts in the host's root layout (or layout for the BPM sub-tree):\n *\n * ```tsx\n * import { BPMNextProviders } from '@rytass/bpm-core-react/next';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html><body>\n * <BPMNextProviders loginPath=\"/auth/sign-in\">\n * {children}\n * </BPMNextProviders>\n * </body></html>\n * );\n * }\n * ```\n *\n * Forwards every prop on {@link BPMNextProvidersProps} to the inner\n * `<Providers>` and `<AuthProvider>`. For per-feature path remapping\n * (BPM internal navigation), additionally wrap with\n * `<BPMRoutesProvider>` exported from the same subpath.\n */\nexport function BPMNextProviders({\n children,\n locale,\n publicPaths,\n loginPath,\n}: BPMNextProvidersProps): ReactElement {\n const nextRouter = useRouter();\n const pathname = usePathname();\n\n const adapter = useMemo<RouterAdapter>(\n () => ({\n pathname,\n push: (href: string): void => nextRouter.push(href),\n replace: (href: string): void => nextRouter.replace(href),\n back: (): void => nextRouter.back(),\n searchParams: (): URLSearchParams => defaultBrowserSearchParams(),\n }),\n [nextRouter, pathname],\n );\n\n return (\n <RouterAdapterProvider value={adapter}>\n <BPMProviders\n locale={locale}\n publicPaths={publicPaths}\n loginPath={loginPath}\n >\n {children}\n </BPMProviders>\n </RouterAdapterProvider>\n );\n}\n"],"mappings":";;;;;;;;AA4EA,SAAgB,EAAiB,EAC/B,aACA,WACA,gBACA,gBACsC;CACtC,IAAM,IAAa,EAAU,GACvB,IAAW,EAAY,GAEvB,IAAU,SACP;EACL;EACA,OAAO,MAAuB,EAAW,KAAK,CAAI;EAClD,UAAU,MAAuB,EAAW,QAAQ,CAAI;EACxD,YAAkB,EAAW,KAAK;EAClC,oBAAqC,EAA2B;CAClE,IACA,CAAC,GAAY,CAAQ,CACvB;CAEA,OACE,kBAAC,GAAD;EAAuB,OAAO;EAC5B,UAAA,kBAAC,GAAD;GACU;GACK;GACF;GAEV;EACW,CAAA;CACO,CAAA;AAE3B"}
|
|
@@ -15,5 +15,5 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require
|
|
|
15
15
|
|
|
16
16
|
嚴格界線:
|
|
17
17
|
- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」
|
|
18
|
-
- 不要假裝執行你工具以外的動作,也不要編造結果。`;function o(){let t=n.WORKFLOW_TOOLSET.map(t=>[t.name,(0,e.tool)({description:t.description,inputSchema:(0,e.jsonSchema)(t.inputSchema)})]);return Object.fromEntries(t)}function s(t={}){let n=o();return async function(o){return(0,e.streamText)({messages:await(0,e.convertToModelMessages)([...
|
|
18
|
+
- 不要假裝執行你工具以外的動作,也不要編造結果。`;function o(){let t=n.WORKFLOW_TOOLSET.map(t=>[t.name,(0,e.tool)({description:t.description,inputSchema:(0,e.jsonSchema)(t.inputSchema)})]);return Object.fromEntries(t)}function s(t={}){let n=o();return async function(o){let s=await o.json();return(0,e.streamText)({messages:await(0,e.convertToModelMessages)([...s.messages]),model:i(t.model??process.env.BPM_LLM_MODEL??r),system:t.system??a,tools:n}).toUIMessageStreamResponse()}}exports.WORKFLOW_CHAT_SYSTEM_PROMPT=a,exports.buildWorkflowAiSdkTools=o,exports.createWorkflowChatPOST=s;
|
|
19
19
|
//# sourceMappingURL=workflow-chat-route.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-chat-route.cjs","names":[],"sources":["../../src/next/workflow-chat-route.ts"],"sourcesContent":["import {\n convertToModelMessages,\n jsonSchema,\n streamText,\n tool,\n type LanguageModel,\n type Tool,\n type ToolSet,\n type UIMessage,\n} from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { WORKFLOW_TOOLSET } from '@rytass/bpm-core-shared';\n\n/**\n * Next.js route handler for the workflow designer LLM assistant.\n *\n * The model is advertised the workflow {@link WORKFLOW_TOOLSET} but the tools\n * carry **no `execute`** — so every tool call is forwarded to the browser,\n * where `useChat`'s `onToolCall` runs it against the React designer controller\n * (see `use-workflow-chat.ts`). This route only holds the API key, runs the\n * model, and streams the tool-call plan back. It never touches the canvas.\n *\n * Host usage (apps/client/src/app/api/chat/route.ts):\n * export const runtime = 'nodejs';\n * export const POST = createWorkflowChatPOST();\n */\n\n/** Default OpenAI model id (overridable via BPM_LLM_MODEL). */\nconst DEFAULT_MODEL = 'gpt-5.4-mini';\n\n/**\n * Resolve the model via the OpenAI provider directly (reads `OPENAI_API_KEY`).\n * Accepts a bare id (`gpt-5.4-mini`) or a `openai/`-prefixed id; the prefix is\n * stripped. This deployment talks to OpenAI directly — no AI Gateway.\n */\nfunction resolveModel(setting: string): LanguageModel {\n return openai(setting.replace(/^openai\\//u, ''));\n}\n\n/**\n * Strict guardrail: the assistant may ONLY design this approval flow, using the\n * provided tools. Anything else is declined. Lives server-side so it cannot be\n * overridden by client-supplied messages.\n */\nexport const WORKFLOW_CHAT_SYSTEM_PROMPT = `你是「簽核流程設計器」頁面內建的 AI 助理,唯一職責是協助使用者在這個畫布上設計/修改一個簽核審批流程(workflow)。\n\n你只能透過系統提供的工具(tools)來操作流程:新增/重命名/刪除節點、連線、設定簽核人、設定條件分流、設定系統節點動作、自動排版、查詢現況與驗證等。除了這些工具,你沒有其他能力。\n\n工作守則:\n1. 動作前,先呼叫 get_workflow_snapshot 了解目前畫布的節點與連線,再規劃要呼叫哪些工具。\n2. 盡量用高階工具(insert_approval_step / insert_notification / insert_conditional_branch)一次完成多步;需要細修時再用細顆粒工具。\n3. **重要:要果斷、先把流程畫出來。** 使用者只給一兩句話時,直接依常見公司慣例建立初稿,不要反覆追問細節。簽核人若使用者沒指定,就省略 approverResolver(系統會自動用「直屬主管」為預設,不需要真實會員 id);通知節點同理可省略 action(預設站內通知)。先把結構建好、再請使用者調整。\n4. 工具呼叫不會因為「缺簽核人/缺資料」而失敗;缺的部分會以合理預設補上,並由 validate_workflow 標示。所以放心連續呼叫工具把流程建完,不要因為怕缺欄位就停下來問。\n5. 當使用者「點名」某個人或部門當簽核人/收件者時,先用 search_members(依姓名關鍵字)、list_org_units、list_positions 查到真實 id,再用 set_user_task_approver / set_service_action 指定(如 DIRECT memberIds、ORG_UNIT_MANAGER orgUnitId、POSITION positionId)。查不到就回報並請使用者確認,不要編造 id。\n6. 節點 id 可從 snapshot 取得;連線條件需要先綁定表單欄位(若 list_form_fields 為空,提醒使用者先綁定表單版本)。\n7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。\n8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。\n9. 全程使用繁體中文(台灣用語)回覆。\n\n嚴格界線:\n- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」\n- 不要假裝執行你工具以外的動作,也不要編造結果。`;\n\n/** Build AI SDK tools from WORKFLOW_TOOLSET — no `execute`, so the browser runs them. */\nexport function buildWorkflowAiSdkTools(): ToolSet {\n const entries: readonly (readonly [string, Tool])[] = WORKFLOW_TOOLSET.map(\n (workflowTool) =>\n [\n workflowTool.name,\n tool({\n description: workflowTool.description,\n inputSchema: jsonSchema(\n workflowTool.inputSchema as Parameters<typeof jsonSchema>[0],\n ),\n }),\n ] as const,\n );\n\n return Object.fromEntries(entries) as ToolSet;\n}\n\nexport interface WorkflowChatRouteOptions {\n /** Override the OpenAI model id (else BPM_LLM_MODEL env, else the default). */\n readonly model?: string;\n /** Override the system prompt (else the strict design-only guardrail). */\n readonly system?: string;\n}\n\ninterface WorkflowChatRequestBody {\n readonly messages: readonly UIMessage[];\n}\n\n/** Create the `POST` handler for the workflow chat route. */\nexport function createWorkflowChatPOST(\n options: WorkflowChatRouteOptions = {},\n): (request: Request) => Promise<Response> {\n const tools = buildWorkflowAiSdkTools();\n\n return async function POST(request: Request): Promise<Response> {\n const body = (await request.json()) as WorkflowChatRequestBody;\n const result = streamText({\n messages: await convertToModelMessages([...body.messages]),\n model: resolveModel(\n options.model ?? process.env.BPM_LLM_MODEL ?? DEFAULT_MODEL,\n ),\n system: options.system ?? WORKFLOW_CHAT_SYSTEM_PROMPT,\n tools,\n });\n\n return result.toUIMessageStreamResponse();\n };\n}\n"],"mappings":"wJA4BA,IAAM,EAAgB,eAOtB,SAAS,EAAa,EAAgC,CACpD,OAAA,
|
|
1
|
+
{"version":3,"file":"workflow-chat-route.cjs","names":[],"sources":["../../src/next/workflow-chat-route.ts"],"sourcesContent":["import {\n convertToModelMessages,\n jsonSchema,\n streamText,\n tool,\n type LanguageModel,\n type Tool,\n type ToolSet,\n type UIMessage,\n} from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { WORKFLOW_TOOLSET } from '@rytass/bpm-core-shared';\n\n/**\n * Next.js route handler for the workflow designer LLM assistant.\n *\n * The model is advertised the workflow {@link WORKFLOW_TOOLSET} but the tools\n * carry **no `execute`** — so every tool call is forwarded to the browser,\n * where `useChat`'s `onToolCall` runs it against the React designer controller\n * (see `use-workflow-chat.ts`). This route only holds the API key, runs the\n * model, and streams the tool-call plan back. It never touches the canvas.\n *\n * Host usage (apps/client/src/app/api/chat/route.ts):\n * export const runtime = 'nodejs';\n * export const POST = createWorkflowChatPOST();\n */\n\n/** Default OpenAI model id (overridable via BPM_LLM_MODEL). */\nconst DEFAULT_MODEL = 'gpt-5.4-mini';\n\n/**\n * Resolve the model via the OpenAI provider directly (reads `OPENAI_API_KEY`).\n * Accepts a bare id (`gpt-5.4-mini`) or a `openai/`-prefixed id; the prefix is\n * stripped. This deployment talks to OpenAI directly — no AI Gateway.\n */\nfunction resolveModel(setting: string): LanguageModel {\n return openai(setting.replace(/^openai\\//u, ''));\n}\n\n/**\n * Strict guardrail: the assistant may ONLY design this approval flow, using the\n * provided tools. Anything else is declined. Lives server-side so it cannot be\n * overridden by client-supplied messages.\n */\nexport const WORKFLOW_CHAT_SYSTEM_PROMPT = `你是「簽核流程設計器」頁面內建的 AI 助理,唯一職責是協助使用者在這個畫布上設計/修改一個簽核審批流程(workflow)。\n\n你只能透過系統提供的工具(tools)來操作流程:新增/重命名/刪除節點、連線、設定簽核人、設定條件分流、設定系統節點動作、自動排版、查詢現況與驗證等。除了這些工具,你沒有其他能力。\n\n工作守則:\n1. 動作前,先呼叫 get_workflow_snapshot 了解目前畫布的節點與連線,再規劃要呼叫哪些工具。\n2. 盡量用高階工具(insert_approval_step / insert_notification / insert_conditional_branch)一次完成多步;需要細修時再用細顆粒工具。\n3. **重要:要果斷、先把流程畫出來。** 使用者只給一兩句話時,直接依常見公司慣例建立初稿,不要反覆追問細節。簽核人若使用者沒指定,就省略 approverResolver(系統會自動用「直屬主管」為預設,不需要真實會員 id);通知節點同理可省略 action(預設站內通知)。先把結構建好、再請使用者調整。\n4. 工具呼叫不會因為「缺簽核人/缺資料」而失敗;缺的部分會以合理預設補上,並由 validate_workflow 標示。所以放心連續呼叫工具把流程建完,不要因為怕缺欄位就停下來問。\n5. 當使用者「點名」某個人或部門當簽核人/收件者時,先用 search_members(依姓名關鍵字)、list_org_units、list_positions 查到真實 id,再用 set_user_task_approver / set_service_action 指定(如 DIRECT memberIds、ORG_UNIT_MANAGER orgUnitId、POSITION positionId)。查不到就回報並請使用者確認,不要編造 id。\n6. 節點 id 可從 snapshot 取得;連線條件需要先綁定表單欄位(若 list_form_fields 為空,提醒使用者先綁定表單版本)。\n7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。\n8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。\n9. 全程使用繁體中文(台灣用語)回覆。\n\n嚴格界線:\n- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」\n- 不要假裝執行你工具以外的動作,也不要編造結果。`;\n\n/** Build AI SDK tools from WORKFLOW_TOOLSET — no `execute`, so the browser runs them. */\nexport function buildWorkflowAiSdkTools(): ToolSet {\n const entries: readonly (readonly [string, Tool])[] = WORKFLOW_TOOLSET.map(\n (workflowTool) =>\n [\n workflowTool.name,\n tool({\n description: workflowTool.description,\n inputSchema: jsonSchema(\n workflowTool.inputSchema as Parameters<typeof jsonSchema>[0],\n ),\n }),\n ] as const,\n );\n\n return Object.fromEntries(entries) as ToolSet;\n}\n\nexport interface WorkflowChatRouteOptions {\n /** Override the OpenAI model id (else BPM_LLM_MODEL env, else the default). */\n readonly model?: string;\n /** Override the system prompt (else the strict design-only guardrail). */\n readonly system?: string;\n}\n\ninterface WorkflowChatRequestBody {\n readonly messages: readonly UIMessage[];\n}\n\n/** Create the `POST` handler for the workflow chat route. */\nexport function createWorkflowChatPOST(\n options: WorkflowChatRouteOptions = {},\n): (request: Request) => Promise<Response> {\n const tools = buildWorkflowAiSdkTools();\n\n return async function POST(request: Request): Promise<Response> {\n const body = (await request.json()) as WorkflowChatRequestBody;\n const result = streamText({\n messages: await convertToModelMessages([...body.messages]),\n model: resolveModel(\n options.model ?? process.env.BPM_LLM_MODEL ?? DEFAULT_MODEL,\n ),\n system: options.system ?? WORKFLOW_CHAT_SYSTEM_PROMPT,\n tools,\n });\n\n return result.toUIMessageStreamResponse();\n };\n}\n"],"mappings":"wJA4BA,IAAM,EAAgB,eAOtB,SAAS,EAAa,EAAgC,CACpD,OAAA,EAAO,EAAA,OAAA,CAAO,EAAQ,QAAQ,aAAc,EAAE,CAAC,CACjD,CAOA,IAAa,EAA8B;;;;;;;;;;;;;;;;;2BAoB3C,SAAgB,GAAmC,CACjD,IAAM,EAAgD,EAAA,iBAAiB,IACpE,GACC,CACE,EAAa,MAAA,EACb,EAAA,KAAA,CAAK,CACH,YAAa,EAAa,YAC1B,aAAA,EAAa,EAAA,WAAA,CACX,EAAa,WACf,CACF,CAAC,CACH,CACJ,EAEA,OAAO,OAAO,YAAY,CAAO,CACnC,CAcA,SAAgB,EACd,EAAoC,CAAC,EACI,CACzC,IAAM,EAAQ,EAAwB,EAEtC,OAAO,eAAoB,EAAqC,CAC9D,IAAM,EAAQ,MAAM,EAAQ,KAAK,EAUjC,OAAA,EATe,EAAA,WAAA,CAAW,CACxB,SAAU,MAAA,EAAM,EAAA,uBAAA,CAAuB,CAAC,GAAG,EAAK,QAAQ,CAAC,EACzD,MAAO,EACL,EAAQ,OAAS,QAAQ,IAAI,eAAiB,CAChD,EACA,OAAQ,EAAQ,QAAU,EAC1B,OACF,CAEO,CAAA,CAAO,0BAA0B,CAC1C,CACF"}
|
|
@@ -17,8 +17,9 @@ function l() {
|
|
|
17
17
|
function u(t = {}) {
|
|
18
18
|
let r = l();
|
|
19
19
|
return async function(i) {
|
|
20
|
+
let a = await i.json();
|
|
20
21
|
return n({
|
|
21
|
-
messages: await e([...
|
|
22
|
+
messages: await e([...a.messages]),
|
|
22
23
|
model: s(t.model ?? process.env.BPM_LLM_MODEL ?? o),
|
|
23
24
|
system: t.system ?? c,
|
|
24
25
|
tools: r
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-chat-route.js","names":[],"sources":["../../src/next/workflow-chat-route.ts"],"sourcesContent":["import {\n convertToModelMessages,\n jsonSchema,\n streamText,\n tool,\n type LanguageModel,\n type Tool,\n type ToolSet,\n type UIMessage,\n} from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { WORKFLOW_TOOLSET } from '@rytass/bpm-core-shared';\n\n/**\n * Next.js route handler for the workflow designer LLM assistant.\n *\n * The model is advertised the workflow {@link WORKFLOW_TOOLSET} but the tools\n * carry **no `execute`** — so every tool call is forwarded to the browser,\n * where `useChat`'s `onToolCall` runs it against the React designer controller\n * (see `use-workflow-chat.ts`). This route only holds the API key, runs the\n * model, and streams the tool-call plan back. It never touches the canvas.\n *\n * Host usage (apps/client/src/app/api/chat/route.ts):\n * export const runtime = 'nodejs';\n * export const POST = createWorkflowChatPOST();\n */\n\n/** Default OpenAI model id (overridable via BPM_LLM_MODEL). */\nconst DEFAULT_MODEL = 'gpt-5.4-mini';\n\n/**\n * Resolve the model via the OpenAI provider directly (reads `OPENAI_API_KEY`).\n * Accepts a bare id (`gpt-5.4-mini`) or a `openai/`-prefixed id; the prefix is\n * stripped. This deployment talks to OpenAI directly — no AI Gateway.\n */\nfunction resolveModel(setting: string): LanguageModel {\n return openai(setting.replace(/^openai\\//u, ''));\n}\n\n/**\n * Strict guardrail: the assistant may ONLY design this approval flow, using the\n * provided tools. Anything else is declined. Lives server-side so it cannot be\n * overridden by client-supplied messages.\n */\nexport const WORKFLOW_CHAT_SYSTEM_PROMPT = `你是「簽核流程設計器」頁面內建的 AI 助理,唯一職責是協助使用者在這個畫布上設計/修改一個簽核審批流程(workflow)。\n\n你只能透過系統提供的工具(tools)來操作流程:新增/重命名/刪除節點、連線、設定簽核人、設定條件分流、設定系統節點動作、自動排版、查詢現況與驗證等。除了這些工具,你沒有其他能力。\n\n工作守則:\n1. 動作前,先呼叫 get_workflow_snapshot 了解目前畫布的節點與連線,再規劃要呼叫哪些工具。\n2. 盡量用高階工具(insert_approval_step / insert_notification / insert_conditional_branch)一次完成多步;需要細修時再用細顆粒工具。\n3. **重要:要果斷、先把流程畫出來。** 使用者只給一兩句話時,直接依常見公司慣例建立初稿,不要反覆追問細節。簽核人若使用者沒指定,就省略 approverResolver(系統會自動用「直屬主管」為預設,不需要真實會員 id);通知節點同理可省略 action(預設站內通知)。先把結構建好、再請使用者調整。\n4. 工具呼叫不會因為「缺簽核人/缺資料」而失敗;缺的部分會以合理預設補上,並由 validate_workflow 標示。所以放心連續呼叫工具把流程建完,不要因為怕缺欄位就停下來問。\n5. 當使用者「點名」某個人或部門當簽核人/收件者時,先用 search_members(依姓名關鍵字)、list_org_units、list_positions 查到真實 id,再用 set_user_task_approver / set_service_action 指定(如 DIRECT memberIds、ORG_UNIT_MANAGER orgUnitId、POSITION positionId)。查不到就回報並請使用者確認,不要編造 id。\n6. 節點 id 可從 snapshot 取得;連線條件需要先綁定表單欄位(若 list_form_fields 為空,提醒使用者先綁定表單版本)。\n7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。\n8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。\n9. 全程使用繁體中文(台灣用語)回覆。\n\n嚴格界線:\n- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」\n- 不要假裝執行你工具以外的動作,也不要編造結果。`;\n\n/** Build AI SDK tools from WORKFLOW_TOOLSET — no `execute`, so the browser runs them. */\nexport function buildWorkflowAiSdkTools(): ToolSet {\n const entries: readonly (readonly [string, Tool])[] = WORKFLOW_TOOLSET.map(\n (workflowTool) =>\n [\n workflowTool.name,\n tool({\n description: workflowTool.description,\n inputSchema: jsonSchema(\n workflowTool.inputSchema as Parameters<typeof jsonSchema>[0],\n ),\n }),\n ] as const,\n );\n\n return Object.fromEntries(entries) as ToolSet;\n}\n\nexport interface WorkflowChatRouteOptions {\n /** Override the OpenAI model id (else BPM_LLM_MODEL env, else the default). */\n readonly model?: string;\n /** Override the system prompt (else the strict design-only guardrail). */\n readonly system?: string;\n}\n\ninterface WorkflowChatRequestBody {\n readonly messages: readonly UIMessage[];\n}\n\n/** Create the `POST` handler for the workflow chat route. */\nexport function createWorkflowChatPOST(\n options: WorkflowChatRouteOptions = {},\n): (request: Request) => Promise<Response> {\n const tools = buildWorkflowAiSdkTools();\n\n return async function POST(request: Request): Promise<Response> {\n const body = (await request.json()) as WorkflowChatRequestBody;\n const result = streamText({\n messages: await convertToModelMessages([...body.messages]),\n model: resolveModel(\n options.model ?? process.env.BPM_LLM_MODEL ?? DEFAULT_MODEL,\n ),\n system: options.system ?? WORKFLOW_CHAT_SYSTEM_PROMPT,\n tools,\n });\n\n return result.toUIMessageStreamResponse();\n };\n}\n"],"mappings":";;;;AA4BA,IAAM,IAAgB;AAOtB,SAAS,EAAa,GAAgC;CACpD,OAAO,EAAO,EAAQ,QAAQ,cAAc,EAAE,CAAC;AACjD;AAOA,IAAa,IAA8B;AAoB3C,SAAgB,IAAmC;CACjD,IAAM,IAAgD,EAAiB,KACpE,MACC,CACE,EAAa,MACb,EAAK;EACH,aAAa,EAAa;EAC1B,aAAa,EACX,EAAa,WACf;CACF,CAAC,CACH,CACJ;CAEA,OAAO,OAAO,YAAY,CAAO;AACnC;AAcA,SAAgB,EACd,IAAoC,CAAC,GACI;CACzC,IAAM,IAAQ,EAAwB;CAEtC,OAAO,eAAoB,GAAqC;
|
|
1
|
+
{"version":3,"file":"workflow-chat-route.js","names":[],"sources":["../../src/next/workflow-chat-route.ts"],"sourcesContent":["import {\n convertToModelMessages,\n jsonSchema,\n streamText,\n tool,\n type LanguageModel,\n type Tool,\n type ToolSet,\n type UIMessage,\n} from 'ai';\nimport { openai } from '@ai-sdk/openai';\nimport { WORKFLOW_TOOLSET } from '@rytass/bpm-core-shared';\n\n/**\n * Next.js route handler for the workflow designer LLM assistant.\n *\n * The model is advertised the workflow {@link WORKFLOW_TOOLSET} but the tools\n * carry **no `execute`** — so every tool call is forwarded to the browser,\n * where `useChat`'s `onToolCall` runs it against the React designer controller\n * (see `use-workflow-chat.ts`). This route only holds the API key, runs the\n * model, and streams the tool-call plan back. It never touches the canvas.\n *\n * Host usage (apps/client/src/app/api/chat/route.ts):\n * export const runtime = 'nodejs';\n * export const POST = createWorkflowChatPOST();\n */\n\n/** Default OpenAI model id (overridable via BPM_LLM_MODEL). */\nconst DEFAULT_MODEL = 'gpt-5.4-mini';\n\n/**\n * Resolve the model via the OpenAI provider directly (reads `OPENAI_API_KEY`).\n * Accepts a bare id (`gpt-5.4-mini`) or a `openai/`-prefixed id; the prefix is\n * stripped. This deployment talks to OpenAI directly — no AI Gateway.\n */\nfunction resolveModel(setting: string): LanguageModel {\n return openai(setting.replace(/^openai\\//u, ''));\n}\n\n/**\n * Strict guardrail: the assistant may ONLY design this approval flow, using the\n * provided tools. Anything else is declined. Lives server-side so it cannot be\n * overridden by client-supplied messages.\n */\nexport const WORKFLOW_CHAT_SYSTEM_PROMPT = `你是「簽核流程設計器」頁面內建的 AI 助理,唯一職責是協助使用者在這個畫布上設計/修改一個簽核審批流程(workflow)。\n\n你只能透過系統提供的工具(tools)來操作流程:新增/重命名/刪除節點、連線、設定簽核人、設定條件分流、設定系統節點動作、自動排版、查詢現況與驗證等。除了這些工具,你沒有其他能力。\n\n工作守則:\n1. 動作前,先呼叫 get_workflow_snapshot 了解目前畫布的節點與連線,再規劃要呼叫哪些工具。\n2. 盡量用高階工具(insert_approval_step / insert_notification / insert_conditional_branch)一次完成多步;需要細修時再用細顆粒工具。\n3. **重要:要果斷、先把流程畫出來。** 使用者只給一兩句話時,直接依常見公司慣例建立初稿,不要反覆追問細節。簽核人若使用者沒指定,就省略 approverResolver(系統會自動用「直屬主管」為預設,不需要真實會員 id);通知節點同理可省略 action(預設站內通知)。先把結構建好、再請使用者調整。\n4. 工具呼叫不會因為「缺簽核人/缺資料」而失敗;缺的部分會以合理預設補上,並由 validate_workflow 標示。所以放心連續呼叫工具把流程建完,不要因為怕缺欄位就停下來問。\n5. 當使用者「點名」某個人或部門當簽核人/收件者時,先用 search_members(依姓名關鍵字)、list_org_units、list_positions 查到真實 id,再用 set_user_task_approver / set_service_action 指定(如 DIRECT memberIds、ORG_UNIT_MANAGER orgUnitId、POSITION positionId)。查不到就回報並請使用者確認,不要編造 id。\n6. 節點 id 可從 snapshot 取得;連線條件需要先綁定表單欄位(若 list_form_fields 為空,提醒使用者先綁定表單版本)。\n7. **條件分流務必主動補上「其他情況」**:設計 exclusiveGateway(條件分流)時,除了符合條件的分支,一定要保留一條未設條件、標記為預設(isDefault)的「其他情況」路徑當 else(通常指向後續節點或完成)。不要把所有輸出連線都設成條件而沒有預設出口;缺預設出口時,用 set_edge_default 把其中一條設為預設。\n8. 完成後,呼叫 validate_workflow,並用一兩句話說明你建了什麼流程、哪些地方(如簽核人、條件門檻)建議使用者再確認或指定。\n9. 全程使用繁體中文(台灣用語)回覆。\n\n嚴格界線:\n- 只做「設計這個簽核流程」相關的事。任何與此無關的請求(閒聊、寫詩、寫程式、查天氣/新聞、操作其他系統或頁面、回答一般知識問題等)一律婉拒,並簡短引導使用者回到流程設計,例如:「我只能協助你設計這個簽核流程,需要我幫你新增關卡或設定條件嗎?」\n- 不要假裝執行你工具以外的動作,也不要編造結果。`;\n\n/** Build AI SDK tools from WORKFLOW_TOOLSET — no `execute`, so the browser runs them. */\nexport function buildWorkflowAiSdkTools(): ToolSet {\n const entries: readonly (readonly [string, Tool])[] = WORKFLOW_TOOLSET.map(\n (workflowTool) =>\n [\n workflowTool.name,\n tool({\n description: workflowTool.description,\n inputSchema: jsonSchema(\n workflowTool.inputSchema as Parameters<typeof jsonSchema>[0],\n ),\n }),\n ] as const,\n );\n\n return Object.fromEntries(entries) as ToolSet;\n}\n\nexport interface WorkflowChatRouteOptions {\n /** Override the OpenAI model id (else BPM_LLM_MODEL env, else the default). */\n readonly model?: string;\n /** Override the system prompt (else the strict design-only guardrail). */\n readonly system?: string;\n}\n\ninterface WorkflowChatRequestBody {\n readonly messages: readonly UIMessage[];\n}\n\n/** Create the `POST` handler for the workflow chat route. */\nexport function createWorkflowChatPOST(\n options: WorkflowChatRouteOptions = {},\n): (request: Request) => Promise<Response> {\n const tools = buildWorkflowAiSdkTools();\n\n return async function POST(request: Request): Promise<Response> {\n const body = (await request.json()) as WorkflowChatRequestBody;\n const result = streamText({\n messages: await convertToModelMessages([...body.messages]),\n model: resolveModel(\n options.model ?? process.env.BPM_LLM_MODEL ?? DEFAULT_MODEL,\n ),\n system: options.system ?? WORKFLOW_CHAT_SYSTEM_PROMPT,\n tools,\n });\n\n return result.toUIMessageStreamResponse();\n };\n}\n"],"mappings":";;;;AA4BA,IAAM,IAAgB;AAOtB,SAAS,EAAa,GAAgC;CACpD,OAAO,EAAO,EAAQ,QAAQ,cAAc,EAAE,CAAC;AACjD;AAOA,IAAa,IAA8B;AAoB3C,SAAgB,IAAmC;CACjD,IAAM,IAAgD,EAAiB,KACpE,MACC,CACE,EAAa,MACb,EAAK;EACH,aAAa,EAAa;EAC1B,aAAa,EACX,EAAa,WACf;CACF,CAAC,CACH,CACJ;CAEA,OAAO,OAAO,YAAY,CAAO;AACnC;AAcA,SAAgB,EACd,IAAoC,CAAC,GACI;CACzC,IAAM,IAAQ,EAAwB;CAEtC,OAAO,eAAoB,GAAqC;EAC9D,IAAM,IAAQ,MAAM,EAAQ,KAAK;EAUjC,OATe,EAAW;GACxB,UAAU,MAAM,EAAuB,CAAC,GAAG,EAAK,QAAQ,CAAC;GACzD,OAAO,EACL,EAAQ,SAAS,QAAQ,IAAI,iBAAiB,CAChD;GACA,QAAQ,EAAQ,UAAU;GAC1B;EACF,CAEO,CAAA,CAAO,0BAA0B;CAC1C;AACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/delegations-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/delegations-Bc9psOZ3.cjs");let t=require("react/jsx-runtime");var n={title:`代理設定 | BPM Admin`,description:`設定簽核代理規則,讓符合範圍的待簽任務自動改派給代理人。`};function r(){return(0,t.jsx)(e.t,{})}exports.default=r,exports.metadata=n;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/pages/admin/delegations/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { AdminDelegationsView } from '../../../views/admin/delegations';\n\nexport const metadata: Metadata = {\n title: '代理設定 | BPM Admin',\n description: '設定簽核代理規則,讓符合範圍的待簽任務自動改派給代理人。',\n};\n\nexport default function AdminDelegationsPage(): ReactElement {\n return <AdminDelegationsView />;\n}\n"],"mappings":"8LAIA,IAAa,EAAqB,CAChC,MAAO,mBACP,YAAa,8BACf,EAEA,SAAwB,GAAqC,CAC3D,OAAO,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/pages/admin/delegations/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { AdminDelegationsView } from '../../../views/admin/delegations';\n\nexport const metadata: Metadata = {\n title: '代理設定 | BPM Admin',\n description: '設定簽核代理規則,讓符合範圍的待簽任務自動改派給代理人。',\n};\n\nexport default function AdminDelegationsPage(): ReactElement {\n return <AdminDelegationsView />;\n}\n"],"mappings":"8LAIA,IAAa,EAAqB,CAChC,MAAO,mBACP,YAAa,8BACf,EAEA,SAAwB,GAAqC,CAC3D,OAAO,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CAAuB,CAAA,CAChC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/orgs-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/orgs-BefGgeyc.cjs");let t=require("react/jsx-runtime");var n={title:`組織管理 | BPM Admin`,description:`維護組織樹、職位、會員歸屬與簽核主管解析規則。`};function r(){return(0,t.jsx)(e.t,{})}exports.default=r,exports.metadata=n;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/pages/admin/orgs/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { AdminOrgsView } from '../../../views/admin/orgs';\n\nexport const metadata: Metadata = {\n title: '組織管理 | BPM Admin',\n description: '維護組織樹、職位、會員歸屬與簽核主管解析規則。',\n};\n\nexport default function AdminOrgsPage(): ReactElement {\n return <AdminOrgsView />;\n}\n"],"mappings":"uLAIA,IAAa,EAAqB,CAChC,MAAO,mBACP,YAAa,yBACf,EAEA,SAAwB,GAA8B,CACpD,OAAO,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/pages/admin/orgs/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { AdminOrgsView } from '../../../views/admin/orgs';\n\nexport const metadata: Metadata = {\n title: '組織管理 | BPM Admin',\n description: '維護組織樹、職位、會員歸屬與簽核主管解析規則。',\n};\n\nexport default function AdminOrgsPage(): ReactElement {\n return <AdminOrgsView />;\n}\n"],"mappings":"uLAIA,IAAa,EAAqB,CAChC,MAAO,mBACP,YAAa,yBACf,EAEA,SAAwB,GAA8B,CACpD,OAAO,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CAAgB,CAAA,CACzB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/users-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/users-C9LA-Rd7.cjs");let t=require("react/jsx-runtime");var n={title:`會員對照 | BPM Admin`,description:`檢視 BPM 內部會員組織歸屬與主管解析。`};function r(){return(0,t.jsx)(e.t,{})}exports.default=r,exports.metadata=n;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/pages/admin/users/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { AdminUsersView } from '../../../views/admin/users';\n\nexport const metadata: Metadata = {\n title: '會員對照 | BPM Admin',\n description: '檢視 BPM 內部會員組織歸屬與主管解析。',\n};\n\nexport default function AdminUsersPage(): ReactElement {\n return <AdminUsersView />;\n}\n"],"mappings":"wLAIA,IAAa,EAAqB,CAChC,MAAO,mBACP,YAAa,uBACf,EAEA,SAAwB,GAA+B,CACrD,OAAO,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/pages/admin/users/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { AdminUsersView } from '../../../views/admin/users';\n\nexport const metadata: Metadata = {\n title: '會員對照 | BPM Admin',\n description: '檢視 BPM 內部會員組織歸屬與主管解析。',\n};\n\nexport default function AdminUsersPage(): ReactElement {\n return <AdminUsersView />;\n}\n"],"mappings":"wLAIA,IAAa,EAAqB,CAChC,MAAO,mBACP,YAAa,uBACf,EAEA,SAAwB,GAA+B,CACrD,OAAO,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CAAiB,CAAA,CAC1B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/pages/cc/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { CcView } from '../../views/cc';\n\n/**\n * Next.js metadata for the BPM \"cc\" page. Consumers re-export this\n * alongside the default page component.\n */\nexport const metadata: Metadata = {\n title: '抄送給我 | BPM Admin',\n};\n\n/**\n * Drop-in Next.js App Router page for the BPM cc inbox. Usage in\n * `app/cc/page.tsx`:\n *\n * ```ts\n * export { default, metadata } from '@rytass/bpm-core-react/pages/cc';\n * ```\n */\nexport default function CcPage(): ReactElement {\n return <CcView />;\n}\n"],"mappings":"8KAQA,IAAa,EAAqB,CAChC,MAAO,kBACT,EAUA,SAAwB,GAAuB,CAC7C,OAAO,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/pages/cc/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { CcView } from '../../views/cc';\n\n/**\n * Next.js metadata for the BPM \"cc\" page. Consumers re-export this\n * alongside the default page component.\n */\nexport const metadata: Metadata = {\n title: '抄送給我 | BPM Admin',\n};\n\n/**\n * Drop-in Next.js App Router page for the BPM cc inbox. Usage in\n * `app/cc/page.tsx`:\n *\n * ```ts\n * export { default, metadata } from '@rytass/bpm-core-react/pages/cc';\n * ```\n */\nexport default function CcPage(): ReactElement {\n return <CcView />;\n}\n"],"mappings":"8KAQA,IAAa,EAAqB,CAChC,MAAO,kBACT,EAUA,SAAwB,GAAuB,CAC7C,OAAO,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CAAS,CAAA,CAClB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/pages/dashboard/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { DashboardView } from '../../views/dashboard';\n\n/**\n * Next.js metadata for the BPM dashboard page. Consumers re-export this\n * alongside the default page component.\n */\nexport const metadata: Metadata = {\n title: '工作台 | BPM Admin',\n};\n\n/**\n * Drop-in Next.js App Router page for the BPM dashboard. Usage in\n * `app/dashboard/page.tsx`:\n *\n * ```ts\n * export { default, metadata } from '@rytass/bpm-core-react/pages/dashboard';\n * ```\n */\nexport default function DashboardPage(): ReactElement {\n return <DashboardView />;\n}\n"],"mappings":"qLAQA,IAAa,EAAqB,CAChC,MAAO,iBACT,EAUA,SAAwB,GAA8B,CACpD,OAAO,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/pages/dashboard/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { DashboardView } from '../../views/dashboard';\n\n/**\n * Next.js metadata for the BPM dashboard page. Consumers re-export this\n * alongside the default page component.\n */\nexport const metadata: Metadata = {\n title: '工作台 | BPM Admin',\n};\n\n/**\n * Drop-in Next.js App Router page for the BPM dashboard. Usage in\n * `app/dashboard/page.tsx`:\n *\n * ```ts\n * export { default, metadata } from '@rytass/bpm-core-react/pages/dashboard';\n * ```\n */\nexport default function DashboardPage(): ReactElement {\n return <DashboardView />;\n}\n"],"mappings":"qLAQA,IAAa,EAAqB,CAChC,MAAO,iBACT,EAUA,SAAwB,GAA8B,CACpD,OAAO,EAAA,EAAA,IAAA,CAAC,EAAA,cAAD,CAAgB,CAAA,CACzB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../chunks/delegations-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../chunks/delegations-vpLtus9g.cjs");let t=require("react/jsx-runtime");var n={title:`我的代理 | BPM Admin`,description:`設定自己的簽核代理,讓指定期間內的新待簽任務自動交由代理人處理。`};function r(){return(0,t.jsx)(e.t,{})}exports.default=r,exports.metadata=n;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/pages/delegations/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { DelegationsView } from '../../views/delegations';\n\nexport const metadata: Metadata = {\n title: '我的代理 | BPM Admin',\n description: '設定自己的簽核代理,讓指定期間內的新待簽任務自動交由代理人處理。',\n};\n\nexport default function DelegationsPage(): ReactElement {\n return <DelegationsView />;\n}\n"],"mappings":"2LAIA,IAAa,EAAqB,CAChC,MAAO,mBACP,YAAa,kCACf,EAEA,SAAwB,GAAgC,CACtD,OAAO,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/pages/delegations/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { DelegationsView } from '../../views/delegations';\n\nexport const metadata: Metadata = {\n title: '我的代理 | BPM Admin',\n description: '設定自己的簽核代理,讓指定期間內的新待簽任務自動交由代理人處理。',\n};\n\nexport default function DelegationsPage(): ReactElement {\n return <DelegationsView />;\n}\n"],"mappings":"2LAIA,IAAa,EAAqB,CAChC,MAAO,mBACP,YAAa,kCACf,EAEA,SAAwB,GAAgC,CACtD,OAAO,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CAAkB,CAAA,CAC3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/pages/inbox/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { InboxView } from '../../views/inbox';\n\n/**\n * Next.js metadata for the BPM inbox page. Consumers re-export this\n * alongside the default page component.\n */\nexport const metadata: Metadata = {\n title: '我的待簽 | BPM Admin',\n};\n\n/**\n * Drop-in Next.js App Router page for the BPM inbox. Usage in\n * `app/inbox/page.tsx`:\n *\n * ```ts\n * export { default, metadata } from '@rytass/bpm-core-react/pages/inbox';\n * ```\n */\nexport default function InboxPage(): ReactElement {\n return <InboxView />;\n}\n"],"mappings":"iLAQA,IAAa,EAAqB,CAChC,MAAO,kBACT,EAUA,SAAwB,GAA0B,CAChD,OAAO,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/pages/inbox/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { InboxView } from '../../views/inbox';\n\n/**\n * Next.js metadata for the BPM inbox page. Consumers re-export this\n * alongside the default page component.\n */\nexport const metadata: Metadata = {\n title: '我的待簽 | BPM Admin',\n};\n\n/**\n * Drop-in Next.js App Router page for the BPM inbox. Usage in\n * `app/inbox/page.tsx`:\n *\n * ```ts\n * export { default, metadata } from '@rytass/bpm-core-react/pages/inbox';\n * ```\n */\nexport default function InboxPage(): ReactElement {\n return <InboxView />;\n}\n"],"mappings":"iLAQA,IAAa,EAAqB,CAChC,MAAO,kBACT,EAUA,SAAwB,GAA0B,CAChD,OAAO,EAAA,EAAA,IAAA,CAAC,EAAA,UAAD,CAAY,CAAA,CACrB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/detail-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../../../chunks/detail-Bp2Nv0cs.cjs");let t=require("react/jsx-runtime");var n={title:`案件詳情 | BPM Admin`};async function r({params:n}){let{id:r}=await n;return(0,t.jsx)(e.t,{instanceId:r})}exports.default=r,exports.metadata=n;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/pages/instances/detail/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { InstanceDetailView } from '../../../views/instances/detail';\n\n/**\n * Next.js metadata for the BPM instance detail page. Consumers re-export\n * this alongside the default page component.\n */\nexport const metadata: Metadata = {\n title: '案件詳情 | BPM Admin',\n};\n\n/**\n * Drop-in Next.js App Router page for the BPM instance detail screen.\n * Accepts the standard App Router `params: Promise<{ id: string }>` shape\n * and forwards the resolved `id` into the view as `instanceId`.\n *\n * Usage in `app/instances/[id]/page.tsx`:\n *\n * ```ts\n * export { default, metadata } from '@rytass/bpm-core-react/pages/instances/detail';\n * ```\n */\nexport default async function InstanceDetailPage({\n params,\n}: {\n readonly params: Promise<{ readonly id: string }>;\n}): Promise<ReactElement> {\n const { id } = await params;\n return <InstanceDetailView instanceId={id} />;\n}\n"],"mappings":"yLAQA,IAAa,EAAqB,CAChC,MAAO,kBACT,EAaA,eAA8B,EAAmB,CAC/C,UAGwB,CACxB,GAAM,CAAE,MAAO,MAAM,EACrB,OAAO,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/pages/instances/detail/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { InstanceDetailView } from '../../../views/instances/detail';\n\n/**\n * Next.js metadata for the BPM instance detail page. Consumers re-export\n * this alongside the default page component.\n */\nexport const metadata: Metadata = {\n title: '案件詳情 | BPM Admin',\n};\n\n/**\n * Drop-in Next.js App Router page for the BPM instance detail screen.\n * Accepts the standard App Router `params: Promise<{ id: string }>` shape\n * and forwards the resolved `id` into the view as `instanceId`.\n *\n * Usage in `app/instances/[id]/page.tsx`:\n *\n * ```ts\n * export { default, metadata } from '@rytass/bpm-core-react/pages/instances/detail';\n * ```\n */\nexport default async function InstanceDetailPage({\n params,\n}: {\n readonly params: Promise<{ readonly id: string }>;\n}): Promise<ReactElement> {\n const { id } = await params;\n return <InstanceDetailView instanceId={id} />;\n}\n"],"mappings":"yLAQA,IAAa,EAAqB,CAChC,MAAO,kBACT,EAaA,eAA8B,EAAmB,CAC/C,UAGwB,CACxB,GAAM,CAAE,MAAO,MAAM,EACrB,OAAO,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CAAoB,WAAY,CAAK,CAAA,CAC9C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/pages/instances/new/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { InstanceNewView } from '../../../views/instances/new';\n\n/**\n * Next.js metadata for the BPM \"launch approval\" page. Consumers re-export\n * this alongside the default page component.\n */\nexport const metadata: Metadata = {\n title: '發起簽核 | BPM Admin',\n};\n\n/**\n * Drop-in Next.js App Router page for the BPM launch screen. Usage in\n * `app/instances/new/page.tsx`:\n *\n * ```ts\n * export { default, metadata } from '@rytass/bpm-core-react/pages/instances/new';\n * ```\n */\nexport default async function InstanceNewPage({\n searchParams,\n}: {\n readonly searchParams: Promise<{ readonly templateId?: string }>;\n}): Promise<ReactElement> {\n const { templateId } = await searchParams;\n return <InstanceNewView templateId={templateId ?? null} />;\n}\n"],"mappings":"4LAQA,IAAa,EAAqB,CAChC,MAAO,kBACT,EAUA,eAA8B,EAAgB,CAC5C,gBAGwB,CACxB,GAAM,CAAE,cAAe,MAAM,EAC7B,OAAO,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../../src/pages/instances/new/index.tsx"],"sourcesContent":["import type { Metadata } from 'next';\nimport type { ReactElement } from 'react';\nimport { InstanceNewView } from '../../../views/instances/new';\n\n/**\n * Next.js metadata for the BPM \"launch approval\" page. Consumers re-export\n * this alongside the default page component.\n */\nexport const metadata: Metadata = {\n title: '發起簽核 | BPM Admin',\n};\n\n/**\n * Drop-in Next.js App Router page for the BPM launch screen. Usage in\n * `app/instances/new/page.tsx`:\n *\n * ```ts\n * export { default, metadata } from '@rytass/bpm-core-react/pages/instances/new';\n * ```\n */\nexport default async function InstanceNewPage({\n searchParams,\n}: {\n readonly searchParams: Promise<{ readonly templateId?: string }>;\n}): Promise<ReactElement> {\n const { templateId } = await searchParams;\n return <InstanceNewView templateId={templateId ?? null} />;\n}\n"],"mappings":"4LAQA,IAAa,EAAqB,CAChC,MAAO,kBACT,EAUA,eAA8B,EAAgB,CAC5C,gBAGwB,CACxB,GAAM,CAAE,cAAe,MAAM,EAC7B,OAAO,EAAA,EAAA,IAAA,CAAC,EAAA,gBAAD,CAAiB,WAAY,GAAc,IAAO,CAAA,CAC3D"}
|