@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":"approval-instance-list-page-C408vxE2.cjs","names":[],"sources":["../../src/components/approval-instance-list-page.module.scss","../../src/components/approval-instance-list-page.tsx"],"sourcesContent":[".instanceFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n","'use client';\n\nimport type { ChangeEvent, ReactElement } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n PageHeader,\n Section,\n SectionGroup,\n Select,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport {\n resolveMembers,\n type MemberProfileRecord,\n} from '@rytass/bpm-core-client';\nimport {\n listApprovalInstancesPage,\n readApprovalInstanceCaseTitle,\n type ApprovalInstanceRecord,\n type ApprovalInstanceState,\n type ApprovalInstanceView,\n} from '@rytass/bpm-core-client/workflow';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { formatDateTime } from '../lib/format-date-time';\nimport styles from './approval-instance-list-page.module.scss';\n\nexport interface ApprovalInstanceListPageProps {\n readonly defaultState: ApprovalInstanceState | null;\n readonly description: string;\n readonly emptyMessage: string;\n readonly searchPlaceholder: string;\n readonly title: string;\n readonly view: ApprovalInstanceView;\n}\n\ntype StateFilterOption = Readonly<{\n id: 'ALL' | ApprovalInstanceState;\n name: string;\n state: ApprovalInstanceState | null;\n}>;\n\ntype ApprovalInstanceRow = Readonly<\n Record<string, unknown> &\n ApprovalInstanceRecord & {\n caseTitle: string;\n key: string;\n stateLabel: string;\n }\n>;\n\nconst INSTANCE_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst STATE_FILTER_OPTIONS: readonly StateFilterOption[] = [\n { id: 'ALL', name: '全部狀態', state: null },\n { id: 'RUNNING', name: '進行中', state: 'RUNNING' },\n { id: 'APPROVED', name: '已通過', state: 'APPROVED' },\n { id: 'REJECTED', name: '已拒絕', state: 'REJECTED' },\n { id: 'RETURNED', name: '已退回', state: 'RETURNED' },\n { id: 'CANCELLED', name: '已取消', state: 'CANCELLED' },\n { id: 'EXPIRED', name: '已逾期', state: 'EXPIRED' },\n { id: 'DRAFT', name: '草稿', state: 'DRAFT' },\n];\n\n/**\n * Shared list page for any approval-instance \"view\" (inbox / sent / cc /\n * delegated). Caller picks the view + default state filter; the page renders\n * the standard BPM filter bar + paginated table and navigates to\n * `/instances/:id` on row action.\n */\nexport function ApprovalInstanceListPage({\n defaultState,\n description,\n emptyMessage,\n searchPlaceholder,\n title,\n view,\n}: ApprovalInstanceListPageProps): ReactElement {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const [error, setError] = useState<string | null>(null);\n const [initiatorProfilesById, setInitiatorProfilesById] = useState<\n ReadonlyMap<string, MemberProfileRecord>\n >(new Map());\n const [instancePage, setInstancePage] = useState(1);\n const [instancePageSize, setInstancePageSize] = useState(10);\n const [instanceTotalCount, setInstanceTotalCount] = useState(0);\n const [loading, setLoading] = useState(true);\n const [rows, setRows] = useState<readonly ApprovalInstanceRow[]>([]);\n const [searchText, setSearchText] = useState('');\n const [stateFilter, setStateFilter] = useState<StateFilterOption>(\n readStateFilterOption(defaultState),\n );\n const refreshRequestIdRef = useRef(0);\n\n const refreshInstances = useCallback(async (): Promise<void> => {\n const requestId = refreshRequestIdRef.current + 1;\n refreshRequestIdRef.current = requestId;\n setLoading(true);\n setError(null);\n\n try {\n const result = await listApprovalInstancesPage({\n page: instancePage,\n pageSize: instancePageSize,\n searchText,\n state: stateFilter.state,\n templateId: null,\n view,\n });\n\n if (requestId !== refreshRequestIdRef.current) {\n return;\n }\n\n setRows(result.instances.map(readApprovalInstanceRow));\n setInstanceTotalCount(result.totalCount);\n } catch (requestError: unknown) {\n if (requestId === refreshRequestIdRef.current) {\n setError(readErrorMessage(requestError));\n }\n } finally {\n if (requestId === refreshRequestIdRef.current) {\n setLoading(false);\n }\n }\n }, [instancePage, instancePageSize, searchText, stateFilter, view]);\n\n useEffect((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n\n useEffect((): (() => void) | void => {\n const initiatorMemberIds = Array.from(\n new Set(rows.map((row) => row.initiatorMemberId).filter(Boolean)),\n );\n\n if (initiatorMemberIds.length === 0) {\n setInitiatorProfilesById(new Map());\n\n return;\n }\n\n let cancelled = false;\n\n void (async (): Promise<void> => {\n try {\n const profiles = await resolveMembers(initiatorMemberIds);\n\n if (cancelled) {\n return;\n }\n\n setInitiatorProfilesById(\n new Map(profiles.map((profile) => [profile.memberId, profile])),\n );\n } catch {\n if (cancelled) {\n return;\n }\n\n setInitiatorProfilesById(new Map());\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, [rows]);\n\n const columns = useMemo(\n (): TableColumn<ApprovalInstanceRow>[] => [\n { dataIndex: 'caseTitle', key: 'caseTitle', title: '案件', width: 300 },\n {\n key: 'state',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography\n color={readInstanceStateColor(record.state)}\n component=\"span\"\n variant=\"body\"\n >\n {record.stateLabel}\n </Typography>\n ),\n title: '狀態',\n width: 120,\n },\n {\n key: 'initiatorMemberId',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {readInitiatorLabel(\n record.initiatorMemberId,\n initiatorProfilesById,\n )}\n </Typography>\n ),\n title: '發起人',\n width: 180,\n },\n {\n key: 'startedAt',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.startedAt)}\n </Typography>\n ),\n title: '發起時間',\n width: 220,\n },\n {\n key: 'completedAt',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.completedAt)}\n </Typography>\n ),\n title: '完成時間',\n width: 220,\n },\n ],\n [initiatorProfilesById],\n );\n const tableActions = useMemo(\n (): TableActions<ApprovalInstanceRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<ApprovalInstanceRow>['render']> => [\n {\n name: '查看',\n onClick: (): void => router.push(routes.caseDetail(record.id)),\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router],\n );\n\n return (\n <>\n <PageHeader>\n <ContentHeader description={description} title={title} />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.instanceFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceSearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setInstancePage(1);\n }}\n placeholder={searchPlaceholder}\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n <Filter span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceState\"\n >\n <Select\n clearable={false}\n fullWidth\n onChange={(option): void => {\n setStateFilter(readSelectedStateFilterOption(option));\n setInstancePage(1);\n }}\n options={[...STATE_FILTER_OPTIONS]}\n placeholder=\"狀態\"\n renderValue={(value): string =>\n `狀態:${readStateFilterLabel(value)}`\n }\n size=\"sub\"\n value={stateFilter}\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n {!error && !loading && rows.length === 0 ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n {emptyMessage}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={[...rows]}\n fullWidth\n loading={loading}\n pagination={{\n current: instancePage,\n onChange: (page): void => {\n setInstancePage(page);\n },\n onChangePageSize: (pageSize): void => {\n setInstancePage(1);\n setInstancePageSize(pageSize);\n },\n pageSize: instancePageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: INSTANCE_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: instanceTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction readApprovalInstanceRow(\n instance: ApprovalInstanceRecord,\n): ApprovalInstanceRow {\n return {\n ...instance,\n caseTitle: readApprovalInstanceCaseTitle(instance),\n key: instance.id,\n stateLabel: readInstanceStateLabel(instance.state),\n };\n}\n\nfunction readSelectedStateFilterOption(option: unknown): StateFilterOption {\n if (!isStateFilterOption(option)) {\n return STATE_FILTER_OPTIONS[0];\n }\n return readStateFilterOption(option.state);\n}\n\nfunction readStateFilterOption(\n state: ApprovalInstanceState | null,\n): StateFilterOption {\n return (\n STATE_FILTER_OPTIONS.find((option) => option.state === state) ??\n STATE_FILTER_OPTIONS[0]\n );\n}\n\nfunction readStateFilterLabel(value: unknown): string {\n return isStateFilterOption(value) ? value.name : STATE_FILTER_OPTIONS[0].name;\n}\n\nfunction isStateFilterOption(value: unknown): value is StateFilterOption {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'id' in value &&\n 'name' in value &&\n 'state' in value\n );\n}\n\nfunction readInstanceStateLabel(state: ApprovalInstanceState): string {\n if (state === 'RUNNING') return '進行中';\n if (state === 'APPROVED') return '已通過';\n if (state === 'REJECTED') return '已拒絕';\n if (state === 'RETURNED') return '已退回';\n if (state === 'CANCELLED') return '已取消';\n if (state === 'EXPIRED') return '已逾期';\n return '草稿';\n}\n\nfunction readInstanceStateColor(\n state: ApprovalInstanceState,\n): 'text-error' | 'text-neutral' | 'text-success' {\n if (state === 'APPROVED') return 'text-success';\n if (state === 'REJECTED' || state === 'CANCELLED' || state === 'EXPIRED') {\n return 'text-error';\n }\n return 'text-neutral';\n}\n\nfunction readInitiatorLabel(\n initiatorMemberId: string | null | undefined,\n initiatorProfilesById: ReadonlyMap<string, MemberProfileRecord>,\n): string {\n const trimmedMemberId = (initiatorMemberId ?? '').trim();\n if (!trimmedMemberId) return '未知發起人';\n return initiatorProfilesById.get(trimmedMemberId)?.name ?? trimmedMemberId;\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '讀取簽核案件失敗。';\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"approval-instance-list-page-CWiQvnfE.cjs","names":[],"sources":["../../src/components/approval-instance-list-page.module.scss","../../src/components/approval-instance-list-page.tsx"],"sourcesContent":[".instanceFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n","'use client';\n\nimport type { ChangeEvent, ReactElement } from 'react';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n PageHeader,\n Section,\n SectionGroup,\n Select,\n Table,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport {\n resolveMembers,\n type MemberProfileRecord,\n} from '@rytass/bpm-core-client';\nimport {\n listApprovalInstancesPage,\n readApprovalInstanceCaseTitle,\n type ApprovalInstanceRecord,\n type ApprovalInstanceState,\n type ApprovalInstanceView,\n} from '@rytass/bpm-core-client/workflow';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { formatDateTime } from '../lib/format-date-time';\nimport styles from './approval-instance-list-page.module.scss';\n\nexport interface ApprovalInstanceListPageProps {\n readonly defaultState: ApprovalInstanceState | null;\n readonly description: string;\n readonly emptyMessage: string;\n readonly searchPlaceholder: string;\n readonly title: string;\n readonly view: ApprovalInstanceView;\n}\n\ntype StateFilterOption = Readonly<{\n id: 'ALL' | ApprovalInstanceState;\n name: string;\n state: ApprovalInstanceState | null;\n}>;\n\ntype ApprovalInstanceRow = Readonly<\n Record<string, unknown> &\n ApprovalInstanceRecord & {\n caseTitle: string;\n key: string;\n stateLabel: string;\n }\n>;\n\nconst INSTANCE_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst STATE_FILTER_OPTIONS: readonly StateFilterOption[] = [\n { id: 'ALL', name: '全部狀態', state: null },\n { id: 'RUNNING', name: '進行中', state: 'RUNNING' },\n { id: 'APPROVED', name: '已通過', state: 'APPROVED' },\n { id: 'REJECTED', name: '已拒絕', state: 'REJECTED' },\n { id: 'RETURNED', name: '已退回', state: 'RETURNED' },\n { id: 'CANCELLED', name: '已取消', state: 'CANCELLED' },\n { id: 'EXPIRED', name: '已逾期', state: 'EXPIRED' },\n { id: 'DRAFT', name: '草稿', state: 'DRAFT' },\n];\n\n/**\n * Shared list page for any approval-instance \"view\" (inbox / sent / cc /\n * delegated). Caller picks the view + default state filter; the page renders\n * the standard BPM filter bar + paginated table and navigates to\n * `/instances/:id` on row action.\n */\nexport function ApprovalInstanceListPage({\n defaultState,\n description,\n emptyMessage,\n searchPlaceholder,\n title,\n view,\n}: ApprovalInstanceListPageProps): ReactElement {\n const router = useRouterAdapter();\n const routes = useBPMRoutes();\n const [error, setError] = useState<string | null>(null);\n const [initiatorProfilesById, setInitiatorProfilesById] = useState<\n ReadonlyMap<string, MemberProfileRecord>\n >(new Map());\n const [instancePage, setInstancePage] = useState(1);\n const [instancePageSize, setInstancePageSize] = useState(10);\n const [instanceTotalCount, setInstanceTotalCount] = useState(0);\n const [loading, setLoading] = useState(true);\n const [rows, setRows] = useState<readonly ApprovalInstanceRow[]>([]);\n const [searchText, setSearchText] = useState('');\n const [stateFilter, setStateFilter] = useState<StateFilterOption>(\n readStateFilterOption(defaultState),\n );\n const refreshRequestIdRef = useRef(0);\n\n const refreshInstances = useCallback(async (): Promise<void> => {\n const requestId = refreshRequestIdRef.current + 1;\n refreshRequestIdRef.current = requestId;\n setLoading(true);\n setError(null);\n\n try {\n const result = await listApprovalInstancesPage({\n page: instancePage,\n pageSize: instancePageSize,\n searchText,\n state: stateFilter.state,\n templateId: null,\n view,\n });\n\n if (requestId !== refreshRequestIdRef.current) {\n return;\n }\n\n setRows(result.instances.map(readApprovalInstanceRow));\n setInstanceTotalCount(result.totalCount);\n } catch (requestError: unknown) {\n if (requestId === refreshRequestIdRef.current) {\n setError(readErrorMessage(requestError));\n }\n } finally {\n if (requestId === refreshRequestIdRef.current) {\n setLoading(false);\n }\n }\n }, [instancePage, instancePageSize, searchText, stateFilter, view]);\n\n useEffect((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n\n useEffect((): (() => void) | void => {\n const initiatorMemberIds = Array.from(\n new Set(rows.map((row) => row.initiatorMemberId).filter(Boolean)),\n );\n\n if (initiatorMemberIds.length === 0) {\n setInitiatorProfilesById(new Map());\n\n return;\n }\n\n let cancelled = false;\n\n void (async (): Promise<void> => {\n try {\n const profiles = await resolveMembers(initiatorMemberIds);\n\n if (cancelled) {\n return;\n }\n\n setInitiatorProfilesById(\n new Map(profiles.map((profile) => [profile.memberId, profile])),\n );\n } catch {\n if (cancelled) {\n return;\n }\n\n setInitiatorProfilesById(new Map());\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, [rows]);\n\n const columns = useMemo(\n (): TableColumn<ApprovalInstanceRow>[] => [\n { dataIndex: 'caseTitle', key: 'caseTitle', title: '案件', width: 300 },\n {\n key: 'state',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography\n color={readInstanceStateColor(record.state)}\n component=\"span\"\n variant=\"body\"\n >\n {record.stateLabel}\n </Typography>\n ),\n title: '狀態',\n width: 120,\n },\n {\n key: 'initiatorMemberId',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {readInitiatorLabel(\n record.initiatorMemberId,\n initiatorProfilesById,\n )}\n </Typography>\n ),\n title: '發起人',\n width: 180,\n },\n {\n key: 'startedAt',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.startedAt)}\n </Typography>\n ),\n title: '發起時間',\n width: 220,\n },\n {\n key: 'completedAt',\n render: (record: ApprovalInstanceRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.completedAt)}\n </Typography>\n ),\n title: '完成時間',\n width: 220,\n },\n ],\n [initiatorProfilesById],\n );\n const tableActions = useMemo(\n (): TableActions<ApprovalInstanceRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<ApprovalInstanceRow>['render']> => [\n {\n name: '查看',\n onClick: (): void => router.push(routes.caseDetail(record.id)),\n },\n ],\n variant: 'base-secondary',\n width: 88,\n }),\n [router],\n );\n\n return (\n <>\n <PageHeader>\n <ContentHeader description={description} title={title} />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.instanceFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceSearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setInstancePage(1);\n }}\n placeholder={searchPlaceholder}\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n <Filter span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceState\"\n >\n <Select\n clearable={false}\n fullWidth\n onChange={(option): void => {\n setStateFilter(readSelectedStateFilterOption(option));\n setInstancePage(1);\n }}\n options={[...STATE_FILTER_OPTIONS]}\n placeholder=\"狀態\"\n renderValue={(value): string =>\n `狀態:${readStateFilterLabel(value)}`\n }\n size=\"sub\"\n value={stateFilter}\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n {!error && !loading && rows.length === 0 ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n {emptyMessage}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={[...rows]}\n fullWidth\n loading={loading}\n pagination={{\n current: instancePage,\n onChange: (page): void => {\n setInstancePage(page);\n },\n onChangePageSize: (pageSize): void => {\n setInstancePage(1);\n setInstancePageSize(pageSize);\n },\n pageSize: instancePageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: INSTANCE_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: instanceTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n </>\n );\n}\n\nfunction readApprovalInstanceRow(\n instance: ApprovalInstanceRecord,\n): ApprovalInstanceRow {\n return {\n ...instance,\n caseTitle: readApprovalInstanceCaseTitle(instance),\n key: instance.id,\n stateLabel: readInstanceStateLabel(instance.state),\n };\n}\n\nfunction readSelectedStateFilterOption(option: unknown): StateFilterOption {\n if (!isStateFilterOption(option)) {\n return STATE_FILTER_OPTIONS[0];\n }\n return readStateFilterOption(option.state);\n}\n\nfunction readStateFilterOption(\n state: ApprovalInstanceState | null,\n): StateFilterOption {\n return (\n STATE_FILTER_OPTIONS.find((option) => option.state === state) ??\n STATE_FILTER_OPTIONS[0]\n );\n}\n\nfunction readStateFilterLabel(value: unknown): string {\n return isStateFilterOption(value) ? value.name : STATE_FILTER_OPTIONS[0].name;\n}\n\nfunction isStateFilterOption(value: unknown): value is StateFilterOption {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'id' in value &&\n 'name' in value &&\n 'state' in value\n );\n}\n\nfunction readInstanceStateLabel(state: ApprovalInstanceState): string {\n if (state === 'RUNNING') return '進行中';\n if (state === 'APPROVED') return '已通過';\n if (state === 'REJECTED') return '已拒絕';\n if (state === 'RETURNED') return '已退回';\n if (state === 'CANCELLED') return '已取消';\n if (state === 'EXPIRED') return '已逾期';\n return '草稿';\n}\n\nfunction readInstanceStateColor(\n state: ApprovalInstanceState,\n): 'text-error' | 'text-neutral' | 'text-success' {\n if (state === 'APPROVED') return 'text-success';\n if (state === 'REJECTED' || state === 'CANCELLED' || state === 'EXPIRED') {\n return 'text-error';\n }\n return 'text-neutral';\n}\n\nfunction readInitiatorLabel(\n initiatorMemberId: string | null | undefined,\n initiatorProfilesById: ReadonlyMap<string, MemberProfileRecord>,\n): string {\n const trimmedMemberId = (initiatorMemberId ?? '').trim();\n if (!trimmedMemberId) return '未知發起人';\n return initiatorProfilesById.get(trimmedMemberId)?.name ?? trimmedMemberId;\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '讀取簽核案件失敗。';\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-provider-4BeCw7cI.cjs","names":[],"sources":["../../src/lib/auth-provider.module.scss","../../src/lib/auth-provider.tsx"],"sourcesContent":[".authLoading {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { Typography } from '@mezzanine-ui/react';\nimport {\n loginApi,\n logoutApi,\n readApiCurrentMember,\n type ApiMember,\n} from '@rytass/bpm-core-client';\nimport { useRouterAdapter } from './router-adapter';\nimport styles from './auth-provider.module.scss';\n\ninterface AuthContextValue {\n readonly loading: boolean;\n readonly member: ApiMember | null;\n readonly login: (input: {\n readonly identifier: string;\n readonly password: string;\n }) => Promise<ApiMember>;\n readonly logout: () => Promise<void>;\n readonly refresh: () => Promise<ApiMember | null>;\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\nexport interface AuthProviderProps {\n readonly children: ReactNode;\n /**\n * Paths that should not redirect to `/login` when there is no session.\n * Defaults to `['/login']`. Override when your host runs the login UI\n * under a different path.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to send unauthenticated users. Defaults to `'/login'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * BPM auth context provider. Reads / writes the host BPM API session via\n * `@rytass/bpm-core-client` (`loginApi` / `logoutApi` / `readApiCurrentMember`)\n * and uses the host-supplied {@link useRouterAdapter} to redirect\n * unauthenticated users.\n */\nexport function AuthProvider({\n children,\n publicPaths = ['/login'],\n loginPath = '/login',\n}: AuthProviderProps): ReactElement {\n const router = useRouterAdapter();\n const [loading, setLoading] = useState(true);\n const [member, setMember] = useState<ApiMember | null>(null);\n\n const refresh = useCallback(async (): Promise<ApiMember | null> => {\n const current = await readApiCurrentMember();\n setMember(current);\n return current;\n }, []);\n\n const login = useCallback(\n async (input: {\n readonly identifier: string;\n readonly password: string;\n }): Promise<ApiMember> => {\n const next = await loginApi(input);\n setMember(next);\n return next;\n },\n [],\n );\n\n const logout = useCallback(async (): Promise<void> => {\n await logoutApi();\n setMember(null);\n router.replace(loginPath);\n }, [loginPath, router]);\n\n useEffect((): (() => void) => {\n let active = true;\n setLoading(true);\n void (async () => {\n try {\n const current = await readApiCurrentMember();\n if (active) setMember(current);\n } catch {\n if (active) setMember(null);\n } finally {\n if (active) setLoading(false);\n }\n })();\n return (): void => {\n active = false;\n };\n }, []);\n\n useEffect((): void => {\n if (loading || isPublicPath(router.pathname, publicPaths) || member) return;\n router.replace(\n `${loginPath}?next=${encodeURIComponent(readCurrentPath(router.pathname))}`,\n );\n }, [loading, loginPath, member, publicPaths, router]);\n\n const value = useMemo<AuthContextValue>(\n () => ({ loading, login, logout, member, refresh }),\n [loading, login, logout, member, refresh],\n );\n\n if (loading && !isPublicPath(router.pathname, publicPaths)) {\n return <AuthLoadingState label=\"確認登入狀態\" />;\n }\n if (!loading && !member && !isPublicPath(router.pathname, publicPaths)) {\n return <AuthLoadingState label=\"前往登入頁\" />;\n }\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Access the BPM auth context. Throws when used outside `<AuthProvider>`.\n */\nexport function useAuth(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>');\n return ctx;\n}\n\nfunction AuthLoadingState({\n label,\n}: {\n readonly label: string;\n}): ReactElement {\n return (\n <main className={styles.authLoading}>\n <Typography color=\"text-neutral\" variant=\"body\">\n {label}\n </Typography>\n </main>\n );\n}\n\nfunction isPublicPath(\n pathname: string | null,\n publicPaths: readonly string[],\n): boolean {\n return publicPaths.some((p) => p === pathname);\n}\n\nfunction readCurrentPath(pathname: string | null): string {\n if (typeof window === 'undefined') return pathname ?? '/';\n return `${window.location.pathname}${window.location.search}`;\n}\n"],"mappings":"sOCiCM,GAAA,
|
|
1
|
+
{"version":3,"file":"auth-provider-4BeCw7cI.cjs","names":[],"sources":["../../src/lib/auth-provider.module.scss","../../src/lib/auth-provider.tsx"],"sourcesContent":[".authLoading {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { Typography } from '@mezzanine-ui/react';\nimport {\n loginApi,\n logoutApi,\n readApiCurrentMember,\n type ApiMember,\n} from '@rytass/bpm-core-client';\nimport { useRouterAdapter } from './router-adapter';\nimport styles from './auth-provider.module.scss';\n\ninterface AuthContextValue {\n readonly loading: boolean;\n readonly member: ApiMember | null;\n readonly login: (input: {\n readonly identifier: string;\n readonly password: string;\n }) => Promise<ApiMember>;\n readonly logout: () => Promise<void>;\n readonly refresh: () => Promise<ApiMember | null>;\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\nexport interface AuthProviderProps {\n readonly children: ReactNode;\n /**\n * Paths that should not redirect to `/login` when there is no session.\n * Defaults to `['/login']`. Override when your host runs the login UI\n * under a different path.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to send unauthenticated users. Defaults to `'/login'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * BPM auth context provider. Reads / writes the host BPM API session via\n * `@rytass/bpm-core-client` (`loginApi` / `logoutApi` / `readApiCurrentMember`)\n * and uses the host-supplied {@link useRouterAdapter} to redirect\n * unauthenticated users.\n */\nexport function AuthProvider({\n children,\n publicPaths = ['/login'],\n loginPath = '/login',\n}: AuthProviderProps): ReactElement {\n const router = useRouterAdapter();\n const [loading, setLoading] = useState(true);\n const [member, setMember] = useState<ApiMember | null>(null);\n\n const refresh = useCallback(async (): Promise<ApiMember | null> => {\n const current = await readApiCurrentMember();\n setMember(current);\n return current;\n }, []);\n\n const login = useCallback(\n async (input: {\n readonly identifier: string;\n readonly password: string;\n }): Promise<ApiMember> => {\n const next = await loginApi(input);\n setMember(next);\n return next;\n },\n [],\n );\n\n const logout = useCallback(async (): Promise<void> => {\n await logoutApi();\n setMember(null);\n router.replace(loginPath);\n }, [loginPath, router]);\n\n useEffect((): (() => void) => {\n let active = true;\n setLoading(true);\n void (async () => {\n try {\n const current = await readApiCurrentMember();\n if (active) setMember(current);\n } catch {\n if (active) setMember(null);\n } finally {\n if (active) setLoading(false);\n }\n })();\n return (): void => {\n active = false;\n };\n }, []);\n\n useEffect((): void => {\n if (loading || isPublicPath(router.pathname, publicPaths) || member) return;\n router.replace(\n `${loginPath}?next=${encodeURIComponent(readCurrentPath(router.pathname))}`,\n );\n }, [loading, loginPath, member, publicPaths, router]);\n\n const value = useMemo<AuthContextValue>(\n () => ({ loading, login, logout, member, refresh }),\n [loading, login, logout, member, refresh],\n );\n\n if (loading && !isPublicPath(router.pathname, publicPaths)) {\n return <AuthLoadingState label=\"確認登入狀態\" />;\n }\n if (!loading && !member && !isPublicPath(router.pathname, publicPaths)) {\n return <AuthLoadingState label=\"前往登入頁\" />;\n }\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Access the BPM auth context. Throws when used outside `<AuthProvider>`.\n */\nexport function useAuth(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>');\n return ctx;\n}\n\nfunction AuthLoadingState({\n label,\n}: {\n readonly label: string;\n}): ReactElement {\n return (\n <main className={styles.authLoading}>\n <Typography color=\"text-neutral\" variant=\"body\">\n {label}\n </Typography>\n </main>\n );\n}\n\nfunction isPublicPath(\n pathname: string | null,\n publicPaths: readonly string[],\n): boolean {\n return publicPaths.some((p) => p === pathname);\n}\n\nfunction readCurrentPath(pathname: string | null): string {\n if (typeof window === 'undefined') return pathname ?? '/';\n return `${window.location.pathname}${window.location.search}`;\n}\n"],"mappings":"sOCiCM,GAAA,EAAc,EAAA,cAAA,CAAuC,IAAI,EAsB/D,SAAgB,EAAa,CAC3B,WACA,cAAc,CAAC,QAAQ,EACvB,YAAY,UACsB,CAClC,IAAM,EAAS,EAAA,EAAiB,EAC1B,CAAC,EAAS,IAAA,EAAc,EAAA,SAAA,CAAS,EAAI,EACrC,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAA2B,IAAI,EAErD,GAAA,EAAU,EAAA,YAAA,CAAY,SAAuC,CACjE,IAAM,EAAU,MAAA,EAAM,EAAA,qBAAA,CAAqB,EAE3C,OADA,EAAU,CAAO,EACV,CACT,EAAG,CAAC,CAAC,EAEC,GAAA,EAAQ,EAAA,YAAA,CACZ,KAAO,IAGmB,CACxB,IAAM,EAAO,MAAA,EAAM,EAAA,SAAA,CAAS,CAAK,EAEjC,OADA,EAAU,CAAI,EACP,CACT,EACA,CAAC,CACH,EAEM,GAAA,EAAS,EAAA,YAAA,CAAY,SAA2B,CACpD,MAAA,EAAM,EAAA,UAAA,CAAU,EAChB,EAAU,IAAI,EACd,EAAO,QAAQ,CAAS,CAC1B,EAAG,CAAC,EAAW,CAAM,CAAC,GAEtB,EAAA,EAAA,UAAA,KAA8B,CAC5B,IAAI,EAAS,GAYb,OAXA,EAAW,EAAI,GACT,SAAY,CAChB,GAAI,CACF,IAAM,EAAU,MAAA,EAAM,EAAA,qBAAA,CAAqB,EACvC,GAAQ,EAAU,CAAO,CAC/B,MAAQ,CACF,GAAQ,EAAU,IAAI,CAC5B,QAAU,CACJ,GAAQ,EAAW,EAAK,CAC9B,CACF,EAAA,CAAG,MACgB,CACjB,EAAS,EACX,CACF,EAAG,CAAC,CAAC,GAEL,EAAA,EAAA,UAAA,KAAsB,CAChB,GAAW,EAAa,EAAO,SAAU,CAAW,GAAK,GAC7D,EAAO,QACL,GAAG,EAAU,QAAQ,mBAAmB,EAAgB,EAAO,QAAQ,CAAC,GAC1E,CACF,EAAG,CAAC,EAAS,EAAW,EAAQ,EAAa,CAAM,CAAC,EAEpD,IAAM,GAAA,EAAQ,EAAA,QAAA,MACL,CAAE,UAAS,QAAO,SAAQ,SAAQ,SAAQ,GACjD,CAAC,EAAS,EAAO,EAAQ,EAAQ,CAAO,CAC1C,EAQA,OANI,GAAW,CAAC,EAAa,EAAO,SAAU,CAAW,GAChD,EAAA,EAAA,IAAA,CAAC,EAAD,CAAkB,MAAM,QAAU,CAAA,EAEvC,CAAC,GAAW,CAAC,GAAU,CAAC,EAAa,EAAO,SAAU,CAAW,GAC5D,EAAA,EAAA,IAAA,CAAC,EAAD,CAAkB,MAAM,OAAS,CAAA,GAEnC,EAAA,EAAA,IAAA,CAAC,EAAY,SAAb,CAA6B,QAAQ,UAA+B,CAAA,CAC7E,CAKA,SAAgB,GAA4B,CAC1C,IAAM,GAAA,EAAM,EAAA,WAAA,CAAW,CAAW,EAClC,GAAI,CAAC,EAAK,MAAU,MAAM,4CAA4C,EACtE,OAAO,CACT,CAEA,SAAS,EAAiB,CACxB,SAGe,CACf,OACE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAO,YACtB,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,WAAD,CAAY,MAAM,eAAe,QAAQ,OACtC,SAAA,CACS,CAAA,CACR,CAAA,CAEV,CAEA,SAAS,EACP,EACA,EACS,CACT,OAAO,EAAY,KAAM,GAAM,IAAM,CAAQ,CAC/C,CAEA,SAAS,EAAgB,EAAiC,CAExD,OADI,OAAO,OAAW,IAAoB,GAAY,IAC/C,GAAG,OAAO,SAAS,WAAW,OAAO,SAAS,QACvD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-provider-B5oPmvk2.js","names":[],"sources":["../../src/lib/auth-provider.module.scss","../../src/lib/auth-provider.tsx"],"sourcesContent":[".authLoading {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { Typography } from '@mezzanine-ui/react';\nimport {\n loginApi,\n logoutApi,\n readApiCurrentMember,\n type ApiMember,\n} from '@rytass/bpm-core-client';\nimport { useRouterAdapter } from './router-adapter';\nimport styles from './auth-provider.module.scss';\n\ninterface AuthContextValue {\n readonly loading: boolean;\n readonly member: ApiMember | null;\n readonly login: (input: {\n readonly identifier: string;\n readonly password: string;\n }) => Promise<ApiMember>;\n readonly logout: () => Promise<void>;\n readonly refresh: () => Promise<ApiMember | null>;\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\nexport interface AuthProviderProps {\n readonly children: ReactNode;\n /**\n * Paths that should not redirect to `/login` when there is no session.\n * Defaults to `['/login']`. Override when your host runs the login UI\n * under a different path.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to send unauthenticated users. Defaults to `'/login'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * BPM auth context provider. Reads / writes the host BPM API session via\n * `@rytass/bpm-core-client` (`loginApi` / `logoutApi` / `readApiCurrentMember`)\n * and uses the host-supplied {@link useRouterAdapter} to redirect\n * unauthenticated users.\n */\nexport function AuthProvider({\n children,\n publicPaths = ['/login'],\n loginPath = '/login',\n}: AuthProviderProps): ReactElement {\n const router = useRouterAdapter();\n const [loading, setLoading] = useState(true);\n const [member, setMember] = useState<ApiMember | null>(null);\n\n const refresh = useCallback(async (): Promise<ApiMember | null> => {\n const current = await readApiCurrentMember();\n setMember(current);\n return current;\n }, []);\n\n const login = useCallback(\n async (input: {\n readonly identifier: string;\n readonly password: string;\n }): Promise<ApiMember> => {\n const next = await loginApi(input);\n setMember(next);\n return next;\n },\n [],\n );\n\n const logout = useCallback(async (): Promise<void> => {\n await logoutApi();\n setMember(null);\n router.replace(loginPath);\n }, [loginPath, router]);\n\n useEffect((): (() => void) => {\n let active = true;\n setLoading(true);\n void (async () => {\n try {\n const current = await readApiCurrentMember();\n if (active) setMember(current);\n } catch {\n if (active) setMember(null);\n } finally {\n if (active) setLoading(false);\n }\n })();\n return (): void => {\n active = false;\n };\n }, []);\n\n useEffect((): void => {\n if (loading || isPublicPath(router.pathname, publicPaths) || member) return;\n router.replace(\n `${loginPath}?next=${encodeURIComponent(readCurrentPath(router.pathname))}`,\n );\n }, [loading, loginPath, member, publicPaths, router]);\n\n const value = useMemo<AuthContextValue>(\n () => ({ loading, login, logout, member, refresh }),\n [loading, login, logout, member, refresh],\n );\n\n if (loading && !isPublicPath(router.pathname, publicPaths)) {\n return <AuthLoadingState label=\"確認登入狀態\" />;\n }\n if (!loading && !member && !isPublicPath(router.pathname, publicPaths)) {\n return <AuthLoadingState label=\"前往登入頁\" />;\n }\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Access the BPM auth context. Throws when used outside `<AuthProvider>`.\n */\nexport function useAuth(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>');\n return ctx;\n}\n\nfunction AuthLoadingState({\n label,\n}: {\n readonly label: string;\n}): ReactElement {\n return (\n <main className={styles.authLoading}>\n <Typography color=\"text-neutral\" variant=\"body\">\n {label}\n </Typography>\n </main>\n );\n}\n\nfunction isPublicPath(\n pathname: string | null,\n publicPaths: readonly string[],\n): boolean {\n return publicPaths.some((p) => p === pathname);\n}\n\nfunction readCurrentPath(pathname: string | null): string {\n if (typeof window === 'undefined') return pathname ?? '/';\n return `${window.location.pathname}${window.location.search}`;\n}\n"],"mappings":";;;;;;kDCiCM,IAAc,EAAuC,IAAI;AAsB/D,SAAgB,EAAa,EAC3B,aACA,iBAAc,CAAC,QAAQ,GACvB,eAAY,YACsB;CAClC,IAAM,IAAS,EAAiB,GAC1B,CAAC,GAAS,KAAc,EAAS,EAAI,GACrC,CAAC,GAAQ,KAAa,EAA2B,IAAI,GAErD,IAAU,EAAY,YAAuC;EACjE,IAAM,IAAU,MAAM,EAAqB;EAE3C,OADA,EAAU,CAAO,GACV;CACT,GAAG,CAAC,CAAC,GAEC,IAAQ,EACZ,OAAO,MAGmB;EACxB,IAAM,IAAO,MAAM,EAAS,CAAK;EAEjC,OADA,EAAU,CAAI,GACP;CACT,GACA,CAAC,CACH,GAEM,IAAS,EAAY,YAA2B;EAGpD,AAFA,MAAM,EAAU,GAChB,EAAU,IAAI,GACd,EAAO,QAAQ,CAAS;CAC1B,GAAG,CAAC,GAAW,CAAM,CAAC;CAoBtB,AAlBA,QAA8B;EAC5B,IAAI,IAAS;EAYb,OAXA,EAAW,EAAI,IACT,YAAY;GAChB,IAAI;IACF,IAAM,IAAU,MAAM,EAAqB;IAC3C,AAAI,KAAQ,EAAU,CAAO;GAC/B,QAAQ;IACN,AAAI,KAAQ,EAAU,IAAI;GAC5B,UAAU;IACR,AAAI,KAAQ,EAAW,EAAK;GAC9B;EACF,
|
|
1
|
+
{"version":3,"file":"auth-provider-B5oPmvk2.js","names":[],"sources":["../../src/lib/auth-provider.module.scss","../../src/lib/auth-provider.tsx"],"sourcesContent":[".authLoading {\n align-items: center;\n background: var(--mzn-color-bg-body);\n display: flex;\n min-height: 100vh;\n justify-content: center;\n padding: 32px;\n}\n","'use client';\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport { Typography } from '@mezzanine-ui/react';\nimport {\n loginApi,\n logoutApi,\n readApiCurrentMember,\n type ApiMember,\n} from '@rytass/bpm-core-client';\nimport { useRouterAdapter } from './router-adapter';\nimport styles from './auth-provider.module.scss';\n\ninterface AuthContextValue {\n readonly loading: boolean;\n readonly member: ApiMember | null;\n readonly login: (input: {\n readonly identifier: string;\n readonly password: string;\n }) => Promise<ApiMember>;\n readonly logout: () => Promise<void>;\n readonly refresh: () => Promise<ApiMember | null>;\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\nexport interface AuthProviderProps {\n readonly children: ReactNode;\n /**\n * Paths that should not redirect to `/login` when there is no session.\n * Defaults to `['/login']`. Override when your host runs the login UI\n * under a different path.\n */\n readonly publicPaths?: readonly string[];\n /**\n * Where to send unauthenticated users. Defaults to `'/login'`.\n */\n readonly loginPath?: string;\n}\n\n/**\n * BPM auth context provider. Reads / writes the host BPM API session via\n * `@rytass/bpm-core-client` (`loginApi` / `logoutApi` / `readApiCurrentMember`)\n * and uses the host-supplied {@link useRouterAdapter} to redirect\n * unauthenticated users.\n */\nexport function AuthProvider({\n children,\n publicPaths = ['/login'],\n loginPath = '/login',\n}: AuthProviderProps): ReactElement {\n const router = useRouterAdapter();\n const [loading, setLoading] = useState(true);\n const [member, setMember] = useState<ApiMember | null>(null);\n\n const refresh = useCallback(async (): Promise<ApiMember | null> => {\n const current = await readApiCurrentMember();\n setMember(current);\n return current;\n }, []);\n\n const login = useCallback(\n async (input: {\n readonly identifier: string;\n readonly password: string;\n }): Promise<ApiMember> => {\n const next = await loginApi(input);\n setMember(next);\n return next;\n },\n [],\n );\n\n const logout = useCallback(async (): Promise<void> => {\n await logoutApi();\n setMember(null);\n router.replace(loginPath);\n }, [loginPath, router]);\n\n useEffect((): (() => void) => {\n let active = true;\n setLoading(true);\n void (async () => {\n try {\n const current = await readApiCurrentMember();\n if (active) setMember(current);\n } catch {\n if (active) setMember(null);\n } finally {\n if (active) setLoading(false);\n }\n })();\n return (): void => {\n active = false;\n };\n }, []);\n\n useEffect((): void => {\n if (loading || isPublicPath(router.pathname, publicPaths) || member) return;\n router.replace(\n `${loginPath}?next=${encodeURIComponent(readCurrentPath(router.pathname))}`,\n );\n }, [loading, loginPath, member, publicPaths, router]);\n\n const value = useMemo<AuthContextValue>(\n () => ({ loading, login, logout, member, refresh }),\n [loading, login, logout, member, refresh],\n );\n\n if (loading && !isPublicPath(router.pathname, publicPaths)) {\n return <AuthLoadingState label=\"確認登入狀態\" />;\n }\n if (!loading && !member && !isPublicPath(router.pathname, publicPaths)) {\n return <AuthLoadingState label=\"前往登入頁\" />;\n }\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n/**\n * Access the BPM auth context. Throws when used outside `<AuthProvider>`.\n */\nexport function useAuth(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) throw new Error('useAuth must be used inside <AuthProvider>');\n return ctx;\n}\n\nfunction AuthLoadingState({\n label,\n}: {\n readonly label: string;\n}): ReactElement {\n return (\n <main className={styles.authLoading}>\n <Typography color=\"text-neutral\" variant=\"body\">\n {label}\n </Typography>\n </main>\n );\n}\n\nfunction isPublicPath(\n pathname: string | null,\n publicPaths: readonly string[],\n): boolean {\n return publicPaths.some((p) => p === pathname);\n}\n\nfunction readCurrentPath(pathname: string | null): string {\n if (typeof window === 'undefined') return pathname ?? '/';\n return `${window.location.pathname}${window.location.search}`;\n}\n"],"mappings":";;;;;;kDCiCM,IAAc,EAAuC,IAAI;AAsB/D,SAAgB,EAAa,EAC3B,aACA,iBAAc,CAAC,QAAQ,GACvB,eAAY,YACsB;CAClC,IAAM,IAAS,EAAiB,GAC1B,CAAC,GAAS,KAAc,EAAS,EAAI,GACrC,CAAC,GAAQ,KAAa,EAA2B,IAAI,GAErD,IAAU,EAAY,YAAuC;EACjE,IAAM,IAAU,MAAM,EAAqB;EAE3C,OADA,EAAU,CAAO,GACV;CACT,GAAG,CAAC,CAAC,GAEC,IAAQ,EACZ,OAAO,MAGmB;EACxB,IAAM,IAAO,MAAM,EAAS,CAAK;EAEjC,OADA,EAAU,CAAI,GACP;CACT,GACA,CAAC,CACH,GAEM,IAAS,EAAY,YAA2B;EAGpD,AAFA,MAAM,EAAU,GAChB,EAAU,IAAI,GACd,EAAO,QAAQ,CAAS;CAC1B,GAAG,CAAC,GAAW,CAAM,CAAC;CAoBtB,AAlBA,QAA8B;EAC5B,IAAI,IAAS;EAYb,OAXA,EAAW,EAAI,IACT,YAAY;GAChB,IAAI;IACF,IAAM,IAAU,MAAM,EAAqB;IAC3C,AAAI,KAAQ,EAAU,CAAO;GAC/B,QAAQ;IACN,AAAI,KAAQ,EAAU,IAAI;GAC5B,UAAU;IACR,AAAI,KAAQ,EAAW,EAAK;GAC9B;EACF,EAAA,CAAG,SACgB;GACjB,IAAS;EACX;CACF,GAAG,CAAC,CAAC,GAEL,QAAsB;EAChB,KAAW,EAAa,EAAO,UAAU,CAAW,KAAK,KAC7D,EAAO,QACL,GAAG,EAAU,QAAQ,mBAAmB,EAAgB,EAAO,QAAQ,CAAC,GAC1E;CACF,GAAG;EAAC;EAAS;EAAW;EAAQ;EAAa;CAAM,CAAC;CAEpD,IAAM,IAAQ,SACL;EAAE;EAAS;EAAO;EAAQ;EAAQ;CAAQ,IACjD;EAAC;EAAS;EAAO;EAAQ;EAAQ;CAAO,CAC1C;CAQA,OANI,KAAW,CAAC,EAAa,EAAO,UAAU,CAAW,IAChD,kBAAC,GAAD,EAAkB,OAAM,SAAU,CAAA,IAEvC,CAAC,KAAW,CAAC,KAAU,CAAC,EAAa,EAAO,UAAU,CAAW,IAC5D,kBAAC,GAAD,EAAkB,OAAM,QAAS,CAAA,IAEnC,kBAAC,EAAY,UAAb;EAA6B;EAAQ;CAA+B,CAAA;AAC7E;AAKA,SAAgB,IAA4B;CAC1C,IAAM,IAAM,EAAW,CAAW;CAClC,IAAI,CAAC,GAAK,MAAU,MAAM,4CAA4C;CACtE,OAAO;AACT;AAEA,SAAS,EAAiB,EACxB,YAGe;CACf,OACE,kBAAC,QAAD;EAAM,WAAW,EAAO;EACtB,UAAA,kBAAC,GAAD;GAAY,OAAM;GAAe,SAAQ;GACtC,UAAA;EACS,CAAA;CACR,CAAA;AAEV;AAEA,SAAS,EACP,GACA,GACS;CACT,OAAO,EAAY,MAAM,MAAM,MAAM,CAAQ;AAC/C;AAEA,SAAS,EAAgB,GAAiC;CAExD,OADI,OAAO,SAAW,MAAoB,KAAY,MAC/C,GAAG,OAAO,SAAS,WAAW,OAAO,SAAS;AACvD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bpm-form-field-Bc6k4ZEO.cjs","names":[],"sources":["../../src/components/bpm-form-field.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactElement } from 'react';\nimport { FormField, type FormFieldProps } from '@mezzanine-ui/react';\nimport { FormFieldDensity, FormFieldLayout } from '@mezzanine-ui/core/form';\n\ntype BPMFormFieldProps = Omit<\n FormFieldProps,\n 'density' | 'fullWidth' | 'layout'\n> &\n Partial<Pick<FormFieldProps, 'density' | 'fullWidth' | 'layout'>>;\n\n/**\n * Mezzanine `<FormField>` pre-configured with the BPM admin defaults\n * (`TIGHT` density, `HORIZONTAL` layout, full width). Use everywhere a\n * label-aligned field is needed.\n */\nexport function BPMFormField({\n density = FormFieldDensity.TIGHT,\n fullWidth = true,\n layout = FormFieldLayout.HORIZONTAL,\n ...props\n}: BPMFormFieldProps): ReactElement {\n return (\n <FormField\n density={density}\n fullWidth={fullWidth}\n layout={layout}\n {...props}\n />\n );\n}\n"],"mappings":"sHAiBA,SAAgB,EAAa,CAC3B,UAAU,EAAA,iBAAiB,MAC3B,YAAY,GACZ,SAAS,EAAA,gBAAgB,WACzB,GAAG,GAC+B,CAClC,OACE,EAAA,EAAA,
|
|
1
|
+
{"version":3,"file":"bpm-form-field-Bc6k4ZEO.cjs","names":[],"sources":["../../src/components/bpm-form-field.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactElement } from 'react';\nimport { FormField, type FormFieldProps } from '@mezzanine-ui/react';\nimport { FormFieldDensity, FormFieldLayout } from '@mezzanine-ui/core/form';\n\ntype BPMFormFieldProps = Omit<\n FormFieldProps,\n 'density' | 'fullWidth' | 'layout'\n> &\n Partial<Pick<FormFieldProps, 'density' | 'fullWidth' | 'layout'>>;\n\n/**\n * Mezzanine `<FormField>` pre-configured with the BPM admin defaults\n * (`TIGHT` density, `HORIZONTAL` layout, full width). Use everywhere a\n * label-aligned field is needed.\n */\nexport function BPMFormField({\n density = FormFieldDensity.TIGHT,\n fullWidth = true,\n layout = FormFieldLayout.HORIZONTAL,\n ...props\n}: BPMFormFieldProps): ReactElement {\n return (\n <FormField\n density={density}\n fullWidth={fullWidth}\n layout={layout}\n {...props}\n />\n );\n}\n"],"mappings":"sHAiBA,SAAgB,EAAa,CAC3B,UAAU,EAAA,iBAAiB,MAC3B,YAAY,GACZ,SAAS,EAAA,gBAAgB,WACzB,GAAG,GAC+B,CAClC,OACE,EAAA,EAAA,IAAA,CAAC,EAAA,UAAD,CACW,UACE,YACH,SACR,GAAI,CACL,CAAA,CAEL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"categories-DGlxuC6X.js","names":[],"sources":["../../src/views/templates/templates.module.scss","../../src/views/templates/categories/TemplateCategoriesView.tsx"],"sourcesContent":[".templateFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.templateModalFields {\n display: grid;\n gap: 16px;\n}\n","'use client';\n\nimport type { ChangeEvent, Key, ReactElement } from 'react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Tab,\n TabItem,\n Table,\n Textarea,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { PlusIcon } from '@mezzanine-ui/icons';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { BPMFormField } from '../../../components/bpm-form-field';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport {\n ApprovalTemplateCategoryRecord,\n ApprovalTemplateCategoryStatus,\n createApprovalTemplateCategory,\n deleteApprovalTemplateCategory,\n listApprovalTemplateCategoriesPage,\n updateApprovalTemplateCategory,\n} from '@rytass/bpm-core-client/template';\nimport styles from '../templates.module.scss';\n\nconst CATEGORY_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst CATEGORY_STATUS_TABS: readonly {\n readonly key: CategoryStatusTabKey;\n readonly label: string;\n}[] = [\n { key: 'ALL', label: '全部' },\n { key: 'ACTIVE', label: '啟用' },\n { key: 'INACTIVE', label: '停用' },\n];\ntype CategoryStatusTabKey = 'ACTIVE' | 'ALL' | 'INACTIVE';\n\ntype CategoryRow = Readonly<\n Record<string, unknown> &\n ApprovalTemplateCategoryRecord & {\n key: string;\n updatedAtLabel: string;\n }\n>;\n\ntype CategoryModalState = Readonly<{\n record: ApprovalTemplateCategoryRecord | null;\n type: 'CREATE' | 'EDIT';\n}>;\n\ntype DeleteConfirmationState = Readonly<{\n id: string;\n name: string;\n}>;\n\n\nexport function TemplateCategoriesView(): ReactElement {\n const [categories, setCategories] = useState<\n readonly ApprovalTemplateCategoryRecord[]\n >([]);\n const [categoryPage, setCategoryPage] = useState(1);\n const [categoryPageSize, setCategoryPageSize] = useState(10);\n const [categoryStatus, setCategoryStatus] =\n useState<CategoryStatusTabKey>('ALL');\n const [categoryTotalCount, setCategoryTotalCount] = useState(0);\n const [deleteConfirmation, setDeleteConfirmation] =\n useState<DeleteConfirmationState | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [modalState, setModalState] = useState<CategoryModalState | null>(null);\n const [saving, setSaving] = useState(false);\n const [searchText, setSearchText] = useState('');\n\n const refreshCategories = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const categoryPageResult = await listApprovalTemplateCategoriesPage({\n page: categoryPage,\n pageSize: categoryPageSize,\n searchText,\n status: readCategoryStatus(categoryStatus),\n });\n\n setCategories(categoryPageResult.categories);\n setCategoryTotalCount(categoryPageResult.totalCount);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [categoryPage, categoryPageSize, categoryStatus, searchText]);\n\n useEffect((): void => {\n void refreshCategories();\n }, [refreshCategories]);\n\n const rows = useMemo(\n (): CategoryRow[] =>\n categories.map((category) => ({\n ...category,\n key: category.id,\n updatedAtLabel: formatDateTime(category.updatedAt),\n })),\n [categories],\n );\n\n const columns = useMemo(\n (): TableColumn<CategoryRow>[] => [\n { dataIndex: 'name', key: 'name', title: '分類名稱', width: 180 },\n {\n key: 'status',\n render: (record: CategoryRow): ReactElement => (\n <CategoryStatusBadge isActive={record.isActive} />\n ),\n title: '狀態',\n width: 120,\n },\n {\n dataIndex: 'sortOrder',\n key: 'sortOrder',\n title: '排序',\n width: 100,\n },\n {\n key: 'description',\n render: (record: CategoryRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {record.description || '無'}\n </Typography>\n ),\n title: '說明',\n width: 260,\n },\n {\n dataIndex: 'updatedAtLabel',\n key: 'updatedAtLabel',\n title: '更新時間',\n width: 220,\n },\n ],\n [],\n );\n\n async function handleSaveCategory(input: CategoryFormInput): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n if (modalState?.type === 'EDIT' && modalState.record) {\n await updateApprovalTemplateCategory({\n ...input,\n id: modalState.record.id,\n });\n } else {\n await createApprovalTemplateCategory(input);\n }\n\n setModalState(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n throw requestError;\n } finally {\n setSaving(false);\n }\n }\n\n async function handleToggleCategory(\n record: ApprovalTemplateCategoryRecord,\n ): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n await updateApprovalTemplateCategory({\n description: record.description,\n id: record.id,\n isActive: !record.isActive,\n name: record.name,\n sortOrder: record.sortOrder,\n });\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n async function handleConfirmDelete(): Promise<void> {\n if (!deleteConfirmation) {\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n await deleteApprovalTemplateCategory(deleteConfirmation.id);\n setDeleteConfirmation(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const tableActions = useMemo(\n (): TableActions<CategoryRow> => ({\n render: (record): ReturnType<TableActions<CategoryRow>['render']> => [\n {\n name: '編輯',\n onClick: (): void => setModalState({ record, type: 'EDIT' }),\n },\n {\n name: record.isActive ? '停用' : '啟用',\n onClick: (): void => void handleToggleCategory(record),\n variant: record.isActive ? 'destructive-secondary' : 'base-secondary',\n },\n {\n name: '刪除',\n onClick: (): void =>\n setDeleteConfirmation({ id: record.id, name: record.name }),\n variant: 'destructive-secondary',\n },\n ],\n variant: 'base-secondary',\n width: 192,\n }),\n [refreshCategories],\n );\n\n return (\n <>\n <>\n <PageHeader>\n <ContentHeader\n description=\"維護簽核模板分類,供模板建立、篩選與列表標示使用。\"\n title=\"簽核模板分類\"\n >\n <Button\n icon={PlusIcon}\n iconType=\"leading\"\n onClick={(): void =>\n setModalState({ record: null, type: 'CREATE' })\n }\n variant=\"base-primary\"\n >\n 建立分類\n </Button>\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.templateFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"categorySearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setCategoryPage(1);\n }}\n placeholder=\"關鍵字:搜尋分類名稱或說明\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n tab={\n <Tab\n activeKey={categoryStatus}\n onChange={(activeKey): void => {\n setCategoryStatus(readCategoryStatusTabKey(activeKey));\n setCategoryPage(1);\n }}\n >\n {CATEGORY_STATUS_TABS.map((statusTab) => (\n <TabItem key={statusTab.key}>{statusTab.label}</TabItem>\n ))}\n </Tab>\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 || saving}\n pagination={{\n current: categoryPage,\n onChange: (page): void => {\n setCategoryPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setCategoryPage(1);\n setCategoryPageSize(pageSize);\n },\n pageSize: categoryPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: CATEGORY_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: categoryTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n </>\n\n <CategoryModal\n loading={saving}\n modal={modalState}\n onClose={(): void => setModalState(null)}\n onSubmit={handleSaveCategory}\n />\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ variant: 'destructive-primary' }}\n confirmText=\"刪除\"\n loading={saving}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={(): void => setDeleteConfirmation(null)}\n onClose={(): void => setDeleteConfirmation(null)}\n onConfirm={(): void => void handleConfirmDelete()}\n open={Boolean(deleteConfirmation)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"若分類已被模板使用,系統會改為停用分類並保留既有模板關聯。\"\n title=\"刪除分類\"\n >\n <Typography color=\"text-neutral\" variant=\"body\">\n 確定要刪除「{deleteConfirmation?.name ?? ''}」嗎?\n </Typography>\n </Modal>\n </>\n );\n}\n\ninterface CategoryFormInput {\n readonly description: string | null;\n readonly isActive: boolean;\n readonly name: string;\n readonly sortOrder: number;\n}\n\nfunction CategoryModal({\n loading,\n modal,\n onClose,\n onSubmit,\n}: {\n readonly loading: boolean;\n readonly modal: CategoryModalState | null;\n readonly onClose: () => void;\n readonly onSubmit: (input: CategoryFormInput) => Promise<void>;\n}): ReactElement {\n const [description, setDescription] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [name, setName] = useState('');\n const [sortOrder, setSortOrder] = useState('0');\n\n useEffect((): void => {\n if (!modal) {\n return;\n }\n\n setDescription(modal.record?.description ?? '');\n setError(null);\n setName(modal.record?.name ?? '');\n setSortOrder(String(modal.record?.sortOrder ?? 0));\n }, [modal]);\n\n async function handleConfirm(): Promise<void> {\n const trimmedName = name.trim();\n const parsedSortOrder = Number(sortOrder);\n\n if (!trimmedName) {\n setError('請輸入分類名稱');\n return;\n }\n\n if (!Number.isInteger(parsedSortOrder)) {\n setError('排序必須是整數');\n return;\n }\n\n try {\n await onSubmit({\n description: description.trim() || null,\n isActive: modal?.record?.isActive ?? true,\n name: trimmedName,\n sortOrder: parsedSortOrder,\n });\n } catch (submitError: unknown) {\n setError(readErrorMessage(submitError));\n }\n }\n\n return (\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ disabled: !name.trim() }}\n confirmText={modal?.type === 'EDIT' ? '儲存' : '建立'}\n loading={loading}\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={(): void => void handleConfirm()}\n open={Boolean(modal)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={modal?.type === 'EDIT' ? '編輯分類' : '建立分類'}\n >\n <div className={styles.templateModalFields}>\n <BPMFormField label=\"分類名稱\" name=\"categoryName\" required>\n <Input\n autoFocus\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setName(event.target.value);\n setError(null);\n }}\n placeholder=\"例如:行政管理\"\n value={name}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"排序\" name=\"categorySortOrder\">\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setSortOrder(event.target.value);\n setError(null);\n }}\n placeholder=\"0\"\n value={sortOrder}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"說明\" name=\"categoryDescription\">\n <Textarea\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setDescription(event.target.value);\n setError(null);\n }}\n placeholder=\"補充分類用途\"\n value={description}\n />\n </BPMFormField>\n </div>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n </Modal>\n );\n}\n\nfunction CategoryStatusBadge({\n isActive,\n}: {\n readonly isActive: boolean;\n}): ReactElement {\n return isActive ? (\n <Badge size=\"sub\" text=\"啟用\" variant=\"dot-success\" />\n ) : (\n <Badge size=\"sub\" text=\"停用\" variant=\"dot-inactive\" />\n );\n}\n\nfunction readCategoryStatusTabKey(activeKey: Key): CategoryStatusTabKey {\n if (activeKey === 'ACTIVE' || activeKey === 'INACTIVE') {\n return activeKey;\n }\n\n return 'ALL';\n}\n\nfunction readCategoryStatus(\n status: CategoryStatusTabKey,\n): ApprovalTemplateCategoryStatus {\n return status;\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":"categories-DGlxuC6X.js","names":[],"sources":["../../src/views/templates/templates.module.scss","../../src/views/templates/categories/TemplateCategoriesView.tsx"],"sourcesContent":[".templateFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.templateModalFields {\n display: grid;\n gap: 16px;\n}\n","'use client';\n\nimport type { ChangeEvent, Key, ReactElement } from 'react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Tab,\n TabItem,\n Table,\n Textarea,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { PlusIcon } from '@mezzanine-ui/icons';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { BPMFormField } from '../../../components/bpm-form-field';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport {\n ApprovalTemplateCategoryRecord,\n ApprovalTemplateCategoryStatus,\n createApprovalTemplateCategory,\n deleteApprovalTemplateCategory,\n listApprovalTemplateCategoriesPage,\n updateApprovalTemplateCategory,\n} from '@rytass/bpm-core-client/template';\nimport styles from '../templates.module.scss';\n\nconst CATEGORY_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst CATEGORY_STATUS_TABS: readonly {\n readonly key: CategoryStatusTabKey;\n readonly label: string;\n}[] = [\n { key: 'ALL', label: '全部' },\n { key: 'ACTIVE', label: '啟用' },\n { key: 'INACTIVE', label: '停用' },\n];\ntype CategoryStatusTabKey = 'ACTIVE' | 'ALL' | 'INACTIVE';\n\ntype CategoryRow = Readonly<\n Record<string, unknown> &\n ApprovalTemplateCategoryRecord & {\n key: string;\n updatedAtLabel: string;\n }\n>;\n\ntype CategoryModalState = Readonly<{\n record: ApprovalTemplateCategoryRecord | null;\n type: 'CREATE' | 'EDIT';\n}>;\n\ntype DeleteConfirmationState = Readonly<{\n id: string;\n name: string;\n}>;\n\n\nexport function TemplateCategoriesView(): ReactElement {\n const [categories, setCategories] = useState<\n readonly ApprovalTemplateCategoryRecord[]\n >([]);\n const [categoryPage, setCategoryPage] = useState(1);\n const [categoryPageSize, setCategoryPageSize] = useState(10);\n const [categoryStatus, setCategoryStatus] =\n useState<CategoryStatusTabKey>('ALL');\n const [categoryTotalCount, setCategoryTotalCount] = useState(0);\n const [deleteConfirmation, setDeleteConfirmation] =\n useState<DeleteConfirmationState | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [modalState, setModalState] = useState<CategoryModalState | null>(null);\n const [saving, setSaving] = useState(false);\n const [searchText, setSearchText] = useState('');\n\n const refreshCategories = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const categoryPageResult = await listApprovalTemplateCategoriesPage({\n page: categoryPage,\n pageSize: categoryPageSize,\n searchText,\n status: readCategoryStatus(categoryStatus),\n });\n\n setCategories(categoryPageResult.categories);\n setCategoryTotalCount(categoryPageResult.totalCount);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [categoryPage, categoryPageSize, categoryStatus, searchText]);\n\n useEffect((): void => {\n void refreshCategories();\n }, [refreshCategories]);\n\n const rows = useMemo(\n (): CategoryRow[] =>\n categories.map((category) => ({\n ...category,\n key: category.id,\n updatedAtLabel: formatDateTime(category.updatedAt),\n })),\n [categories],\n );\n\n const columns = useMemo(\n (): TableColumn<CategoryRow>[] => [\n { dataIndex: 'name', key: 'name', title: '分類名稱', width: 180 },\n {\n key: 'status',\n render: (record: CategoryRow): ReactElement => (\n <CategoryStatusBadge isActive={record.isActive} />\n ),\n title: '狀態',\n width: 120,\n },\n {\n dataIndex: 'sortOrder',\n key: 'sortOrder',\n title: '排序',\n width: 100,\n },\n {\n key: 'description',\n render: (record: CategoryRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {record.description || '無'}\n </Typography>\n ),\n title: '說明',\n width: 260,\n },\n {\n dataIndex: 'updatedAtLabel',\n key: 'updatedAtLabel',\n title: '更新時間',\n width: 220,\n },\n ],\n [],\n );\n\n async function handleSaveCategory(input: CategoryFormInput): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n if (modalState?.type === 'EDIT' && modalState.record) {\n await updateApprovalTemplateCategory({\n ...input,\n id: modalState.record.id,\n });\n } else {\n await createApprovalTemplateCategory(input);\n }\n\n setModalState(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n throw requestError;\n } finally {\n setSaving(false);\n }\n }\n\n async function handleToggleCategory(\n record: ApprovalTemplateCategoryRecord,\n ): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n await updateApprovalTemplateCategory({\n description: record.description,\n id: record.id,\n isActive: !record.isActive,\n name: record.name,\n sortOrder: record.sortOrder,\n });\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n async function handleConfirmDelete(): Promise<void> {\n if (!deleteConfirmation) {\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n await deleteApprovalTemplateCategory(deleteConfirmation.id);\n setDeleteConfirmation(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const tableActions = useMemo(\n (): TableActions<CategoryRow> => ({\n render: (record): ReturnType<TableActions<CategoryRow>['render']> => [\n {\n name: '編輯',\n onClick: (): void => setModalState({ record, type: 'EDIT' }),\n },\n {\n name: record.isActive ? '停用' : '啟用',\n onClick: (): void => void handleToggleCategory(record),\n variant: record.isActive ? 'destructive-secondary' : 'base-secondary',\n },\n {\n name: '刪除',\n onClick: (): void =>\n setDeleteConfirmation({ id: record.id, name: record.name }),\n variant: 'destructive-secondary',\n },\n ],\n variant: 'base-secondary',\n width: 192,\n }),\n [refreshCategories],\n );\n\n return (\n <>\n <>\n <PageHeader>\n <ContentHeader\n description=\"維護簽核模板分類,供模板建立、篩選與列表標示使用。\"\n title=\"簽核模板分類\"\n >\n <Button\n icon={PlusIcon}\n iconType=\"leading\"\n onClick={(): void =>\n setModalState({ record: null, type: 'CREATE' })\n }\n variant=\"base-primary\"\n >\n 建立分類\n </Button>\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.templateFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"categorySearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setCategoryPage(1);\n }}\n placeholder=\"關鍵字:搜尋分類名稱或說明\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n tab={\n <Tab\n activeKey={categoryStatus}\n onChange={(activeKey): void => {\n setCategoryStatus(readCategoryStatusTabKey(activeKey));\n setCategoryPage(1);\n }}\n >\n {CATEGORY_STATUS_TABS.map((statusTab) => (\n <TabItem key={statusTab.key}>{statusTab.label}</TabItem>\n ))}\n </Tab>\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 || saving}\n pagination={{\n current: categoryPage,\n onChange: (page): void => {\n setCategoryPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setCategoryPage(1);\n setCategoryPageSize(pageSize);\n },\n pageSize: categoryPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: CATEGORY_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: categoryTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n </>\n\n <CategoryModal\n loading={saving}\n modal={modalState}\n onClose={(): void => setModalState(null)}\n onSubmit={handleSaveCategory}\n />\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ variant: 'destructive-primary' }}\n confirmText=\"刪除\"\n loading={saving}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={(): void => setDeleteConfirmation(null)}\n onClose={(): void => setDeleteConfirmation(null)}\n onConfirm={(): void => void handleConfirmDelete()}\n open={Boolean(deleteConfirmation)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"若分類已被模板使用,系統會改為停用分類並保留既有模板關聯。\"\n title=\"刪除分類\"\n >\n <Typography color=\"text-neutral\" variant=\"body\">\n 確定要刪除「{deleteConfirmation?.name ?? ''}」嗎?\n </Typography>\n </Modal>\n </>\n );\n}\n\ninterface CategoryFormInput {\n readonly description: string | null;\n readonly isActive: boolean;\n readonly name: string;\n readonly sortOrder: number;\n}\n\nfunction CategoryModal({\n loading,\n modal,\n onClose,\n onSubmit,\n}: {\n readonly loading: boolean;\n readonly modal: CategoryModalState | null;\n readonly onClose: () => void;\n readonly onSubmit: (input: CategoryFormInput) => Promise<void>;\n}): ReactElement {\n const [description, setDescription] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [name, setName] = useState('');\n const [sortOrder, setSortOrder] = useState('0');\n\n useEffect((): void => {\n if (!modal) {\n return;\n }\n\n setDescription(modal.record?.description ?? '');\n setError(null);\n setName(modal.record?.name ?? '');\n setSortOrder(String(modal.record?.sortOrder ?? 0));\n }, [modal]);\n\n async function handleConfirm(): Promise<void> {\n const trimmedName = name.trim();\n const parsedSortOrder = Number(sortOrder);\n\n if (!trimmedName) {\n setError('請輸入分類名稱');\n return;\n }\n\n if (!Number.isInteger(parsedSortOrder)) {\n setError('排序必須是整數');\n return;\n }\n\n try {\n await onSubmit({\n description: description.trim() || null,\n isActive: modal?.record?.isActive ?? true,\n name: trimmedName,\n sortOrder: parsedSortOrder,\n });\n } catch (submitError: unknown) {\n setError(readErrorMessage(submitError));\n }\n }\n\n return (\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ disabled: !name.trim() }}\n confirmText={modal?.type === 'EDIT' ? '儲存' : '建立'}\n loading={loading}\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={(): void => void handleConfirm()}\n open={Boolean(modal)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={modal?.type === 'EDIT' ? '編輯分類' : '建立分類'}\n >\n <div className={styles.templateModalFields}>\n <BPMFormField label=\"分類名稱\" name=\"categoryName\" required>\n <Input\n autoFocus\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setName(event.target.value);\n setError(null);\n }}\n placeholder=\"例如:行政管理\"\n value={name}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"排序\" name=\"categorySortOrder\">\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setSortOrder(event.target.value);\n setError(null);\n }}\n placeholder=\"0\"\n value={sortOrder}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"說明\" name=\"categoryDescription\">\n <Textarea\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setDescription(event.target.value);\n setError(null);\n }}\n placeholder=\"補充分類用途\"\n value={description}\n />\n </BPMFormField>\n </div>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n </Modal>\n );\n}\n\nfunction CategoryStatusBadge({\n isActive,\n}: {\n readonly isActive: boolean;\n}): ReactElement {\n return isActive ? (\n <Badge size=\"sub\" text=\"啟用\" variant=\"dot-success\" />\n ) : (\n <Badge size=\"sub\" text=\"停用\" variant=\"dot-inactive\" />\n );\n}\n\nfunction readCategoryStatusTabKey(activeKey: Key): CategoryStatusTabKey {\n if (activeKey === 'ACTIVE' || activeKey === 'INACTIVE') {\n return activeKey;\n }\n\n return 'ALL';\n}\n\nfunction readCategoryStatus(\n status: CategoryStatusTabKey,\n): ApprovalTemplateCategoryStatus {\n return status;\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";require('../categories.css');const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./format-date-time-D-cx5M8z.cjs"),n=require("./bpm-form-field-Bc6k4ZEO.cjs");let r=require("react"),i=require("@mezzanine-ui/react"),a=require("react/jsx-runtime"),o=require("@mezzanine-ui/core/form"),s=require("@mezzanine-ui/react/ContentHeader");s=e.t(s,1);let c=require("@mezzanine-ui/icons"),l=require("@rytass/bpm-core-client/template");var u={templateFilterArea:`bpm_templateFilterArea_nUL-R`,templateModalFields:`bpm_templateModalFields_hUxWp`},d=[10,20,50],f=[{key:`ALL`,label:`全部`},{key:`ACTIVE`,label:`啟用`},{key:`INACTIVE`,label:`停用`}];function p(){let[e,n]=(0,r.useState)([]),[p,y]=(0,r.useState)(1),[b,x]=(0,r.useState)(10),[S,C]=(0,r.useState)(`ALL`),[w,T]=(0,r.useState)(0),[E,D]=(0,r.useState)(null),[O,k]=(0,r.useState)(null),[A,j]=(0,r.useState)(!0),[M,N]=(0,r.useState)(null),[P,F]=(0,r.useState)(!1),[I,L]=(0,r.useState)(``),R=(0,r.useCallback)(async()=>{j(!0),k(null);try{let e=await(0,l.listApprovalTemplateCategoriesPage)({page:p,pageSize:b,searchText:I,status:_(S)});n(e.categories),T(e.totalCount)}catch(e){k(v(e))}finally{j(!1)}},[p,b,S,I]);(0,r.useEffect)(()=>{R()},[R]);let z=(0,r.useMemo)(()=>e.map(e=>({...e,key:e.id,updatedAtLabel:t.t(e.updatedAt)})),[e]),B=(0,r.useMemo)(()=>[{dataIndex:`name`,key:`name`,title:`分類名稱`,width:180},{key:`status`,render:e=>(0,a.jsx)(h,{isActive:e.isActive}),title:`狀態`,width:120},{dataIndex:`sortOrder`,key:`sortOrder`,title:`排序`,width:100},{key:`description`,render:e=>(0,a.jsx)(i.Typography,{component:`span`,variant:`body`,children:e.description||`無`}),title:`說明`,width:260},{dataIndex:`updatedAtLabel`,key:`updatedAtLabel`,title:`更新時間`,width:220}],[]);async function V(e){F(!0),k(null);try{M?.type===`EDIT`&&M.record?await(0,l.updateApprovalTemplateCategory)({...e,id:M.record.id}):await(0,l.createApprovalTemplateCategory)(e),N(null),await R()}catch(e){throw k(v(e)),e}finally{F(!1)}}async function H(e){F(!0),k(null);try{await(0,l.updateApprovalTemplateCategory)({description:e.description,id:e.id,isActive:!e.isActive,name:e.name,sortOrder:e.sortOrder}),await R()}catch(e){k(v(e))}finally{F(!1)}}async function U(){if(E){F(!0),k(null);try{await(0,l.deleteApprovalTemplateCategory)(E.id),D(null),await R()}catch(e){k(v(e))}finally{F(!1)}}}let W=(0,r.useMemo)(()=>({render:e=>[{name:`編輯`,onClick:()=>N({record:e,type:`EDIT`})},{name:e.isActive?`停用`:`啟用`,onClick:()=>void H(e),variant:e.isActive?`destructive-secondary`:`base-secondary`},{name:`刪除`,onClick:()=>D({id:e.id,name:e.name}),variant:`destructive-secondary`}],variant:`base-secondary`,width:192}),[R]);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(i.PageHeader,{children:(0,a.jsx)(s.default,{description:`維護簽核模板分類,供模板建立、篩選與列表標示使用。`,title:`簽核模板分類`,children:(0,a.jsx)(i.Button,{icon:c.PlusIcon,iconType:`leading`,onClick:()=>N({record:null,type:`CREATE`}),variant:`base-primary`,children:`建立分類`})})}),(0,a.jsx)(i.SectionGroup,{children:(0,a.jsxs)(i.Section,{filterArea:(0,a.jsx)(i.FilterArea,{className:u.templateFilterArea,size:`sub`,children:(0,a.jsx)(i.FilterLine,{children:(0,a.jsx)(i.Filter,{span:3,children:(0,a.jsx)(i.FormField,{fullWidth:!0,layout:o.FormFieldLayout.VERTICAL,name:`categorySearchText`,children:(0,a.jsx)(i.Input,{fullWidth:!0,onChange:e=>{L(e.target.value),y(1)},placeholder:`關鍵字:搜尋分類名稱或說明`,size:`sub`,value:I,variant:`base`})})})})}),tab:(0,a.jsx)(i.Tab,{activeKey:S,onChange:e=>{C(g(e)),y(1)},children:f.map(e=>(0,a.jsx)(i.TabItem,{children:e.label},e.key))}),children:[O?(0,a.jsx)(i.Typography,{color:`text-error`,variant:`body`,children:O}):null,(0,a.jsx)(i.Table,{actions:W,columns:B,dataSource:z,fullWidth:!0,loading:A||P,pagination:{current:p,onChange:e=>{y(e)},onChangePageSize:e=>{y(1),x(e)},pageSize:b,pageSizeLabel:`每頁筆數`,pageSizeOptions:d,renderResultSummary:(e,t,n)=>`顯示 ${e}-${t} 筆,共 ${n} 筆`,showPageSizeOptions:!0,total:w}})]})})]}),(0,a.jsx)(m,{loading:P,modal:M,onClose:()=>N(null),onSubmit:V}),(0,a.jsx)(i.Modal,{cancelText:`取消`,confirmButtonProps:{variant:`destructive-primary`},confirmText:`刪除`,loading:P,modalStatusType:`error`,modalType:`standard`,onCancel:()=>D(null),onClose:()=>D(null),onConfirm:()=>void U(),open:!!E,showModalFooter:!0,showModalHeader:!0,size:`regular`,supportingText:`若分類已被模板使用,系統會改為停用分類並保留既有模板關聯。`,title:`刪除分類`,children:(0,a.jsxs)(i.Typography,{color:`text-neutral`,variant:`body`,children:[`確定要刪除「`,E?.name??``,`」嗎?`]})})]})}function m({loading:e,modal:t,onClose:o,onSubmit:s}){let[c,l]=(0,r.useState)(``),[d,f]=(0,r.useState)(null),[p,m]=(0,r.useState)(``),[h,g]=(0,r.useState)(`0`);(0,r.useEffect)(()=>{t&&(l(t.record?.description??``),f(null),m(t.record?.name??``),g(String(t.record?.sortOrder??0)))},[t]);async function _(){let e=p.trim(),n=Number(h);if(!e){f(`請輸入分類名稱`);return}if(!Number.isInteger(n)){f(`排序必須是整數`);return}try{await s({description:c.trim()||null,isActive:t?.record?.isActive??!0,name:e,sortOrder:n})}catch(e){f(v(e))}}return(0,a.jsxs)(i.Modal,{cancelText:`取消`,confirmButtonProps:{disabled:!p.trim()},confirmText:t?.type===`EDIT`?`儲存`:`建立`,loading:e,modalType:`standard`,onCancel:o,onClose:o,onConfirm:()=>void _(),open:!!t,showModalFooter:!0,showModalHeader:!0,size:`regular`,title:t?.type===`EDIT`?`編輯分類`:`建立分類`,children:[(0,a.jsxs)(`div`,{className:u.templateModalFields,children:[(0,a.jsx)(n.t,{label:`分類名稱`,name:`categoryName`,required:!0,children:(0,a.jsx)(i.Input,{autoFocus:!0,fullWidth:!0,onChange:e=>{m(e.target.value),f(null)},placeholder:`例如:行政管理`,value:p,variant:`base`})}),(0,a.jsx)(n.t,{label:`排序`,name:`categorySortOrder`,children:(0,a.jsx)(i.Input,{fullWidth:!0,onChange:e=>{g(e.target.value),f(null)},placeholder:`0`,value:h,variant:`base`})}),(0,a.jsx)(n.t,{label:`說明`,name:`categoryDescription`,children:(0,a.jsx)(i.Textarea,{onChange:e=>{l(e.target.value),f(null)},placeholder:`補充分類用途`,value:c})})]}),d?(0,a.jsx)(i.Typography,{color:`text-error`,variant:`body`,children:d}):null]})}function h({isActive:e}){return e?(0,a.jsx)(i.Badge,{size:`sub`,text:`啟用`,variant:`dot-success`}):(0,a.jsx)(i.Badge,{size:`sub`,text:`停用`,variant:`dot-inactive`})}function g(e){return e===`ACTIVE`||e===`INACTIVE`?e:`ALL`}function _(e){return e}function v(e){return e instanceof Error?e.message:`發生未知錯誤`}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return p}});
|
|
2
|
+
//# sourceMappingURL=categories-Yid05WAX.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"categories-DUfldZ_9.cjs","names":[],"sources":["../../src/views/templates/templates.module.scss","../../src/views/templates/categories/TemplateCategoriesView.tsx"],"sourcesContent":[".templateFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.templateModalFields {\n display: grid;\n gap: 16px;\n}\n","'use client';\n\nimport type { ChangeEvent, Key, ReactElement } from 'react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Tab,\n TabItem,\n Table,\n Textarea,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { PlusIcon } from '@mezzanine-ui/icons';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { BPMFormField } from '../../../components/bpm-form-field';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport {\n ApprovalTemplateCategoryRecord,\n ApprovalTemplateCategoryStatus,\n createApprovalTemplateCategory,\n deleteApprovalTemplateCategory,\n listApprovalTemplateCategoriesPage,\n updateApprovalTemplateCategory,\n} from '@rytass/bpm-core-client/template';\nimport styles from '../templates.module.scss';\n\nconst CATEGORY_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst CATEGORY_STATUS_TABS: readonly {\n readonly key: CategoryStatusTabKey;\n readonly label: string;\n}[] = [\n { key: 'ALL', label: '全部' },\n { key: 'ACTIVE', label: '啟用' },\n { key: 'INACTIVE', label: '停用' },\n];\ntype CategoryStatusTabKey = 'ACTIVE' | 'ALL' | 'INACTIVE';\n\ntype CategoryRow = Readonly<\n Record<string, unknown> &\n ApprovalTemplateCategoryRecord & {\n key: string;\n updatedAtLabel: string;\n }\n>;\n\ntype CategoryModalState = Readonly<{\n record: ApprovalTemplateCategoryRecord | null;\n type: 'CREATE' | 'EDIT';\n}>;\n\ntype DeleteConfirmationState = Readonly<{\n id: string;\n name: string;\n}>;\n\n\nexport function TemplateCategoriesView(): ReactElement {\n const [categories, setCategories] = useState<\n readonly ApprovalTemplateCategoryRecord[]\n >([]);\n const [categoryPage, setCategoryPage] = useState(1);\n const [categoryPageSize, setCategoryPageSize] = useState(10);\n const [categoryStatus, setCategoryStatus] =\n useState<CategoryStatusTabKey>('ALL');\n const [categoryTotalCount, setCategoryTotalCount] = useState(0);\n const [deleteConfirmation, setDeleteConfirmation] =\n useState<DeleteConfirmationState | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [modalState, setModalState] = useState<CategoryModalState | null>(null);\n const [saving, setSaving] = useState(false);\n const [searchText, setSearchText] = useState('');\n\n const refreshCategories = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const categoryPageResult = await listApprovalTemplateCategoriesPage({\n page: categoryPage,\n pageSize: categoryPageSize,\n searchText,\n status: readCategoryStatus(categoryStatus),\n });\n\n setCategories(categoryPageResult.categories);\n setCategoryTotalCount(categoryPageResult.totalCount);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [categoryPage, categoryPageSize, categoryStatus, searchText]);\n\n useEffect((): void => {\n void refreshCategories();\n }, [refreshCategories]);\n\n const rows = useMemo(\n (): CategoryRow[] =>\n categories.map((category) => ({\n ...category,\n key: category.id,\n updatedAtLabel: formatDateTime(category.updatedAt),\n })),\n [categories],\n );\n\n const columns = useMemo(\n (): TableColumn<CategoryRow>[] => [\n { dataIndex: 'name', key: 'name', title: '分類名稱', width: 180 },\n {\n key: 'status',\n render: (record: CategoryRow): ReactElement => (\n <CategoryStatusBadge isActive={record.isActive} />\n ),\n title: '狀態',\n width: 120,\n },\n {\n dataIndex: 'sortOrder',\n key: 'sortOrder',\n title: '排序',\n width: 100,\n },\n {\n key: 'description',\n render: (record: CategoryRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {record.description || '無'}\n </Typography>\n ),\n title: '說明',\n width: 260,\n },\n {\n dataIndex: 'updatedAtLabel',\n key: 'updatedAtLabel',\n title: '更新時間',\n width: 220,\n },\n ],\n [],\n );\n\n async function handleSaveCategory(input: CategoryFormInput): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n if (modalState?.type === 'EDIT' && modalState.record) {\n await updateApprovalTemplateCategory({\n ...input,\n id: modalState.record.id,\n });\n } else {\n await createApprovalTemplateCategory(input);\n }\n\n setModalState(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n throw requestError;\n } finally {\n setSaving(false);\n }\n }\n\n async function handleToggleCategory(\n record: ApprovalTemplateCategoryRecord,\n ): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n await updateApprovalTemplateCategory({\n description: record.description,\n id: record.id,\n isActive: !record.isActive,\n name: record.name,\n sortOrder: record.sortOrder,\n });\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n async function handleConfirmDelete(): Promise<void> {\n if (!deleteConfirmation) {\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n await deleteApprovalTemplateCategory(deleteConfirmation.id);\n setDeleteConfirmation(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const tableActions = useMemo(\n (): TableActions<CategoryRow> => ({\n render: (record): ReturnType<TableActions<CategoryRow>['render']> => [\n {\n name: '編輯',\n onClick: (): void => setModalState({ record, type: 'EDIT' }),\n },\n {\n name: record.isActive ? '停用' : '啟用',\n onClick: (): void => void handleToggleCategory(record),\n variant: record.isActive ? 'destructive-secondary' : 'base-secondary',\n },\n {\n name: '刪除',\n onClick: (): void =>\n setDeleteConfirmation({ id: record.id, name: record.name }),\n variant: 'destructive-secondary',\n },\n ],\n variant: 'base-secondary',\n width: 192,\n }),\n [refreshCategories],\n );\n\n return (\n <>\n <>\n <PageHeader>\n <ContentHeader\n description=\"維護簽核模板分類,供模板建立、篩選與列表標示使用。\"\n title=\"簽核模板分類\"\n >\n <Button\n icon={PlusIcon}\n iconType=\"leading\"\n onClick={(): void =>\n setModalState({ record: null, type: 'CREATE' })\n }\n variant=\"base-primary\"\n >\n 建立分類\n </Button>\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.templateFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"categorySearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setCategoryPage(1);\n }}\n placeholder=\"關鍵字:搜尋分類名稱或說明\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n tab={\n <Tab\n activeKey={categoryStatus}\n onChange={(activeKey): void => {\n setCategoryStatus(readCategoryStatusTabKey(activeKey));\n setCategoryPage(1);\n }}\n >\n {CATEGORY_STATUS_TABS.map((statusTab) => (\n <TabItem key={statusTab.key}>{statusTab.label}</TabItem>\n ))}\n </Tab>\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 || saving}\n pagination={{\n current: categoryPage,\n onChange: (page): void => {\n setCategoryPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setCategoryPage(1);\n setCategoryPageSize(pageSize);\n },\n pageSize: categoryPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: CATEGORY_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: categoryTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n </>\n\n <CategoryModal\n loading={saving}\n modal={modalState}\n onClose={(): void => setModalState(null)}\n onSubmit={handleSaveCategory}\n />\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ variant: 'destructive-primary' }}\n confirmText=\"刪除\"\n loading={saving}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={(): void => setDeleteConfirmation(null)}\n onClose={(): void => setDeleteConfirmation(null)}\n onConfirm={(): void => void handleConfirmDelete()}\n open={Boolean(deleteConfirmation)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"若分類已被模板使用,系統會改為停用分類並保留既有模板關聯。\"\n title=\"刪除分類\"\n >\n <Typography color=\"text-neutral\" variant=\"body\">\n 確定要刪除「{deleteConfirmation?.name ?? ''}」嗎?\n </Typography>\n </Modal>\n </>\n );\n}\n\ninterface CategoryFormInput {\n readonly description: string | null;\n readonly isActive: boolean;\n readonly name: string;\n readonly sortOrder: number;\n}\n\nfunction CategoryModal({\n loading,\n modal,\n onClose,\n onSubmit,\n}: {\n readonly loading: boolean;\n readonly modal: CategoryModalState | null;\n readonly onClose: () => void;\n readonly onSubmit: (input: CategoryFormInput) => Promise<void>;\n}): ReactElement {\n const [description, setDescription] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [name, setName] = useState('');\n const [sortOrder, setSortOrder] = useState('0');\n\n useEffect((): void => {\n if (!modal) {\n return;\n }\n\n setDescription(modal.record?.description ?? '');\n setError(null);\n setName(modal.record?.name ?? '');\n setSortOrder(String(modal.record?.sortOrder ?? 0));\n }, [modal]);\n\n async function handleConfirm(): Promise<void> {\n const trimmedName = name.trim();\n const parsedSortOrder = Number(sortOrder);\n\n if (!trimmedName) {\n setError('請輸入分類名稱');\n return;\n }\n\n if (!Number.isInteger(parsedSortOrder)) {\n setError('排序必須是整數');\n return;\n }\n\n try {\n await onSubmit({\n description: description.trim() || null,\n isActive: modal?.record?.isActive ?? true,\n name: trimmedName,\n sortOrder: parsedSortOrder,\n });\n } catch (submitError: unknown) {\n setError(readErrorMessage(submitError));\n }\n }\n\n return (\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ disabled: !name.trim() }}\n confirmText={modal?.type === 'EDIT' ? '儲存' : '建立'}\n loading={loading}\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={(): void => void handleConfirm()}\n open={Boolean(modal)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={modal?.type === 'EDIT' ? '編輯分類' : '建立分類'}\n >\n <div className={styles.templateModalFields}>\n <BPMFormField label=\"分類名稱\" name=\"categoryName\" required>\n <Input\n autoFocus\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setName(event.target.value);\n setError(null);\n }}\n placeholder=\"例如:行政管理\"\n value={name}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"排序\" name=\"categorySortOrder\">\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setSortOrder(event.target.value);\n setError(null);\n }}\n placeholder=\"0\"\n value={sortOrder}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"說明\" name=\"categoryDescription\">\n <Textarea\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setDescription(event.target.value);\n setError(null);\n }}\n placeholder=\"補充分類用途\"\n value={description}\n />\n </BPMFormField>\n </div>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n </Modal>\n );\n}\n\nfunction CategoryStatusBadge({\n isActive,\n}: {\n readonly isActive: boolean;\n}): ReactElement {\n return isActive ? (\n <Badge size=\"sub\" text=\"啟用\" variant=\"dot-success\" />\n ) : (\n <Badge size=\"sub\" text=\"停用\" variant=\"dot-inactive\" />\n );\n}\n\nfunction readCategoryStatusTabKey(activeKey: Key): CategoryStatusTabKey {\n if (activeKey === 'ACTIVE' || activeKey === 'INACTIVE') {\n return activeKey;\n }\n\n return 'ALL';\n}\n\nfunction readCategoryStatus(\n status: CategoryStatusTabKey,\n): ApprovalTemplateCategoryStatus {\n return status;\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":"categories-Yid05WAX.cjs","names":[],"sources":["../../src/views/templates/templates.module.scss","../../src/views/templates/categories/TemplateCategoriesView.tsx"],"sourcesContent":[".templateFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.templateModalFields {\n display: grid;\n gap: 16px;\n}\n","'use client';\n\nimport type { ChangeEvent, Key, ReactElement } from 'react';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Input,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Tab,\n TabItem,\n Table,\n Textarea,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { PlusIcon } from '@mezzanine-ui/icons';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { BPMFormField } from '../../../components/bpm-form-field';\nimport { formatDateTime } from '../../../lib/format-date-time';\nimport {\n ApprovalTemplateCategoryRecord,\n ApprovalTemplateCategoryStatus,\n createApprovalTemplateCategory,\n deleteApprovalTemplateCategory,\n listApprovalTemplateCategoriesPage,\n updateApprovalTemplateCategory,\n} from '@rytass/bpm-core-client/template';\nimport styles from '../templates.module.scss';\n\nconst CATEGORY_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst CATEGORY_STATUS_TABS: readonly {\n readonly key: CategoryStatusTabKey;\n readonly label: string;\n}[] = [\n { key: 'ALL', label: '全部' },\n { key: 'ACTIVE', label: '啟用' },\n { key: 'INACTIVE', label: '停用' },\n];\ntype CategoryStatusTabKey = 'ACTIVE' | 'ALL' | 'INACTIVE';\n\ntype CategoryRow = Readonly<\n Record<string, unknown> &\n ApprovalTemplateCategoryRecord & {\n key: string;\n updatedAtLabel: string;\n }\n>;\n\ntype CategoryModalState = Readonly<{\n record: ApprovalTemplateCategoryRecord | null;\n type: 'CREATE' | 'EDIT';\n}>;\n\ntype DeleteConfirmationState = Readonly<{\n id: string;\n name: string;\n}>;\n\n\nexport function TemplateCategoriesView(): ReactElement {\n const [categories, setCategories] = useState<\n readonly ApprovalTemplateCategoryRecord[]\n >([]);\n const [categoryPage, setCategoryPage] = useState(1);\n const [categoryPageSize, setCategoryPageSize] = useState(10);\n const [categoryStatus, setCategoryStatus] =\n useState<CategoryStatusTabKey>('ALL');\n const [categoryTotalCount, setCategoryTotalCount] = useState(0);\n const [deleteConfirmation, setDeleteConfirmation] =\n useState<DeleteConfirmationState | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [modalState, setModalState] = useState<CategoryModalState | null>(null);\n const [saving, setSaving] = useState(false);\n const [searchText, setSearchText] = useState('');\n\n const refreshCategories = useCallback(async (): Promise<void> => {\n setLoading(true);\n setError(null);\n\n try {\n const categoryPageResult = await listApprovalTemplateCategoriesPage({\n page: categoryPage,\n pageSize: categoryPageSize,\n searchText,\n status: readCategoryStatus(categoryStatus),\n });\n\n setCategories(categoryPageResult.categories);\n setCategoryTotalCount(categoryPageResult.totalCount);\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setLoading(false);\n }\n }, [categoryPage, categoryPageSize, categoryStatus, searchText]);\n\n useEffect((): void => {\n void refreshCategories();\n }, [refreshCategories]);\n\n const rows = useMemo(\n (): CategoryRow[] =>\n categories.map((category) => ({\n ...category,\n key: category.id,\n updatedAtLabel: formatDateTime(category.updatedAt),\n })),\n [categories],\n );\n\n const columns = useMemo(\n (): TableColumn<CategoryRow>[] => [\n { dataIndex: 'name', key: 'name', title: '分類名稱', width: 180 },\n {\n key: 'status',\n render: (record: CategoryRow): ReactElement => (\n <CategoryStatusBadge isActive={record.isActive} />\n ),\n title: '狀態',\n width: 120,\n },\n {\n dataIndex: 'sortOrder',\n key: 'sortOrder',\n title: '排序',\n width: 100,\n },\n {\n key: 'description',\n render: (record: CategoryRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {record.description || '無'}\n </Typography>\n ),\n title: '說明',\n width: 260,\n },\n {\n dataIndex: 'updatedAtLabel',\n key: 'updatedAtLabel',\n title: '更新時間',\n width: 220,\n },\n ],\n [],\n );\n\n async function handleSaveCategory(input: CategoryFormInput): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n if (modalState?.type === 'EDIT' && modalState.record) {\n await updateApprovalTemplateCategory({\n ...input,\n id: modalState.record.id,\n });\n } else {\n await createApprovalTemplateCategory(input);\n }\n\n setModalState(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n throw requestError;\n } finally {\n setSaving(false);\n }\n }\n\n async function handleToggleCategory(\n record: ApprovalTemplateCategoryRecord,\n ): Promise<void> {\n setSaving(true);\n setError(null);\n\n try {\n await updateApprovalTemplateCategory({\n description: record.description,\n id: record.id,\n isActive: !record.isActive,\n name: record.name,\n sortOrder: record.sortOrder,\n });\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n async function handleConfirmDelete(): Promise<void> {\n if (!deleteConfirmation) {\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n await deleteApprovalTemplateCategory(deleteConfirmation.id);\n setDeleteConfirmation(null);\n await refreshCategories();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n const tableActions = useMemo(\n (): TableActions<CategoryRow> => ({\n render: (record): ReturnType<TableActions<CategoryRow>['render']> => [\n {\n name: '編輯',\n onClick: (): void => setModalState({ record, type: 'EDIT' }),\n },\n {\n name: record.isActive ? '停用' : '啟用',\n onClick: (): void => void handleToggleCategory(record),\n variant: record.isActive ? 'destructive-secondary' : 'base-secondary',\n },\n {\n name: '刪除',\n onClick: (): void =>\n setDeleteConfirmation({ id: record.id, name: record.name }),\n variant: 'destructive-secondary',\n },\n ],\n variant: 'base-secondary',\n width: 192,\n }),\n [refreshCategories],\n );\n\n return (\n <>\n <>\n <PageHeader>\n <ContentHeader\n description=\"維護簽核模板分類,供模板建立、篩選與列表標示使用。\"\n title=\"簽核模板分類\"\n >\n <Button\n icon={PlusIcon}\n iconType=\"leading\"\n onClick={(): void =>\n setModalState({ record: null, type: 'CREATE' })\n }\n variant=\"base-primary\"\n >\n 建立分類\n </Button>\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.templateFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={3}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"categorySearchText\"\n >\n <Input\n fullWidth\n onChange={(\n event: ChangeEvent<HTMLInputElement>,\n ): void => {\n setSearchText(event.target.value);\n setCategoryPage(1);\n }}\n placeholder=\"關鍵字:搜尋分類名稱或說明\"\n size=\"sub\"\n value={searchText}\n variant=\"base\"\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n tab={\n <Tab\n activeKey={categoryStatus}\n onChange={(activeKey): void => {\n setCategoryStatus(readCategoryStatusTabKey(activeKey));\n setCategoryPage(1);\n }}\n >\n {CATEGORY_STATUS_TABS.map((statusTab) => (\n <TabItem key={statusTab.key}>{statusTab.label}</TabItem>\n ))}\n </Tab>\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 || saving}\n pagination={{\n current: categoryPage,\n onChange: (page): void => {\n setCategoryPage(page);\n },\n onChangePageSize: (pageSize): void => {\n setCategoryPage(1);\n setCategoryPageSize(pageSize);\n },\n pageSize: categoryPageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: CATEGORY_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: categoryTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n </>\n\n <CategoryModal\n loading={saving}\n modal={modalState}\n onClose={(): void => setModalState(null)}\n onSubmit={handleSaveCategory}\n />\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ variant: 'destructive-primary' }}\n confirmText=\"刪除\"\n loading={saving}\n modalStatusType=\"error\"\n modalType=\"standard\"\n onCancel={(): void => setDeleteConfirmation(null)}\n onClose={(): void => setDeleteConfirmation(null)}\n onConfirm={(): void => void handleConfirmDelete()}\n open={Boolean(deleteConfirmation)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"若分類已被模板使用,系統會改為停用分類並保留既有模板關聯。\"\n title=\"刪除分類\"\n >\n <Typography color=\"text-neutral\" variant=\"body\">\n 確定要刪除「{deleteConfirmation?.name ?? ''}」嗎?\n </Typography>\n </Modal>\n </>\n );\n}\n\ninterface CategoryFormInput {\n readonly description: string | null;\n readonly isActive: boolean;\n readonly name: string;\n readonly sortOrder: number;\n}\n\nfunction CategoryModal({\n loading,\n modal,\n onClose,\n onSubmit,\n}: {\n readonly loading: boolean;\n readonly modal: CategoryModalState | null;\n readonly onClose: () => void;\n readonly onSubmit: (input: CategoryFormInput) => Promise<void>;\n}): ReactElement {\n const [description, setDescription] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [name, setName] = useState('');\n const [sortOrder, setSortOrder] = useState('0');\n\n useEffect((): void => {\n if (!modal) {\n return;\n }\n\n setDescription(modal.record?.description ?? '');\n setError(null);\n setName(modal.record?.name ?? '');\n setSortOrder(String(modal.record?.sortOrder ?? 0));\n }, [modal]);\n\n async function handleConfirm(): Promise<void> {\n const trimmedName = name.trim();\n const parsedSortOrder = Number(sortOrder);\n\n if (!trimmedName) {\n setError('請輸入分類名稱');\n return;\n }\n\n if (!Number.isInteger(parsedSortOrder)) {\n setError('排序必須是整數');\n return;\n }\n\n try {\n await onSubmit({\n description: description.trim() || null,\n isActive: modal?.record?.isActive ?? true,\n name: trimmedName,\n sortOrder: parsedSortOrder,\n });\n } catch (submitError: unknown) {\n setError(readErrorMessage(submitError));\n }\n }\n\n return (\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{ disabled: !name.trim() }}\n confirmText={modal?.type === 'EDIT' ? '儲存' : '建立'}\n loading={loading}\n modalType=\"standard\"\n onCancel={onClose}\n onClose={onClose}\n onConfirm={(): void => void handleConfirm()}\n open={Boolean(modal)}\n showModalFooter\n showModalHeader\n size=\"regular\"\n title={modal?.type === 'EDIT' ? '編輯分類' : '建立分類'}\n >\n <div className={styles.templateModalFields}>\n <BPMFormField label=\"分類名稱\" name=\"categoryName\" required>\n <Input\n autoFocus\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setName(event.target.value);\n setError(null);\n }}\n placeholder=\"例如:行政管理\"\n value={name}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"排序\" name=\"categorySortOrder\">\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setSortOrder(event.target.value);\n setError(null);\n }}\n placeholder=\"0\"\n value={sortOrder}\n variant=\"base\"\n />\n </BPMFormField>\n <BPMFormField label=\"說明\" name=\"categoryDescription\">\n <Textarea\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void => {\n setDescription(event.target.value);\n setError(null);\n }}\n placeholder=\"補充分類用途\"\n value={description}\n />\n </BPMFormField>\n </div>\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n </Modal>\n );\n}\n\nfunction CategoryStatusBadge({\n isActive,\n}: {\n readonly isActive: boolean;\n}): ReactElement {\n return isActive ? (\n <Badge size=\"sub\" text=\"啟用\" variant=\"dot-success\" />\n ) : (\n <Badge size=\"sub\" text=\"停用\" variant=\"dot-inactive\" />\n );\n}\n\nfunction readCategoryStatusTabKey(activeKey: Key): CategoryStatusTabKey {\n if (activeKey === 'ACTIVE' || activeKey === 'INACTIVE') {\n return activeKey;\n }\n\n return 'ALL';\n}\n\nfunction readCategoryStatus(\n status: CategoryStatusTabKey,\n): ApprovalTemplateCategoryStatus {\n return status;\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { r as e } from "./router-adapter-DftlFTOd.js";
|
|
3
3
|
import { r as t } from "./routes-config-RBYQtUd0.js";
|
|
4
|
-
import { t as n } from "./FormRendererView-
|
|
5
|
-
import { t as r } from "./FormBuilderView-
|
|
6
|
-
import { t as i } from "./designer-
|
|
4
|
+
import { t as n } from "./FormRendererView-BurlEDUu.js";
|
|
5
|
+
import { t as r } from "./FormBuilderView-TzSezeqC.js";
|
|
6
|
+
import { t as i } from "./designer-Cb3WAC46.js";
|
|
7
7
|
import { useCallback as a, useEffect as o, useState as s } from "react";
|
|
8
8
|
import { Button as c, Empty as l, FormField as u, Input as d, PageHeader as f, Section as p, SectionGroup as m, Select as h, Step as g, Stepper as _, Table as v, Tag as y, Typography as b } from "@mezzanine-ui/react";
|
|
9
9
|
import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
@@ -454,4 +454,4 @@ var q = {
|
|
|
454
454
|
//#endregion
|
|
455
455
|
export { U as i, V as n, H as r, K as t };
|
|
456
456
|
|
|
457
|
-
//# sourceMappingURL=compose-
|
|
457
|
+
//# sourceMappingURL=compose-CUOe5Ia9.js.map
|