@rytass/bpm-core-react 0.21.2 → 0.21.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/chunks/{FormBuilderView-CGKkuVFi.cjs → FormBuilderView-Bq4TXdxF.cjs} +2 -2
  3. package/dist/chunks/{FormBuilderView-CGKkuVFi.cjs.map → FormBuilderView-Bq4TXdxF.cjs.map} +1 -1
  4. package/dist/chunks/{FormBuilderView-6KdApi-D.js → FormBuilderView-DNsFwZgk.js} +2 -2
  5. package/dist/chunks/{FormBuilderView-6KdApi-D.js.map → FormBuilderView-DNsFwZgk.js.map} +1 -1
  6. package/dist/chunks/{FormRendererView-BcQKUi1A.js → FormRendererView-BF8Ge0Zk.js} +178 -153
  7. package/dist/chunks/FormRendererView-BF8Ge0Zk.js.map +1 -0
  8. package/dist/chunks/FormRendererView-ZIfSRWPi.cjs +6 -0
  9. package/dist/chunks/FormRendererView-ZIfSRWPi.cjs.map +1 -0
  10. package/dist/chunks/admin-pickers-BjbkBpSJ.cjs.map +1 -1
  11. package/dist/chunks/admin-pickers-DfGxsjpp.js.map +1 -1
  12. package/dist/chunks/approval-instance-list-page-BI5fIXc-.js.map +1 -1
  13. package/dist/chunks/approval-instance-list-page-Cv5I1YPX.cjs.map +1 -1
  14. package/dist/chunks/auth-provider-4BeCw7cI.cjs.map +1 -1
  15. package/dist/chunks/auth-provider-C2b-5mGt.js.map +1 -1
  16. package/dist/chunks/categories-CyyqG6wG.cjs.map +1 -1
  17. package/dist/chunks/categories-f7E-RJEc.js.map +1 -1
  18. package/dist/chunks/{compose-M-GR6smM.js → compose-C_Y38ENa.js} +4 -4
  19. package/dist/chunks/{compose-M-GR6smM.js.map → compose-C_Y38ENa.js.map} +1 -1
  20. package/dist/chunks/{compose-CtWuunwB.cjs → compose-NHbyjV73.cjs} +2 -2
  21. package/dist/chunks/{compose-CtWuunwB.cjs.map → compose-NHbyjV73.cjs.map} +1 -1
  22. package/dist/chunks/dashboard-page-BoqNhjT_.js.map +1 -1
  23. package/dist/chunks/dashboard-page-D7YJ3udk.cjs.map +1 -1
  24. package/dist/chunks/delegations-ClRCwD1I.cjs.map +1 -1
  25. package/dist/chunks/delegations-CqND-Qtz.js.map +1 -1
  26. package/dist/chunks/delegations-D43VDSz1.cjs.map +1 -1
  27. package/dist/chunks/delegations-zisffLwk.js.map +1 -1
  28. package/dist/chunks/{designer-BnMeiMVe.js → designer-BRYIpeTC.js} +3 -3
  29. package/dist/chunks/{designer-BnMeiMVe.js.map → designer-BRYIpeTC.js.map} +1 -1
  30. package/dist/chunks/{designer-D6fCDq_q.cjs → designer-oDGvHWBJ.cjs} +2 -2
  31. package/dist/chunks/{designer-D6fCDq_q.cjs.map → designer-oDGvHWBJ.cjs.map} +1 -1
  32. package/dist/chunks/{detail-C5KrUC7s.cjs → detail-BLhZp1mO.cjs} +2 -2
  33. package/dist/chunks/detail-BLhZp1mO.cjs.map +1 -0
  34. package/dist/chunks/{detail-Dnc4PPWO.js → detail-CkRgfU7l.js} +2 -2
  35. package/dist/chunks/detail-CkRgfU7l.js.map +1 -0
  36. package/dist/chunks/login-D6cc3bo7.js.map +1 -1
  37. package/dist/chunks/login-uvVcltrS.cjs.map +1 -1
  38. package/dist/chunks/notifications-Bc4mgSds.cjs.map +1 -1
  39. package/dist/chunks/notifications-Cvme5Qbl.js.map +1 -1
  40. package/dist/chunks/orgs-B_2J2tLX.cjs.map +1 -1
  41. package/dist/chunks/orgs-DqxMds26.js.map +1 -1
  42. package/dist/chunks/users-BBtiZ9QQ.js.map +1 -1
  43. package/dist/chunks/users-EqSzDhuj.cjs.map +1 -1
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/pages/instances/detail/index.cjs +1 -1
  47. package/dist/pages/instances/detail/index.js +1 -1
  48. package/dist/pages/templates/compose/index.cjs +1 -1
  49. package/dist/pages/templates/compose/index.js +1 -1
  50. package/dist/pages/templates/designer/index.cjs +1 -1
  51. package/dist/pages/templates/designer/index.js +1 -1
  52. package/dist/views/forms/builder/index.cjs +1 -1
  53. package/dist/views/forms/builder/index.js +1 -1
  54. package/dist/views/forms/renderer/form-control-menu.d.cts +7 -0
  55. package/dist/views/forms/renderer/form-control-menu.d.ts +7 -0
  56. package/dist/views/forms/renderer/form-data-source-field.d.cts +12 -0
  57. package/dist/views/forms/renderer/form-data-source-field.d.ts +12 -0
  58. package/dist/views/forms/renderer/index.cjs +1 -1
  59. package/dist/views/forms/renderer/index.js +1 -1
  60. package/dist/views/instances/detail/index.cjs +1 -1
  61. package/dist/views/instances/detail/index.js +1 -1
  62. package/dist/views/instances/new/index.cjs +1 -1
  63. package/dist/views/instances/new/index.js +1 -1
  64. package/dist/views/templates/compose/index.cjs +1 -1
  65. package/dist/views/templates/compose/index.js +1 -1
  66. package/dist/views/templates/designer/index.cjs +1 -1
  67. package/dist/views/templates/designer/index.js +1 -1
  68. package/package.json +3 -3
  69. package/dist/chunks/FormRendererView-B-xycC9S.cjs +0 -6
  70. package/dist/chunks/FormRendererView-B-xycC9S.cjs.map +0 -1
  71. package/dist/chunks/FormRendererView-BcQKUi1A.js.map +0 -1
  72. package/dist/chunks/detail-C5KrUC7s.cjs.map +0 -1
  73. package/dist/chunks/detail-Dnc4PPWO.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"approval-instance-list-page-BI5fIXc-.js","names":["_Fragment","styles","useRouterAdapter","useBPMRoutes","formatDateTime","Fragment$1","ContentHeader","readFieldFilterControl","item","EMPTY_FIELD_FILTER_DRAFT","DECIMAL_PATTERN","isDecimalText","readDecimal","value","plain","readIdList","text","toApprovalInstanceFieldFilter","draft","ids","from","to","buildApprovalInstanceFieldFilters","paths","filter","drafts","DEFAULT_SORT_DRAFT","DEFAULT_SORT_OPTION","SORT_TIME_LABELS","readSortFieldOptions","timeOptions","APPROVAL_INSTANCE_SORT_TIME_FIELDS","field","fieldOptions","templateSelected","isFilterableFieldSortable","isSortFieldAvailable","toApprovalInstanceSort","BOOLEAN_CHOICES","readFieldFilterElements","change","next","control","renderFilter","Input","event","readNumberHint","renderControl","readIdNoun","Filter","FormField","ControlFieldSlotLayout","FormFieldLayout","key","choices","option","Select","selected","label","choice","DateRangePicker","formatDatePickerValue","target","DateTimeRangePicker","formatDateTimePickerValue","approval_instance_list_page_module_default","INSTANCE_PAGE_SIZE_OPTIONS","FIELD_FILTERS_PER_LINE","STATE_FILTER_OPTIONS","SORT_DIRECTION_OPTIONS","ApprovalInstanceListPage","router","routes","error","setError","useState","initiatorProfilesById","setInitiatorProfilesById","instancePage","setInstancePage","instancePageSize","setInstancePageSize","instanceTotalCount","setInstanceTotalCount","loading","setLoading","rows","setRows","searchText","setSearchText","stateFilter","setStateFilter","readStateFilterOption","defaultState","templates","setTemplates","templateId","setTemplateId","templateSchema","setTemplateSchema","schemaLoading","setSchemaLoading","schemaError","setSchemaError","fieldDrafts","setFieldDrafts","sortDraft","setSortDraft","refreshRequestIdRef","useRef","fieldPaths","useMemo","readFilterableFieldPaths","fieldFilters","sort","sortOptions","refreshInstances","useCallback","requestId","result","listApprovalInstancesPage","readApprovalInstanceRow","requestError","readErrorMessage","view","useEffect","cancelled","loaded","listApprovalTemplates","schema","readApprovalTemplateCurrentFormSchema","schemaLoadError","initiatorMemberIds","row","profiles","resolveMembers","profile","handleTemplateChange","previous","handleFilterReset","handleFilterSubmit","handleFieldDraftChange","path","columns","Typography","readInstanceStateColor","record","readInitiatorLabel","tableActions","templateOptions","template","selectedTemplate","fieldFilterLines","chunkElements","FilterLine","elements","index","sortFieldChoices","selectedSortField","searchFilterLine","searchPlaceholder","readSelectedStateFilterOption","readStateFilterLabel","sortFilterLine","RadioGroup","readSortDirection","PageHeader","SectionGroup","Section","FilterArea","emptyMessage","Table","page","pageSize","total","items","last","lines","size","instance","readApprovalInstanceCaseTitle","readInstanceStateLabel","isStateFilterOption","state","trimmedMemberId","initiatorMemberId"],"sources":["../../src/components/approval-instance-field-filters.ts","../../src/components/approval-instance-field-filter-controls.tsx","../../src/components/approval-instance-list-page.module.scss","../../src/components/approval-instance-list-page.tsx"],"sourcesContent":["import {\n APPROVAL_INSTANCE_SORT_TIME_FIELDS,\n isFilterableFieldSortable,\n type ApprovalInstanceFieldFilter,\n type ApprovalInstanceSort,\n type ApprovalInstanceSortDirection,\n type FilterableFieldPath,\n type FormFieldOption,\n} from '@rytass/bpm-core-shared';\n\n/**\n * Pure logic behind the list page's field filters (ADR 25 §7). Turns the\n * filterable fields of a template's schema into controls, and the control\n * values into `fieldFilters` and `sort` for `listApprovalInstancesPage`. The\n * operator for each control is one `readFilterOperatorsForFieldType` allows.\n */\n\n/** Which control a filterable field gets on the list page. */\nexport type FieldFilterControl =\n /** Text, textarea and dynamic (data-source) options: CONTAINS on a text input. */\n | { readonly kind: 'contains' }\n /** Static options: IN on a multi-select. */\n | { readonly kind: 'option-in'; readonly options: readonly FormFieldOption[] }\n /** Member and org unit: IN on comma-separated ids (no picker yet). */\n | { readonly kind: 'id-in' }\n /** Number and money: RANGE with two number inputs. */\n | { readonly kind: 'number-range' }\n /** Date: RANGE with a date range picker. */\n | { readonly kind: 'date-range' }\n /** Datetime: RANGE with a datetime range picker. */\n | { readonly kind: 'datetime-range' }\n /** Boolean: EQUALS with a yes / no select. */\n | { readonly kind: 'boolean' };\n\nexport function readFieldFilterControl(\n item: FilterableFieldPath,\n): FieldFilterControl {\n switch (item.kind) {\n case 'number':\n return { kind: 'number-range' };\n case 'date':\n return { kind: 'date-range' };\n case 'datetime':\n return { kind: 'datetime-range' };\n case 'boolean':\n return { kind: 'boolean' };\n case 'member':\n case 'org_unit':\n return { kind: 'id-in' };\n case 'option':\n return item.staticOptions\n ? { kind: 'option-in', options: item.staticOptions }\n : { kind: 'contains' };\n case 'text':\n return { kind: 'contains' };\n }\n}\n\n/**\n * One field's entered condition. `from` and `to` hold canonical values\n * (decimal string, `YYYY-MM-DD`, or ISO 8601) as the controls produce them.\n */\nexport interface FieldFilterDraft {\n readonly from: string;\n readonly to: string;\n /** CONTAINS text, or comma-separated ids for IN on member and org unit. */\n readonly text: string;\n /** Selected option values (IN), or `['true']` / `['false']` for boolean. */\n readonly values: readonly string[];\n}\n\nexport const EMPTY_FIELD_FILTER_DRAFT: FieldFilterDraft = {\n from: '',\n text: '',\n to: '',\n values: [],\n};\n\n/** Drafts keyed by the field path, so a path can be looked up directly. */\nexport type FieldFilterDrafts = Readonly<Record<string, FieldFilterDraft>>;\n\nconst DECIMAL_PATTERN = /^-?\\d+(\\.\\d+)?$/u;\n\n/**\n * Whether a number control's text is a decimal. Surrounding spaces and\n * thousands separators (`1,000`) are accepted and dropped before sending.\n */\nexport function isDecimalText(value: string): boolean {\n return readDecimal(value) !== null;\n}\n\nfunction readDecimal(value: string): string | null {\n const plain = value.trim().replace(/,/gu, '');\n\n return DECIMAL_PATTERN.test(plain) ? plain : null;\n}\n\nfunction readIdList(text: string): readonly string[] {\n return text\n .split(',')\n .map((item) => item.trim())\n .filter((item) => item.length > 0);\n}\n\n/**\n * The condition one draft asks for, or `null` when it is empty. Empty means\n * no condition, so it must not reach the server as a filter.\n */\nexport function toApprovalInstanceFieldFilter(\n item: FilterableFieldPath,\n draft: FieldFilterDraft,\n): ApprovalInstanceFieldFilter | null {\n const control = readFieldFilterControl(item);\n\n switch (control.kind) {\n case 'contains': {\n const text = draft.text.trim();\n\n return text\n ? { fieldKey: item.path, operator: 'CONTAINS', values: [text] }\n : null;\n }\n case 'option-in':\n return draft.values.length > 0\n ? { fieldKey: item.path, operator: 'IN', values: [...draft.values] }\n : null;\n case 'id-in': {\n const ids = readIdList(draft.text);\n\n return ids.length > 0\n ? { fieldKey: item.path, operator: 'IN', values: ids }\n : null;\n }\n case 'number-range': {\n const from = readDecimal(draft.from);\n const to = readDecimal(draft.to);\n\n return from || to\n ? {\n fieldKey: item.path,\n operator: 'RANGE',\n ...(from ? { from } : {}),\n ...(to ? { to } : {}),\n }\n : null;\n }\n case 'date-range':\n case 'datetime-range': {\n const from = draft.from.trim();\n const to = draft.to.trim();\n\n return from || to\n ? {\n fieldKey: item.path,\n operator: 'RANGE',\n ...(from ? { from } : {}),\n ...(to ? { to } : {}),\n }\n : null;\n }\n case 'boolean': {\n const value = draft.values[0];\n\n return value === 'true' || value === 'false'\n ? { fieldKey: item.path, operator: 'EQUALS', values: [value] }\n : null;\n }\n }\n}\n\n/** Every non-empty condition, in schema order. */\nexport function buildApprovalInstanceFieldFilters(\n paths: readonly FilterableFieldPath[],\n drafts: FieldFilterDrafts,\n): readonly ApprovalInstanceFieldFilter[] {\n return paths.flatMap((item): readonly ApprovalInstanceFieldFilter[] => {\n const filter = toApprovalInstanceFieldFilter(\n item,\n drafts[item.path] ?? EMPTY_FIELD_FILTER_DRAFT,\n );\n\n return filter ? [filter] : [];\n });\n}\n\n/** The sort the user picked. An empty `field` keeps the server default. */\nexport interface SortDraft {\n readonly direction: ApprovalInstanceSortDirection;\n readonly field: string;\n}\n\nexport const DEFAULT_SORT_DRAFT: SortDraft = { direction: 'DESC', field: '' };\n\nexport interface SortFieldOption {\n readonly id: string;\n readonly label: string;\n}\n\nexport const DEFAULT_SORT_OPTION: SortFieldOption = {\n id: '',\n label: '預設(建立時間,新到舊)',\n};\n\nconst SORT_TIME_LABELS: Readonly<\n Record<(typeof APPROVAL_INSTANCE_SORT_TIME_FIELDS)[number], string>\n> = {\n completedAt: '完成時間',\n submittedAt: '送出時間',\n updatedAt: '最後更新',\n};\n\n/**\n * Sort choices. Time fields are always offered. Form fields are offered only\n * with a template selected, because the server needs a template to sort by a\n * form field.\n */\nexport function readSortFieldOptions(\n paths: readonly FilterableFieldPath[],\n templateSelected: boolean,\n): readonly SortFieldOption[] {\n const timeOptions = APPROVAL_INSTANCE_SORT_TIME_FIELDS.map(\n (field): SortFieldOption => ({ id: field, label: SORT_TIME_LABELS[field] }),\n );\n const fieldOptions = templateSelected\n ? paths\n .filter(isFilterableFieldSortable)\n .map((item): SortFieldOption => ({ id: item.path, label: item.label }))\n : [];\n\n return [DEFAULT_SORT_OPTION, ...timeOptions, ...fieldOptions];\n}\n\n/** Whether a sort on `field` is valid for the current template selection. */\nexport function isSortFieldAvailable(\n field: string,\n paths: readonly FilterableFieldPath[],\n templateSelected: boolean,\n): boolean {\n if (field === '') {\n return true;\n }\n\n if (\n (APPROVAL_INSTANCE_SORT_TIME_FIELDS as readonly string[]).includes(field)\n ) {\n return true;\n }\n\n return templateSelected && paths.some((item) => item.path === field);\n}\n\nexport function toApprovalInstanceSort(\n draft: SortDraft,\n): readonly ApprovalInstanceSort[] | undefined {\n return draft.field === ''\n ? undefined\n : [{ direction: draft.direction, field: draft.field }];\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport {\n DateRangePicker,\n DateTimeRangePicker,\n Filter,\n FormField,\n Input,\n Select,\n type FilterProps,\n} from '@mezzanine-ui/react';\nimport {\n ControlFieldSlotLayout,\n FormFieldLayout,\n} from '@mezzanine-ui/core/form';\nimport {\n formatDatePickerValue,\n formatDateTimePickerValue,\n} from '@rytass/bpm-core-client/form';\nimport type { FilterableFieldPath } from '@rytass/bpm-core-shared';\nimport {\n EMPTY_FIELD_FILTER_DRAFT,\n isDecimalText,\n readFieldFilterControl,\n type FieldFilterDraft,\n type FieldFilterDrafts,\n} from './approval-instance-field-filters';\n\nexport type FieldFilterDraftChange = (\n path: string,\n draft: FieldFilterDraft,\n) => void;\n\ntype SelectChoice = Readonly<{ id: string; name: string }>;\n\nconst BOOLEAN_CHOICES: readonly SelectChoice[] = [\n { id: 'true', name: '是' },\n { id: 'false', name: '否' },\n];\n\n/**\n * One list-page `Filter` per condition the template's filterable fields need.\n * A number field yields two (lower and upper bound) because Mezzanine has no\n * number range control. Returned as elements so the caller can place them\n * directly inside `FilterLine`, which only accepts `Filter` children.\n */\nexport function readFieldFilterElements(\n paths: readonly FilterableFieldPath[],\n drafts: FieldFilterDrafts,\n onDraftChange: FieldFilterDraftChange,\n): readonly ReactElement<FilterProps>[] {\n return paths.flatMap((item): readonly ReactElement<FilterProps>[] => {\n const draft = drafts[item.path] ?? EMPTY_FIELD_FILTER_DRAFT;\n const change = (next: FieldFilterDraft): void => {\n onDraftChange(item.path, next);\n };\n const control = readFieldFilterControl(item);\n\n if (control.kind === 'number-range') {\n return [\n renderFilter(\n `${item.path}:from`,\n `${item.label}(下限)`,\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, from: event.target.value });\n }}\n placeholder={`${item.label}:下限`}\n size=\"sub\"\n value={draft.from}\n variant=\"base\"\n />,\n readNumberHint(draft.from),\n ),\n renderFilter(\n `${item.path}:to`,\n `${item.label}(上限)`,\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, to: event.target.value });\n }}\n placeholder={`${item.label}:上限`}\n size=\"sub\"\n value={draft.to}\n variant=\"base\"\n />,\n readNumberHint(draft.to),\n ),\n ];\n }\n\n return [\n renderFilter(\n item.path,\n item.label,\n renderControl(item, draft, change, item.label),\n control.kind === 'id-in'\n ? `多個${readIdNoun(item)}以逗號分隔`\n : undefined,\n ),\n ];\n });\n}\n\nfunction renderFilter(\n key: string,\n label: string,\n control: ReactElement,\n hintText?: string,\n): ReactElement<FilterProps> {\n return (\n <Filter key={key} span={2}>\n <FormField\n fullWidth\n hintText={hintText}\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label={label}\n name={`fieldFilter-${key}`}\n >\n {control}\n </FormField>\n </Filter>\n );\n}\n\n/** What an id-in field holds: members, or org units for an `org_unit` field. */\nfunction readIdNoun(item: FilterableFieldPath): string {\n return item.kind === 'org_unit' ? '組織' : '成員';\n}\n\nfunction readNumberHint(value: string): string | undefined {\n return !value || isDecimalText(value) ? undefined : '請輸入數字';\n}\n\nfunction renderControl(\n item: FilterableFieldPath,\n draft: FieldFilterDraft,\n change: (next: FieldFilterDraft) => void,\n label: string,\n): ReactElement {\n // The list page hides FormField labels to keep its filter rows compact, so\n // each control names its field in the placeholder, as the state filter does.\n const control = readFieldFilterControl(item);\n\n if (control.kind === 'option-in') {\n const choices = control.options.map((option): SelectChoice => ({\n id: option.value,\n name: option.label,\n }));\n\n return (\n <Select\n clearable\n fullWidth\n mode=\"multiple\"\n onChange={(selected: readonly SelectChoice[]): void => {\n change({ ...draft, values: selected.map((option) => option.id) });\n }}\n options={[...choices]}\n placeholder={`${label}:全部`}\n size=\"sub\"\n value={choices.filter((choice) => draft.values.includes(choice.id))}\n />\n );\n }\n\n if (control.kind === 'boolean') {\n const selected = BOOLEAN_CHOICES.find((choice) =>\n draft.values.includes(choice.id),\n );\n\n return (\n <Select\n clearable\n fullWidth\n onChange={(option: SelectChoice | null): void => {\n change({ ...draft, values: option ? [option.id] : [] });\n }}\n options={[...BOOLEAN_CHOICES]}\n placeholder={`${label}:全部`}\n size=\"sub\"\n value={selected ?? null}\n />\n );\n }\n\n if (control.kind === 'date-range') {\n return (\n <DateRangePicker\n clearable\n fullWidth\n inputFromPlaceholder={`${label}:起`}\n inputToPlaceholder=\"迄\"\n onChange={(target?: readonly (string | undefined)[]): void => {\n change({\n ...draft,\n from: formatDatePickerValue(target?.[0]) ?? '',\n to: formatDatePickerValue(target?.[1]) ?? '',\n });\n }}\n size=\"sub\"\n value={draft.from && draft.to ? [draft.from, draft.to] : []}\n />\n );\n }\n\n if (control.kind === 'datetime-range') {\n return (\n <DateTimeRangePicker\n clearable\n fullWidth\n onChange={(value): void => {\n change({\n ...draft,\n from: formatDateTimePickerValue(value[0]) ?? '',\n to: formatDateTimePickerValue(value[1]) ?? '',\n });\n }}\n size=\"sub\"\n value={[draft.from || undefined, draft.to || undefined]}\n />\n );\n }\n\n if (control.kind === 'id-in') {\n return (\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, text: event.target.value });\n }}\n placeholder={`${label}:${readIdNoun(item)} ID`}\n size=\"sub\"\n value={draft.text}\n variant=\"base\"\n />\n );\n }\n\n // contains (text, textarea and data-source options)\n return (\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, text: event.target.value });\n }}\n placeholder={`${label}:包含`}\n size=\"sub\"\n value={draft.text}\n variant=\"base\"\n />\n );\n}\n",".instanceFilterArea {\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 RadioGroup,\n Section,\n SectionGroup,\n Select,\n Table,\n Typography,\n type FilterLineProps,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport {\n ControlFieldSlotLayout,\n FormFieldLayout,\n} 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 listApprovalTemplates,\n type ApprovalTemplateRecord,\n} from '@rytass/bpm-core-client/template';\nimport {\n listApprovalInstancesPage,\n readApprovalInstanceCaseTitle,\n readApprovalTemplateCurrentFormSchema,\n type ApprovalInstanceRecord,\n type ApprovalInstanceState,\n type ApprovalInstanceView,\n} from '@rytass/bpm-core-client/workflow';\nimport {\n readFilterableFieldPaths,\n type ApprovalInstanceFieldFilter,\n type ApprovalInstanceSort,\n type ApprovalInstanceSortDirection,\n type FilterableFieldPath,\n type FormDefinitionSchema,\n} from '@rytass/bpm-core-shared';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { formatDateTime } from '../lib/format-date-time';\nimport {\n buildApprovalInstanceFieldFilters,\n DEFAULT_SORT_DRAFT,\n DEFAULT_SORT_OPTION,\n isSortFieldAvailable,\n readSortFieldOptions,\n toApprovalInstanceSort,\n type FieldFilterDraft,\n type FieldFilterDrafts,\n type SortDraft,\n type SortFieldOption,\n} from './approval-instance-field-filters';\nimport { readFieldFilterElements } from './approval-instance-field-filter-controls';\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\ntype SelectChoice = Readonly<{ id: string; name: string }>;\n\nconst INSTANCE_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst FIELD_FILTERS_PER_LINE = 3;\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];\nconst SORT_DIRECTION_OPTIONS: readonly (SelectChoice & {\n id: ApprovalInstanceSortDirection;\n})[] = [\n { id: 'DESC', name: '新到舊' },\n { id: 'ASC', name: '舊到新' },\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 *\n * With a template selected, the filter bar also carries one control per\n * filterable field of that template's current form (ADR 25 §7), and the sort\n * may use those fields. Without a template only time fields can sort.\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 [templates, setTemplates] = useState<readonly ApprovalTemplateRecord[]>(\n [],\n );\n const [templateId, setTemplateId] = useState<string | null>(null);\n const [templateSchema, setTemplateSchema] =\n useState<FormDefinitionSchema | null>(null);\n const [schemaLoading, setSchemaLoading] = useState(false);\n const [schemaError, setSchemaError] = useState<string | null>(null);\n const [fieldDrafts, setFieldDrafts] = useState<FieldFilterDrafts>({});\n const [sortDraft, setSortDraft] = useState<SortDraft>(DEFAULT_SORT_DRAFT);\n const refreshRequestIdRef = useRef(0);\n\n const fieldPaths = useMemo(\n (): readonly FilterableFieldPath[] =>\n templateSchema ? readFilterableFieldPaths(templateSchema) : [],\n [templateSchema],\n );\n const fieldFilters = useMemo(\n (): readonly ApprovalInstanceFieldFilter[] | undefined =>\n templateId === null\n ? undefined\n : buildApprovalInstanceFieldFilters(fieldPaths, fieldDrafts),\n [fieldDrafts, fieldPaths, templateId],\n );\n const sort = useMemo(\n (): readonly ApprovalInstanceSort[] | undefined =>\n toApprovalInstanceSort(sortDraft),\n [sortDraft],\n );\n const sortOptions = useMemo(\n (): readonly SortFieldOption[] =>\n readSortFieldOptions(fieldPaths, templateId !== null),\n [fieldPaths, templateId],\n );\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 fieldFilters,\n page: instancePage,\n pageSize: instancePageSize,\n searchText,\n sort,\n state: stateFilter.state,\n templateId,\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 }, [\n fieldFilters,\n instancePage,\n instancePageSize,\n searchText,\n sort,\n stateFilter,\n templateId,\n view,\n ]);\n\n useEffect((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n\n useEffect((): (() => void) | void => {\n let cancelled = false;\n\n void (async (): Promise<void> => {\n try {\n const loaded = await listApprovalTemplates();\n\n if (!cancelled) {\n setTemplates(loaded);\n }\n } catch {\n if (!cancelled) {\n setTemplates([]);\n }\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, []);\n\n // Race guard: a schema response for a previously picked template is dropped\n // when the template changes again (cleanup flips `cancelled`).\n useEffect((): (() => void) | void => {\n if (templateId === null) {\n setTemplateSchema(null);\n setSchemaLoading(false);\n setSchemaError(null);\n\n return;\n }\n\n let cancelled = false;\n setSchemaLoading(true);\n setSchemaError(null);\n\n void (async (): Promise<void> => {\n try {\n const schema = await readApprovalTemplateCurrentFormSchema(templateId);\n\n if (!cancelled) {\n setTemplateSchema(schema);\n }\n } catch (schemaLoadError: unknown) {\n if (!cancelled) {\n setTemplateSchema(null);\n setSchemaError(readErrorMessage(schemaLoadError));\n }\n } finally {\n if (!cancelled) {\n setSchemaLoading(false);\n }\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, [templateId]);\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 handleTemplateChange = useCallback(\n (option: SelectChoice | null): void => {\n setTemplateId(option?.id ?? null);\n // Drop the previous template's fields at once, so nothing is built from\n // them while the new schema loads.\n setTemplateSchema(null);\n setFieldDrafts({});\n setInstancePage(1);\n // A form-field sort belongs to one template. Time-field sorts survive.\n setSortDraft((previous): SortDraft =>\n isSortFieldAvailable(previous.field, [], false)\n ? previous\n : { ...previous, field: DEFAULT_SORT_DRAFT.field },\n );\n },\n [],\n );\n\n const handleFilterReset = useCallback((): void => {\n setSearchText('');\n setStateFilter(readStateFilterOption(defaultState));\n setTemplateId(null);\n setTemplateSchema(null);\n setFieldDrafts({});\n setSortDraft(DEFAULT_SORT_DRAFT);\n setInstancePage(1);\n }, [defaultState]);\n const handleFilterSubmit = useCallback((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n const handleFieldDraftChange = useCallback(\n (path: string, draft: FieldFilterDraft): void => {\n setFieldDrafts((previous): FieldFilterDrafts => ({\n ...previous,\n [path]: draft,\n }));\n setInstancePage(1);\n },\n [],\n );\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 const templateOptions = useMemo(\n (): readonly SelectChoice[] =>\n templates.map((template) => ({ id: template.id, name: template.name })),\n [templates],\n );\n const selectedTemplate = templateOptions.find(\n (option) => option.id === templateId,\n );\n const fieldFilterLines = useMemo(\n (): readonly ReactElement<FilterLineProps>[] =>\n chunkElements(\n readFieldFilterElements(\n fieldPaths,\n fieldDrafts,\n handleFieldDraftChange,\n ),\n FIELD_FILTERS_PER_LINE,\n ).map((elements, index): ReactElement<FilterLineProps> => (\n <FilterLine key={`field-filter-line-${index}`}>\n {[...elements]}\n </FilterLine>\n )),\n [fieldDrafts, fieldPaths, handleFieldDraftChange],\n );\n const sortFieldChoices = useMemo(\n (): readonly SelectChoice[] =>\n sortOptions.map((option) => ({ id: option.id, name: option.label })),\n [sortOptions],\n );\n const selectedSortField =\n sortFieldChoices.find((option) => option.id === sortDraft.field) ??\n sortFieldChoices.find((option) => option.id === DEFAULT_SORT_OPTION.id);\n\n const searchFilterLine = (\n <FilterLine key=\"search\">\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceSearchText\"\n >\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): 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 controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\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 <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"範本\"\n name=\"instanceTemplate\"\n >\n <Select\n clearable\n fullWidth\n onChange={handleTemplateChange}\n options={[...templateOptions]}\n placeholder=\"全部範本\"\n size=\"sub\"\n value={selectedTemplate ?? null}\n />\n </FormField>\n </Filter>\n </FilterLine>\n );\n\n const sortFilterLine = (\n <FilterLine key=\"sort\">\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"排序欄位\"\n name=\"instanceSortField\"\n >\n <Select\n clearable={false}\n fullWidth\n onChange={(option: SelectChoice | null): void => {\n setSortDraft((previous): SortDraft => ({\n ...previous,\n field: option?.id ?? DEFAULT_SORT_DRAFT.field,\n }));\n setInstancePage(1);\n }}\n options={[...sortFieldChoices]}\n placeholder=\"排序欄位\"\n size=\"sub\"\n value={selectedSortField ?? null}\n />\n </FormField>\n </Filter>\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"排序方向\"\n name=\"instanceSortDirection\"\n >\n <RadioGroup\n name=\"instanceSortDirection\"\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setSortDraft((previous): SortDraft => ({\n ...previous,\n direction: readSortDirection(event.target.value),\n }));\n setInstancePage(1);\n }}\n options={[...SORT_DIRECTION_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={sortDraft.direction}\n />\n </FormField>\n </Filter>\n </FilterLine>\n );\n\n return (\n <>\n <PageHeader>\n <ContentHeader description={description} title={title} />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea\n className={styles.instanceFilterArea}\n onReset={handleFilterReset}\n onSubmit={handleFilterSubmit}\n resetText=\"清除條件\"\n rowAlign=\"end\"\n size=\"sub\"\n submitText=\"重新查詢\"\n >\n {[searchFilterLine, ...fieldFilterLines, sortFilterLine]}\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n {schemaError ? (\n <Typography color=\"text-error\" variant=\"body\">\n {schemaError}\n </Typography>\n ) : null}\n {templateId !== null &&\n !schemaLoading &&\n !schemaError &&\n templateSchema &&\n fieldPaths.length === 0 ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n 此範本沒有可篩選的欄位。\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 chunkElements<T>(\n items: readonly T[],\n size: number,\n): readonly (readonly T[])[] {\n return items.reduce<readonly (readonly T[])[]>(\n (lines, item): readonly (readonly T[])[] => {\n const last = lines[lines.length - 1];\n\n return last && last.length < size\n ? [...lines.slice(0, -1), [...last, item]]\n : [...lines, [item]];\n },\n [],\n );\n}\n\nfunction readSortDirection(value: string): ApprovalInstanceSortDirection {\n return value === 'ASC' ? 'ASC' : 'DESC';\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-BI5fIXc-.js","names":["_Fragment","styles","useRouterAdapter","useBPMRoutes","formatDateTime","Fragment$1","ContentHeader","readFieldFilterControl","item","EMPTY_FIELD_FILTER_DRAFT","DECIMAL_PATTERN","isDecimalText","readDecimal","value","plain","readIdList","text","toApprovalInstanceFieldFilter","draft","ids","from","to","buildApprovalInstanceFieldFilters","paths","filter","drafts","DEFAULT_SORT_DRAFT","DEFAULT_SORT_OPTION","SORT_TIME_LABELS","readSortFieldOptions","timeOptions","APPROVAL_INSTANCE_SORT_TIME_FIELDS","field","fieldOptions","templateSelected","isFilterableFieldSortable","isSortFieldAvailable","toApprovalInstanceSort","BOOLEAN_CHOICES","readFieldFilterElements","change","next","control","renderFilter","Input","event","readNumberHint","renderControl","readIdNoun","Filter","FormField","ControlFieldSlotLayout","FormFieldLayout","key","choices","option","Select","selected","label","choice","DateRangePicker","formatDatePickerValue","target","DateTimeRangePicker","formatDateTimePickerValue","approval_instance_list_page_module_default","INSTANCE_PAGE_SIZE_OPTIONS","FIELD_FILTERS_PER_LINE","STATE_FILTER_OPTIONS","SORT_DIRECTION_OPTIONS","ApprovalInstanceListPage","router","routes","error","setError","useState","initiatorProfilesById","setInitiatorProfilesById","instancePage","setInstancePage","instancePageSize","setInstancePageSize","instanceTotalCount","setInstanceTotalCount","loading","setLoading","rows","setRows","searchText","setSearchText","stateFilter","setStateFilter","readStateFilterOption","defaultState","templates","setTemplates","templateId","setTemplateId","templateSchema","setTemplateSchema","schemaLoading","setSchemaLoading","schemaError","setSchemaError","fieldDrafts","setFieldDrafts","sortDraft","setSortDraft","refreshRequestIdRef","useRef","fieldPaths","useMemo","readFilterableFieldPaths","fieldFilters","sort","sortOptions","refreshInstances","useCallback","requestId","result","listApprovalInstancesPage","readApprovalInstanceRow","requestError","readErrorMessage","view","useEffect","cancelled","loaded","listApprovalTemplates","schema","readApprovalTemplateCurrentFormSchema","schemaLoadError","initiatorMemberIds","row","profiles","resolveMembers","profile","handleTemplateChange","previous","handleFilterReset","handleFilterSubmit","handleFieldDraftChange","path","columns","Typography","readInstanceStateColor","record","readInitiatorLabel","tableActions","templateOptions","template","selectedTemplate","fieldFilterLines","chunkElements","FilterLine","elements","index","sortFieldChoices","selectedSortField","searchFilterLine","searchPlaceholder","readSelectedStateFilterOption","readStateFilterLabel","sortFilterLine","RadioGroup","readSortDirection","PageHeader","SectionGroup","Section","FilterArea","emptyMessage","Table","page","pageSize","total","items","last","lines","size","instance","readApprovalInstanceCaseTitle","readInstanceStateLabel","isStateFilterOption","state","trimmedMemberId","initiatorMemberId"],"sources":["../../src/components/approval-instance-field-filters.ts","../../src/components/approval-instance-field-filter-controls.tsx","../../src/components/approval-instance-list-page.tsx"],"sourcesContent":["import {\n APPROVAL_INSTANCE_SORT_TIME_FIELDS,\n isFilterableFieldSortable,\n type ApprovalInstanceFieldFilter,\n type ApprovalInstanceSort,\n type ApprovalInstanceSortDirection,\n type FilterableFieldPath,\n type FormFieldOption,\n} from '@rytass/bpm-core-shared';\n\n/**\n * Pure logic behind the list page's field filters (ADR 25 §7). Turns the\n * filterable fields of a template's schema into controls, and the control\n * values into `fieldFilters` and `sort` for `listApprovalInstancesPage`. The\n * operator for each control is one `readFilterOperatorsForFieldType` allows.\n */\n\n/** Which control a filterable field gets on the list page. */\nexport type FieldFilterControl =\n /** Text, textarea and dynamic (data-source) options: CONTAINS on a text input. */\n | { readonly kind: 'contains' }\n /** Static options: IN on a multi-select. */\n | { readonly kind: 'option-in'; readonly options: readonly FormFieldOption[] }\n /** Member and org unit: IN on comma-separated ids (no picker yet). */\n | { readonly kind: 'id-in' }\n /** Number and money: RANGE with two number inputs. */\n | { readonly kind: 'number-range' }\n /** Date: RANGE with a date range picker. */\n | { readonly kind: 'date-range' }\n /** Datetime: RANGE with a datetime range picker. */\n | { readonly kind: 'datetime-range' }\n /** Boolean: EQUALS with a yes / no select. */\n | { readonly kind: 'boolean' };\n\nexport function readFieldFilterControl(\n item: FilterableFieldPath,\n): FieldFilterControl {\n switch (item.kind) {\n case 'number':\n return { kind: 'number-range' };\n case 'date':\n return { kind: 'date-range' };\n case 'datetime':\n return { kind: 'datetime-range' };\n case 'boolean':\n return { kind: 'boolean' };\n case 'member':\n case 'org_unit':\n return { kind: 'id-in' };\n case 'option':\n return item.staticOptions\n ? { kind: 'option-in', options: item.staticOptions }\n : { kind: 'contains' };\n case 'text':\n return { kind: 'contains' };\n }\n}\n\n/**\n * One field's entered condition. `from` and `to` hold canonical values\n * (decimal string, `YYYY-MM-DD`, or ISO 8601) as the controls produce them.\n */\nexport interface FieldFilterDraft {\n readonly from: string;\n readonly to: string;\n /** CONTAINS text, or comma-separated ids for IN on member and org unit. */\n readonly text: string;\n /** Selected option values (IN), or `['true']` / `['false']` for boolean. */\n readonly values: readonly string[];\n}\n\nexport const EMPTY_FIELD_FILTER_DRAFT: FieldFilterDraft = {\n from: '',\n text: '',\n to: '',\n values: [],\n};\n\n/** Drafts keyed by the field path, so a path can be looked up directly. */\nexport type FieldFilterDrafts = Readonly<Record<string, FieldFilterDraft>>;\n\nconst DECIMAL_PATTERN = /^-?\\d+(\\.\\d+)?$/u;\n\n/**\n * Whether a number control's text is a decimal. Surrounding spaces and\n * thousands separators (`1,000`) are accepted and dropped before sending.\n */\nexport function isDecimalText(value: string): boolean {\n return readDecimal(value) !== null;\n}\n\nfunction readDecimal(value: string): string | null {\n const plain = value.trim().replace(/,/gu, '');\n\n return DECIMAL_PATTERN.test(plain) ? plain : null;\n}\n\nfunction readIdList(text: string): readonly string[] {\n return text\n .split(',')\n .map((item) => item.trim())\n .filter((item) => item.length > 0);\n}\n\n/**\n * The condition one draft asks for, or `null` when it is empty. Empty means\n * no condition, so it must not reach the server as a filter.\n */\nexport function toApprovalInstanceFieldFilter(\n item: FilterableFieldPath,\n draft: FieldFilterDraft,\n): ApprovalInstanceFieldFilter | null {\n const control = readFieldFilterControl(item);\n\n switch (control.kind) {\n case 'contains': {\n const text = draft.text.trim();\n\n return text\n ? { fieldKey: item.path, operator: 'CONTAINS', values: [text] }\n : null;\n }\n case 'option-in':\n return draft.values.length > 0\n ? { fieldKey: item.path, operator: 'IN', values: [...draft.values] }\n : null;\n case 'id-in': {\n const ids = readIdList(draft.text);\n\n return ids.length > 0\n ? { fieldKey: item.path, operator: 'IN', values: ids }\n : null;\n }\n case 'number-range': {\n const from = readDecimal(draft.from);\n const to = readDecimal(draft.to);\n\n return from || to\n ? {\n fieldKey: item.path,\n operator: 'RANGE',\n ...(from ? { from } : {}),\n ...(to ? { to } : {}),\n }\n : null;\n }\n case 'date-range':\n case 'datetime-range': {\n const from = draft.from.trim();\n const to = draft.to.trim();\n\n return from || to\n ? {\n fieldKey: item.path,\n operator: 'RANGE',\n ...(from ? { from } : {}),\n ...(to ? { to } : {}),\n }\n : null;\n }\n case 'boolean': {\n const value = draft.values[0];\n\n return value === 'true' || value === 'false'\n ? { fieldKey: item.path, operator: 'EQUALS', values: [value] }\n : null;\n }\n }\n}\n\n/** Every non-empty condition, in schema order. */\nexport function buildApprovalInstanceFieldFilters(\n paths: readonly FilterableFieldPath[],\n drafts: FieldFilterDrafts,\n): readonly ApprovalInstanceFieldFilter[] {\n return paths.flatMap((item): readonly ApprovalInstanceFieldFilter[] => {\n const filter = toApprovalInstanceFieldFilter(\n item,\n drafts[item.path] ?? EMPTY_FIELD_FILTER_DRAFT,\n );\n\n return filter ? [filter] : [];\n });\n}\n\n/** The sort the user picked. An empty `field` keeps the server default. */\nexport interface SortDraft {\n readonly direction: ApprovalInstanceSortDirection;\n readonly field: string;\n}\n\nexport const DEFAULT_SORT_DRAFT: SortDraft = { direction: 'DESC', field: '' };\n\nexport interface SortFieldOption {\n readonly id: string;\n readonly label: string;\n}\n\nexport const DEFAULT_SORT_OPTION: SortFieldOption = {\n id: '',\n label: '預設(建立時間,新到舊)',\n};\n\nconst SORT_TIME_LABELS: Readonly<\n Record<(typeof APPROVAL_INSTANCE_SORT_TIME_FIELDS)[number], string>\n> = {\n completedAt: '完成時間',\n submittedAt: '送出時間',\n updatedAt: '最後更新',\n};\n\n/**\n * Sort choices. Time fields are always offered. Form fields are offered only\n * with a template selected, because the server needs a template to sort by a\n * form field.\n */\nexport function readSortFieldOptions(\n paths: readonly FilterableFieldPath[],\n templateSelected: boolean,\n): readonly SortFieldOption[] {\n const timeOptions = APPROVAL_INSTANCE_SORT_TIME_FIELDS.map(\n (field): SortFieldOption => ({ id: field, label: SORT_TIME_LABELS[field] }),\n );\n const fieldOptions = templateSelected\n ? paths\n .filter(isFilterableFieldSortable)\n .map((item): SortFieldOption => ({ id: item.path, label: item.label }))\n : [];\n\n return [DEFAULT_SORT_OPTION, ...timeOptions, ...fieldOptions];\n}\n\n/** Whether a sort on `field` is valid for the current template selection. */\nexport function isSortFieldAvailable(\n field: string,\n paths: readonly FilterableFieldPath[],\n templateSelected: boolean,\n): boolean {\n if (field === '') {\n return true;\n }\n\n if (\n (APPROVAL_INSTANCE_SORT_TIME_FIELDS as readonly string[]).includes(field)\n ) {\n return true;\n }\n\n return templateSelected && paths.some((item) => item.path === field);\n}\n\nexport function toApprovalInstanceSort(\n draft: SortDraft,\n): readonly ApprovalInstanceSort[] | undefined {\n return draft.field === ''\n ? undefined\n : [{ direction: draft.direction, field: draft.field }];\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport {\n DateRangePicker,\n DateTimeRangePicker,\n Filter,\n FormField,\n Input,\n Select,\n type FilterProps,\n} from '@mezzanine-ui/react';\nimport {\n ControlFieldSlotLayout,\n FormFieldLayout,\n} from '@mezzanine-ui/core/form';\nimport {\n formatDatePickerValue,\n formatDateTimePickerValue,\n} from '@rytass/bpm-core-client/form';\nimport type { FilterableFieldPath } from '@rytass/bpm-core-shared';\nimport {\n EMPTY_FIELD_FILTER_DRAFT,\n isDecimalText,\n readFieldFilterControl,\n type FieldFilterDraft,\n type FieldFilterDrafts,\n} from './approval-instance-field-filters';\n\nexport type FieldFilterDraftChange = (\n path: string,\n draft: FieldFilterDraft,\n) => void;\n\ntype SelectChoice = Readonly<{ id: string; name: string }>;\n\nconst BOOLEAN_CHOICES: readonly SelectChoice[] = [\n { id: 'true', name: '是' },\n { id: 'false', name: '否' },\n];\n\n/**\n * One list-page `Filter` per condition the template's filterable fields need.\n * A number field yields two (lower and upper bound) because Mezzanine has no\n * number range control. Returned as elements so the caller can place them\n * directly inside `FilterLine`, which only accepts `Filter` children.\n */\nexport function readFieldFilterElements(\n paths: readonly FilterableFieldPath[],\n drafts: FieldFilterDrafts,\n onDraftChange: FieldFilterDraftChange,\n): readonly ReactElement<FilterProps>[] {\n return paths.flatMap((item): readonly ReactElement<FilterProps>[] => {\n const draft = drafts[item.path] ?? EMPTY_FIELD_FILTER_DRAFT;\n const change = (next: FieldFilterDraft): void => {\n onDraftChange(item.path, next);\n };\n const control = readFieldFilterControl(item);\n\n if (control.kind === 'number-range') {\n return [\n renderFilter(\n `${item.path}:from`,\n `${item.label}(下限)`,\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, from: event.target.value });\n }}\n placeholder={`${item.label}:下限`}\n size=\"sub\"\n value={draft.from}\n variant=\"base\"\n />,\n readNumberHint(draft.from),\n ),\n renderFilter(\n `${item.path}:to`,\n `${item.label}(上限)`,\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, to: event.target.value });\n }}\n placeholder={`${item.label}:上限`}\n size=\"sub\"\n value={draft.to}\n variant=\"base\"\n />,\n readNumberHint(draft.to),\n ),\n ];\n }\n\n return [\n renderFilter(\n item.path,\n item.label,\n renderControl(item, draft, change, item.label),\n control.kind === 'id-in'\n ? `多個${readIdNoun(item)}以逗號分隔`\n : undefined,\n ),\n ];\n });\n}\n\nfunction renderFilter(\n key: string,\n label: string,\n control: ReactElement,\n hintText?: string,\n): ReactElement<FilterProps> {\n return (\n <Filter key={key} span={2}>\n <FormField\n fullWidth\n hintText={hintText}\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label={label}\n name={`fieldFilter-${key}`}\n >\n {control}\n </FormField>\n </Filter>\n );\n}\n\n/** What an id-in field holds: members, or org units for an `org_unit` field. */\nfunction readIdNoun(item: FilterableFieldPath): string {\n return item.kind === 'org_unit' ? '組織' : '成員';\n}\n\nfunction readNumberHint(value: string): string | undefined {\n return !value || isDecimalText(value) ? undefined : '請輸入數字';\n}\n\nfunction renderControl(\n item: FilterableFieldPath,\n draft: FieldFilterDraft,\n change: (next: FieldFilterDraft) => void,\n label: string,\n): ReactElement {\n // The list page hides FormField labels to keep its filter rows compact, so\n // each control names its field in the placeholder, as the state filter does.\n const control = readFieldFilterControl(item);\n\n if (control.kind === 'option-in') {\n const choices = control.options.map((option): SelectChoice => ({\n id: option.value,\n name: option.label,\n }));\n\n return (\n <Select\n clearable\n fullWidth\n mode=\"multiple\"\n onChange={(selected: readonly SelectChoice[]): void => {\n change({ ...draft, values: selected.map((option) => option.id) });\n }}\n options={[...choices]}\n placeholder={`${label}:全部`}\n size=\"sub\"\n value={choices.filter((choice) => draft.values.includes(choice.id))}\n />\n );\n }\n\n if (control.kind === 'boolean') {\n const selected = BOOLEAN_CHOICES.find((choice) =>\n draft.values.includes(choice.id),\n );\n\n return (\n <Select\n clearable\n fullWidth\n onChange={(option: SelectChoice | null): void => {\n change({ ...draft, values: option ? [option.id] : [] });\n }}\n options={[...BOOLEAN_CHOICES]}\n placeholder={`${label}:全部`}\n size=\"sub\"\n value={selected ?? null}\n />\n );\n }\n\n if (control.kind === 'date-range') {\n return (\n <DateRangePicker\n clearable\n fullWidth\n inputFromPlaceholder={`${label}:起`}\n inputToPlaceholder=\"迄\"\n onChange={(target?: readonly (string | undefined)[]): void => {\n change({\n ...draft,\n from: formatDatePickerValue(target?.[0]) ?? '',\n to: formatDatePickerValue(target?.[1]) ?? '',\n });\n }}\n size=\"sub\"\n value={draft.from && draft.to ? [draft.from, draft.to] : []}\n />\n );\n }\n\n if (control.kind === 'datetime-range') {\n return (\n <DateTimeRangePicker\n clearable\n fullWidth\n onChange={(value): void => {\n change({\n ...draft,\n from: formatDateTimePickerValue(value[0]) ?? '',\n to: formatDateTimePickerValue(value[1]) ?? '',\n });\n }}\n size=\"sub\"\n value={[draft.from || undefined, draft.to || undefined]}\n />\n );\n }\n\n if (control.kind === 'id-in') {\n return (\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, text: event.target.value });\n }}\n placeholder={`${label}:${readIdNoun(item)} ID`}\n size=\"sub\"\n value={draft.text}\n variant=\"base\"\n />\n );\n }\n\n // contains (text, textarea and data-source options)\n return (\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, text: event.target.value });\n }}\n placeholder={`${label}:包含`}\n size=\"sub\"\n value={draft.text}\n variant=\"base\"\n />\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 RadioGroup,\n Section,\n SectionGroup,\n Select,\n Table,\n Typography,\n type FilterLineProps,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport {\n ControlFieldSlotLayout,\n FormFieldLayout,\n} 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 listApprovalTemplates,\n type ApprovalTemplateRecord,\n} from '@rytass/bpm-core-client/template';\nimport {\n listApprovalInstancesPage,\n readApprovalInstanceCaseTitle,\n readApprovalTemplateCurrentFormSchema,\n type ApprovalInstanceRecord,\n type ApprovalInstanceState,\n type ApprovalInstanceView,\n} from '@rytass/bpm-core-client/workflow';\nimport {\n readFilterableFieldPaths,\n type ApprovalInstanceFieldFilter,\n type ApprovalInstanceSort,\n type ApprovalInstanceSortDirection,\n type FilterableFieldPath,\n type FormDefinitionSchema,\n} from '@rytass/bpm-core-shared';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { formatDateTime } from '../lib/format-date-time';\nimport {\n buildApprovalInstanceFieldFilters,\n DEFAULT_SORT_DRAFT,\n DEFAULT_SORT_OPTION,\n isSortFieldAvailable,\n readSortFieldOptions,\n toApprovalInstanceSort,\n type FieldFilterDraft,\n type FieldFilterDrafts,\n type SortDraft,\n type SortFieldOption,\n} from './approval-instance-field-filters';\nimport { readFieldFilterElements } from './approval-instance-field-filter-controls';\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\ntype SelectChoice = Readonly<{ id: string; name: string }>;\n\nconst INSTANCE_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst FIELD_FILTERS_PER_LINE = 3;\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];\nconst SORT_DIRECTION_OPTIONS: readonly (SelectChoice & {\n id: ApprovalInstanceSortDirection;\n})[] = [\n { id: 'DESC', name: '新到舊' },\n { id: 'ASC', name: '舊到新' },\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 *\n * With a template selected, the filter bar also carries one control per\n * filterable field of that template's current form (ADR 25 §7), and the sort\n * may use those fields. Without a template only time fields can sort.\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 [templates, setTemplates] = useState<readonly ApprovalTemplateRecord[]>(\n [],\n );\n const [templateId, setTemplateId] = useState<string | null>(null);\n const [templateSchema, setTemplateSchema] =\n useState<FormDefinitionSchema | null>(null);\n const [schemaLoading, setSchemaLoading] = useState(false);\n const [schemaError, setSchemaError] = useState<string | null>(null);\n const [fieldDrafts, setFieldDrafts] = useState<FieldFilterDrafts>({});\n const [sortDraft, setSortDraft] = useState<SortDraft>(DEFAULT_SORT_DRAFT);\n const refreshRequestIdRef = useRef(0);\n\n const fieldPaths = useMemo(\n (): readonly FilterableFieldPath[] =>\n templateSchema ? readFilterableFieldPaths(templateSchema) : [],\n [templateSchema],\n );\n const fieldFilters = useMemo(\n (): readonly ApprovalInstanceFieldFilter[] | undefined =>\n templateId === null\n ? undefined\n : buildApprovalInstanceFieldFilters(fieldPaths, fieldDrafts),\n [fieldDrafts, fieldPaths, templateId],\n );\n const sort = useMemo(\n (): readonly ApprovalInstanceSort[] | undefined =>\n toApprovalInstanceSort(sortDraft),\n [sortDraft],\n );\n const sortOptions = useMemo(\n (): readonly SortFieldOption[] =>\n readSortFieldOptions(fieldPaths, templateId !== null),\n [fieldPaths, templateId],\n );\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 fieldFilters,\n page: instancePage,\n pageSize: instancePageSize,\n searchText,\n sort,\n state: stateFilter.state,\n templateId,\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 }, [\n fieldFilters,\n instancePage,\n instancePageSize,\n searchText,\n sort,\n stateFilter,\n templateId,\n view,\n ]);\n\n useEffect((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n\n useEffect((): (() => void) | void => {\n let cancelled = false;\n\n void (async (): Promise<void> => {\n try {\n const loaded = await listApprovalTemplates();\n\n if (!cancelled) {\n setTemplates(loaded);\n }\n } catch {\n if (!cancelled) {\n setTemplates([]);\n }\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, []);\n\n // Race guard: a schema response for a previously picked template is dropped\n // when the template changes again (cleanup flips `cancelled`).\n useEffect((): (() => void) | void => {\n if (templateId === null) {\n setTemplateSchema(null);\n setSchemaLoading(false);\n setSchemaError(null);\n\n return;\n }\n\n let cancelled = false;\n setSchemaLoading(true);\n setSchemaError(null);\n\n void (async (): Promise<void> => {\n try {\n const schema = await readApprovalTemplateCurrentFormSchema(templateId);\n\n if (!cancelled) {\n setTemplateSchema(schema);\n }\n } catch (schemaLoadError: unknown) {\n if (!cancelled) {\n setTemplateSchema(null);\n setSchemaError(readErrorMessage(schemaLoadError));\n }\n } finally {\n if (!cancelled) {\n setSchemaLoading(false);\n }\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, [templateId]);\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 handleTemplateChange = useCallback(\n (option: SelectChoice | null): void => {\n setTemplateId(option?.id ?? null);\n // Drop the previous template's fields at once, so nothing is built from\n // them while the new schema loads.\n setTemplateSchema(null);\n setFieldDrafts({});\n setInstancePage(1);\n // A form-field sort belongs to one template. Time-field sorts survive.\n setSortDraft((previous): SortDraft =>\n isSortFieldAvailable(previous.field, [], false)\n ? previous\n : { ...previous, field: DEFAULT_SORT_DRAFT.field },\n );\n },\n [],\n );\n\n const handleFilterReset = useCallback((): void => {\n setSearchText('');\n setStateFilter(readStateFilterOption(defaultState));\n setTemplateId(null);\n setTemplateSchema(null);\n setFieldDrafts({});\n setSortDraft(DEFAULT_SORT_DRAFT);\n setInstancePage(1);\n }, [defaultState]);\n const handleFilterSubmit = useCallback((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n const handleFieldDraftChange = useCallback(\n (path: string, draft: FieldFilterDraft): void => {\n setFieldDrafts((previous): FieldFilterDrafts => ({\n ...previous,\n [path]: draft,\n }));\n setInstancePage(1);\n },\n [],\n );\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 const templateOptions = useMemo(\n (): readonly SelectChoice[] =>\n templates.map((template) => ({ id: template.id, name: template.name })),\n [templates],\n );\n const selectedTemplate = templateOptions.find(\n (option) => option.id === templateId,\n );\n const fieldFilterLines = useMemo(\n (): readonly ReactElement<FilterLineProps>[] =>\n chunkElements(\n readFieldFilterElements(\n fieldPaths,\n fieldDrafts,\n handleFieldDraftChange,\n ),\n FIELD_FILTERS_PER_LINE,\n ).map((elements, index): ReactElement<FilterLineProps> => (\n <FilterLine key={`field-filter-line-${index}`}>\n {[...elements]}\n </FilterLine>\n )),\n [fieldDrafts, fieldPaths, handleFieldDraftChange],\n );\n const sortFieldChoices = useMemo(\n (): readonly SelectChoice[] =>\n sortOptions.map((option) => ({ id: option.id, name: option.label })),\n [sortOptions],\n );\n const selectedSortField =\n sortFieldChoices.find((option) => option.id === sortDraft.field) ??\n sortFieldChoices.find((option) => option.id === DEFAULT_SORT_OPTION.id);\n\n const searchFilterLine = (\n <FilterLine key=\"search\">\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceSearchText\"\n >\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): 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 controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\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 <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"範本\"\n name=\"instanceTemplate\"\n >\n <Select\n clearable\n fullWidth\n onChange={handleTemplateChange}\n options={[...templateOptions]}\n placeholder=\"全部範本\"\n size=\"sub\"\n value={selectedTemplate ?? null}\n />\n </FormField>\n </Filter>\n </FilterLine>\n );\n\n const sortFilterLine = (\n <FilterLine key=\"sort\">\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"排序欄位\"\n name=\"instanceSortField\"\n >\n <Select\n clearable={false}\n fullWidth\n onChange={(option: SelectChoice | null): void => {\n setSortDraft((previous): SortDraft => ({\n ...previous,\n field: option?.id ?? DEFAULT_SORT_DRAFT.field,\n }));\n setInstancePage(1);\n }}\n options={[...sortFieldChoices]}\n placeholder=\"排序欄位\"\n size=\"sub\"\n value={selectedSortField ?? null}\n />\n </FormField>\n </Filter>\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"排序方向\"\n name=\"instanceSortDirection\"\n >\n <RadioGroup\n name=\"instanceSortDirection\"\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setSortDraft((previous): SortDraft => ({\n ...previous,\n direction: readSortDirection(event.target.value),\n }));\n setInstancePage(1);\n }}\n options={[...SORT_DIRECTION_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={sortDraft.direction}\n />\n </FormField>\n </Filter>\n </FilterLine>\n );\n\n return (\n <>\n <PageHeader>\n <ContentHeader description={description} title={title} />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea\n className={styles.instanceFilterArea}\n onReset={handleFilterReset}\n onSubmit={handleFilterSubmit}\n resetText=\"清除條件\"\n rowAlign=\"end\"\n size=\"sub\"\n submitText=\"重新查詢\"\n >\n {[searchFilterLine, ...fieldFilterLines, sortFilterLine]}\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n {schemaError ? (\n <Typography color=\"text-error\" variant=\"body\">\n {schemaError}\n </Typography>\n ) : null}\n {templateId !== null &&\n !schemaLoading &&\n !schemaError &&\n templateSchema &&\n fieldPaths.length === 0 ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n 此範本沒有可篩選的欄位。\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 chunkElements<T>(\n items: readonly T[],\n size: number,\n): readonly (readonly T[])[] {\n return items.reduce<readonly (readonly T[])[]>(\n (lines, item): readonly (readonly T[])[] => {\n const last = lines[lines.length - 1];\n\n return last && last.length < size\n ? [...lines.slice(0, -1), [...last, item]]\n : [...lines, [item]];\n },\n [],\n );\n}\n\nfunction readSortDirection(value: string): ApprovalInstanceSortDirection {\n return value === 'ASC' ? 'ASC' : 'DESC';\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":"approval-instance-list-page-Cv5I1YPX.cjs","names":["APPROVAL_INSTANCE_SORT_TIME_FIELDS","isFilterableFieldSortable","Input","Filter","FormField","ControlFieldSlotLayout","FormFieldLayout","Select","DateRangePicker","formatDatePickerValue","DateTimeRangePicker","formatDateTimePickerValue","useRouterAdapter","useBPMRoutes","useState","useRef","useMemo","readFilterableFieldPaths","useCallback","listApprovalInstancesPage","listApprovalTemplates","readApprovalTemplateCurrentFormSchema","resolveMembers","Typography","formatDateTime","FilterLine","Filter","FormField","ControlFieldSlotLayout","FormFieldLayout","Input","Select","RadioGroup","_Fragment","PageHeader","ContentHeader","SectionGroup","Section","FilterArea","styles","Table","readApprovalInstanceCaseTitle","require_rolldown_runtime","require_router_adapter","require_routes_config","require_format_date_time","react","_mezzanine_ui_react","_rytass_bpm_core_client","react_jsx_runtime","_rytass_bpm_core_client_workflow","_rytass_bpm_core_shared","_mezzanine_ui_core_form","_mezzanine_ui_react_ContentHeader","_rytass_bpm_core_client_template","_rytass_bpm_core_client_form","readFieldFilterControl","item","EMPTY_FIELD_FILTER_DRAFT","DECIMAL_PATTERN","isDecimalText","readDecimal","value","plain","readIdList","text","toApprovalInstanceFieldFilter","draft","ids","from","to","buildApprovalInstanceFieldFilters","paths","filter","drafts","DEFAULT_SORT_DRAFT","DEFAULT_SORT_OPTION","SORT_TIME_LABELS","readSortFieldOptions","timeOptions","field","fieldOptions","templateSelected","isSortFieldAvailable","toApprovalInstanceSort","BOOLEAN_CHOICES","readFieldFilterElements","change","onDraftChange","next","control","renderFilter","event","readNumberHint","renderControl","readIdNoun","key","choices","option","selected","label","choice","target","approval_instance_list_page_module_default","INSTANCE_PAGE_SIZE_OPTIONS","FIELD_FILTERS_PER_LINE","STATE_FILTER_OPTIONS","SORT_DIRECTION_OPTIONS","ApprovalInstanceListPage","router","routes","error","setError","initiatorProfilesById","setInitiatorProfilesById","instancePage","setInstancePage","instancePageSize","setInstancePageSize","instanceTotalCount","setInstanceTotalCount","loading","setLoading","rows","setRows","searchText","setSearchText","stateFilter","setStateFilter","readStateFilterOption","defaultState","templates","setTemplates","templateId","setTemplateId","templateSchema","setTemplateSchema","schemaLoading","setSchemaLoading","schemaError","setSchemaError","fieldDrafts","setFieldDrafts","sortDraft","setSortDraft","refreshRequestIdRef","fieldPaths","fieldFilters","sort","sortOptions","refreshInstances","requestId","result","readApprovalInstanceRow","requestError","readErrorMessage","view","cancelled","loaded","schema","schemaLoadError","initiatorMemberIds","row","profiles","profile","handleTemplateChange","previous","handleFilterReset","handleFilterSubmit","handleFieldDraftChange","path","columns","readInstanceStateColor","record","readInitiatorLabel","tableActions","templateOptions","template","selectedTemplate","fieldFilterLines","chunkElements","elements","index","sortFieldChoices","selectedSortField","searchFilterLine","searchPlaceholder","readSelectedStateFilterOption","readStateFilterLabel","sortFilterLine","readSortDirection","emptyMessage","page","pageSize","total","items","last","lines","size","instance","readInstanceStateLabel","isStateFilterOption","state","trimmedMemberId","initiatorMemberId"],"sources":["../../src/components/approval-instance-field-filters.ts","../../src/components/approval-instance-field-filter-controls.tsx","../../src/components/approval-instance-list-page.module.scss","../../src/components/approval-instance-list-page.tsx"],"sourcesContent":["import {\n APPROVAL_INSTANCE_SORT_TIME_FIELDS,\n isFilterableFieldSortable,\n type ApprovalInstanceFieldFilter,\n type ApprovalInstanceSort,\n type ApprovalInstanceSortDirection,\n type FilterableFieldPath,\n type FormFieldOption,\n} from '@rytass/bpm-core-shared';\n\n/**\n * Pure logic behind the list page's field filters (ADR 25 §7). Turns the\n * filterable fields of a template's schema into controls, and the control\n * values into `fieldFilters` and `sort` for `listApprovalInstancesPage`. The\n * operator for each control is one `readFilterOperatorsForFieldType` allows.\n */\n\n/** Which control a filterable field gets on the list page. */\nexport type FieldFilterControl =\n /** Text, textarea and dynamic (data-source) options: CONTAINS on a text input. */\n | { readonly kind: 'contains' }\n /** Static options: IN on a multi-select. */\n | { readonly kind: 'option-in'; readonly options: readonly FormFieldOption[] }\n /** Member and org unit: IN on comma-separated ids (no picker yet). */\n | { readonly kind: 'id-in' }\n /** Number and money: RANGE with two number inputs. */\n | { readonly kind: 'number-range' }\n /** Date: RANGE with a date range picker. */\n | { readonly kind: 'date-range' }\n /** Datetime: RANGE with a datetime range picker. */\n | { readonly kind: 'datetime-range' }\n /** Boolean: EQUALS with a yes / no select. */\n | { readonly kind: 'boolean' };\n\nexport function readFieldFilterControl(\n item: FilterableFieldPath,\n): FieldFilterControl {\n switch (item.kind) {\n case 'number':\n return { kind: 'number-range' };\n case 'date':\n return { kind: 'date-range' };\n case 'datetime':\n return { kind: 'datetime-range' };\n case 'boolean':\n return { kind: 'boolean' };\n case 'member':\n case 'org_unit':\n return { kind: 'id-in' };\n case 'option':\n return item.staticOptions\n ? { kind: 'option-in', options: item.staticOptions }\n : { kind: 'contains' };\n case 'text':\n return { kind: 'contains' };\n }\n}\n\n/**\n * One field's entered condition. `from` and `to` hold canonical values\n * (decimal string, `YYYY-MM-DD`, or ISO 8601) as the controls produce them.\n */\nexport interface FieldFilterDraft {\n readonly from: string;\n readonly to: string;\n /** CONTAINS text, or comma-separated ids for IN on member and org unit. */\n readonly text: string;\n /** Selected option values (IN), or `['true']` / `['false']` for boolean. */\n readonly values: readonly string[];\n}\n\nexport const EMPTY_FIELD_FILTER_DRAFT: FieldFilterDraft = {\n from: '',\n text: '',\n to: '',\n values: [],\n};\n\n/** Drafts keyed by the field path, so a path can be looked up directly. */\nexport type FieldFilterDrafts = Readonly<Record<string, FieldFilterDraft>>;\n\nconst DECIMAL_PATTERN = /^-?\\d+(\\.\\d+)?$/u;\n\n/**\n * Whether a number control's text is a decimal. Surrounding spaces and\n * thousands separators (`1,000`) are accepted and dropped before sending.\n */\nexport function isDecimalText(value: string): boolean {\n return readDecimal(value) !== null;\n}\n\nfunction readDecimal(value: string): string | null {\n const plain = value.trim().replace(/,/gu, '');\n\n return DECIMAL_PATTERN.test(plain) ? plain : null;\n}\n\nfunction readIdList(text: string): readonly string[] {\n return text\n .split(',')\n .map((item) => item.trim())\n .filter((item) => item.length > 0);\n}\n\n/**\n * The condition one draft asks for, or `null` when it is empty. Empty means\n * no condition, so it must not reach the server as a filter.\n */\nexport function toApprovalInstanceFieldFilter(\n item: FilterableFieldPath,\n draft: FieldFilterDraft,\n): ApprovalInstanceFieldFilter | null {\n const control = readFieldFilterControl(item);\n\n switch (control.kind) {\n case 'contains': {\n const text = draft.text.trim();\n\n return text\n ? { fieldKey: item.path, operator: 'CONTAINS', values: [text] }\n : null;\n }\n case 'option-in':\n return draft.values.length > 0\n ? { fieldKey: item.path, operator: 'IN', values: [...draft.values] }\n : null;\n case 'id-in': {\n const ids = readIdList(draft.text);\n\n return ids.length > 0\n ? { fieldKey: item.path, operator: 'IN', values: ids }\n : null;\n }\n case 'number-range': {\n const from = readDecimal(draft.from);\n const to = readDecimal(draft.to);\n\n return from || to\n ? {\n fieldKey: item.path,\n operator: 'RANGE',\n ...(from ? { from } : {}),\n ...(to ? { to } : {}),\n }\n : null;\n }\n case 'date-range':\n case 'datetime-range': {\n const from = draft.from.trim();\n const to = draft.to.trim();\n\n return from || to\n ? {\n fieldKey: item.path,\n operator: 'RANGE',\n ...(from ? { from } : {}),\n ...(to ? { to } : {}),\n }\n : null;\n }\n case 'boolean': {\n const value = draft.values[0];\n\n return value === 'true' || value === 'false'\n ? { fieldKey: item.path, operator: 'EQUALS', values: [value] }\n : null;\n }\n }\n}\n\n/** Every non-empty condition, in schema order. */\nexport function buildApprovalInstanceFieldFilters(\n paths: readonly FilterableFieldPath[],\n drafts: FieldFilterDrafts,\n): readonly ApprovalInstanceFieldFilter[] {\n return paths.flatMap((item): readonly ApprovalInstanceFieldFilter[] => {\n const filter = toApprovalInstanceFieldFilter(\n item,\n drafts[item.path] ?? EMPTY_FIELD_FILTER_DRAFT,\n );\n\n return filter ? [filter] : [];\n });\n}\n\n/** The sort the user picked. An empty `field` keeps the server default. */\nexport interface SortDraft {\n readonly direction: ApprovalInstanceSortDirection;\n readonly field: string;\n}\n\nexport const DEFAULT_SORT_DRAFT: SortDraft = { direction: 'DESC', field: '' };\n\nexport interface SortFieldOption {\n readonly id: string;\n readonly label: string;\n}\n\nexport const DEFAULT_SORT_OPTION: SortFieldOption = {\n id: '',\n label: '預設(建立時間,新到舊)',\n};\n\nconst SORT_TIME_LABELS: Readonly<\n Record<(typeof APPROVAL_INSTANCE_SORT_TIME_FIELDS)[number], string>\n> = {\n completedAt: '完成時間',\n submittedAt: '送出時間',\n updatedAt: '最後更新',\n};\n\n/**\n * Sort choices. Time fields are always offered. Form fields are offered only\n * with a template selected, because the server needs a template to sort by a\n * form field.\n */\nexport function readSortFieldOptions(\n paths: readonly FilterableFieldPath[],\n templateSelected: boolean,\n): readonly SortFieldOption[] {\n const timeOptions = APPROVAL_INSTANCE_SORT_TIME_FIELDS.map(\n (field): SortFieldOption => ({ id: field, label: SORT_TIME_LABELS[field] }),\n );\n const fieldOptions = templateSelected\n ? paths\n .filter(isFilterableFieldSortable)\n .map((item): SortFieldOption => ({ id: item.path, label: item.label }))\n : [];\n\n return [DEFAULT_SORT_OPTION, ...timeOptions, ...fieldOptions];\n}\n\n/** Whether a sort on `field` is valid for the current template selection. */\nexport function isSortFieldAvailable(\n field: string,\n paths: readonly FilterableFieldPath[],\n templateSelected: boolean,\n): boolean {\n if (field === '') {\n return true;\n }\n\n if (\n (APPROVAL_INSTANCE_SORT_TIME_FIELDS as readonly string[]).includes(field)\n ) {\n return true;\n }\n\n return templateSelected && paths.some((item) => item.path === field);\n}\n\nexport function toApprovalInstanceSort(\n draft: SortDraft,\n): readonly ApprovalInstanceSort[] | undefined {\n return draft.field === ''\n ? undefined\n : [{ direction: draft.direction, field: draft.field }];\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport {\n DateRangePicker,\n DateTimeRangePicker,\n Filter,\n FormField,\n Input,\n Select,\n type FilterProps,\n} from '@mezzanine-ui/react';\nimport {\n ControlFieldSlotLayout,\n FormFieldLayout,\n} from '@mezzanine-ui/core/form';\nimport {\n formatDatePickerValue,\n formatDateTimePickerValue,\n} from '@rytass/bpm-core-client/form';\nimport type { FilterableFieldPath } from '@rytass/bpm-core-shared';\nimport {\n EMPTY_FIELD_FILTER_DRAFT,\n isDecimalText,\n readFieldFilterControl,\n type FieldFilterDraft,\n type FieldFilterDrafts,\n} from './approval-instance-field-filters';\n\nexport type FieldFilterDraftChange = (\n path: string,\n draft: FieldFilterDraft,\n) => void;\n\ntype SelectChoice = Readonly<{ id: string; name: string }>;\n\nconst BOOLEAN_CHOICES: readonly SelectChoice[] = [\n { id: 'true', name: '是' },\n { id: 'false', name: '否' },\n];\n\n/**\n * One list-page `Filter` per condition the template's filterable fields need.\n * A number field yields two (lower and upper bound) because Mezzanine has no\n * number range control. Returned as elements so the caller can place them\n * directly inside `FilterLine`, which only accepts `Filter` children.\n */\nexport function readFieldFilterElements(\n paths: readonly FilterableFieldPath[],\n drafts: FieldFilterDrafts,\n onDraftChange: FieldFilterDraftChange,\n): readonly ReactElement<FilterProps>[] {\n return paths.flatMap((item): readonly ReactElement<FilterProps>[] => {\n const draft = drafts[item.path] ?? EMPTY_FIELD_FILTER_DRAFT;\n const change = (next: FieldFilterDraft): void => {\n onDraftChange(item.path, next);\n };\n const control = readFieldFilterControl(item);\n\n if (control.kind === 'number-range') {\n return [\n renderFilter(\n `${item.path}:from`,\n `${item.label}(下限)`,\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, from: event.target.value });\n }}\n placeholder={`${item.label}:下限`}\n size=\"sub\"\n value={draft.from}\n variant=\"base\"\n />,\n readNumberHint(draft.from),\n ),\n renderFilter(\n `${item.path}:to`,\n `${item.label}(上限)`,\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, to: event.target.value });\n }}\n placeholder={`${item.label}:上限`}\n size=\"sub\"\n value={draft.to}\n variant=\"base\"\n />,\n readNumberHint(draft.to),\n ),\n ];\n }\n\n return [\n renderFilter(\n item.path,\n item.label,\n renderControl(item, draft, change, item.label),\n control.kind === 'id-in'\n ? `多個${readIdNoun(item)}以逗號分隔`\n : undefined,\n ),\n ];\n });\n}\n\nfunction renderFilter(\n key: string,\n label: string,\n control: ReactElement,\n hintText?: string,\n): ReactElement<FilterProps> {\n return (\n <Filter key={key} span={2}>\n <FormField\n fullWidth\n hintText={hintText}\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label={label}\n name={`fieldFilter-${key}`}\n >\n {control}\n </FormField>\n </Filter>\n );\n}\n\n/** What an id-in field holds: members, or org units for an `org_unit` field. */\nfunction readIdNoun(item: FilterableFieldPath): string {\n return item.kind === 'org_unit' ? '組織' : '成員';\n}\n\nfunction readNumberHint(value: string): string | undefined {\n return !value || isDecimalText(value) ? undefined : '請輸入數字';\n}\n\nfunction renderControl(\n item: FilterableFieldPath,\n draft: FieldFilterDraft,\n change: (next: FieldFilterDraft) => void,\n label: string,\n): ReactElement {\n // The list page hides FormField labels to keep its filter rows compact, so\n // each control names its field in the placeholder, as the state filter does.\n const control = readFieldFilterControl(item);\n\n if (control.kind === 'option-in') {\n const choices = control.options.map((option): SelectChoice => ({\n id: option.value,\n name: option.label,\n }));\n\n return (\n <Select\n clearable\n fullWidth\n mode=\"multiple\"\n onChange={(selected: readonly SelectChoice[]): void => {\n change({ ...draft, values: selected.map((option) => option.id) });\n }}\n options={[...choices]}\n placeholder={`${label}:全部`}\n size=\"sub\"\n value={choices.filter((choice) => draft.values.includes(choice.id))}\n />\n );\n }\n\n if (control.kind === 'boolean') {\n const selected = BOOLEAN_CHOICES.find((choice) =>\n draft.values.includes(choice.id),\n );\n\n return (\n <Select\n clearable\n fullWidth\n onChange={(option: SelectChoice | null): void => {\n change({ ...draft, values: option ? [option.id] : [] });\n }}\n options={[...BOOLEAN_CHOICES]}\n placeholder={`${label}:全部`}\n size=\"sub\"\n value={selected ?? null}\n />\n );\n }\n\n if (control.kind === 'date-range') {\n return (\n <DateRangePicker\n clearable\n fullWidth\n inputFromPlaceholder={`${label}:起`}\n inputToPlaceholder=\"迄\"\n onChange={(target?: readonly (string | undefined)[]): void => {\n change({\n ...draft,\n from: formatDatePickerValue(target?.[0]) ?? '',\n to: formatDatePickerValue(target?.[1]) ?? '',\n });\n }}\n size=\"sub\"\n value={draft.from && draft.to ? [draft.from, draft.to] : []}\n />\n );\n }\n\n if (control.kind === 'datetime-range') {\n return (\n <DateTimeRangePicker\n clearable\n fullWidth\n onChange={(value): void => {\n change({\n ...draft,\n from: formatDateTimePickerValue(value[0]) ?? '',\n to: formatDateTimePickerValue(value[1]) ?? '',\n });\n }}\n size=\"sub\"\n value={[draft.from || undefined, draft.to || undefined]}\n />\n );\n }\n\n if (control.kind === 'id-in') {\n return (\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, text: event.target.value });\n }}\n placeholder={`${label}:${readIdNoun(item)} ID`}\n size=\"sub\"\n value={draft.text}\n variant=\"base\"\n />\n );\n }\n\n // contains (text, textarea and data-source options)\n return (\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, text: event.target.value });\n }}\n placeholder={`${label}:包含`}\n size=\"sub\"\n value={draft.text}\n variant=\"base\"\n />\n );\n}\n",".instanceFilterArea {\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 RadioGroup,\n Section,\n SectionGroup,\n Select,\n Table,\n Typography,\n type FilterLineProps,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport {\n ControlFieldSlotLayout,\n FormFieldLayout,\n} 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 listApprovalTemplates,\n type ApprovalTemplateRecord,\n} from '@rytass/bpm-core-client/template';\nimport {\n listApprovalInstancesPage,\n readApprovalInstanceCaseTitle,\n readApprovalTemplateCurrentFormSchema,\n type ApprovalInstanceRecord,\n type ApprovalInstanceState,\n type ApprovalInstanceView,\n} from '@rytass/bpm-core-client/workflow';\nimport {\n readFilterableFieldPaths,\n type ApprovalInstanceFieldFilter,\n type ApprovalInstanceSort,\n type ApprovalInstanceSortDirection,\n type FilterableFieldPath,\n type FormDefinitionSchema,\n} from '@rytass/bpm-core-shared';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { formatDateTime } from '../lib/format-date-time';\nimport {\n buildApprovalInstanceFieldFilters,\n DEFAULT_SORT_DRAFT,\n DEFAULT_SORT_OPTION,\n isSortFieldAvailable,\n readSortFieldOptions,\n toApprovalInstanceSort,\n type FieldFilterDraft,\n type FieldFilterDrafts,\n type SortDraft,\n type SortFieldOption,\n} from './approval-instance-field-filters';\nimport { readFieldFilterElements } from './approval-instance-field-filter-controls';\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\ntype SelectChoice = Readonly<{ id: string; name: string }>;\n\nconst INSTANCE_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst FIELD_FILTERS_PER_LINE = 3;\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];\nconst SORT_DIRECTION_OPTIONS: readonly (SelectChoice & {\n id: ApprovalInstanceSortDirection;\n})[] = [\n { id: 'DESC', name: '新到舊' },\n { id: 'ASC', name: '舊到新' },\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 *\n * With a template selected, the filter bar also carries one control per\n * filterable field of that template's current form (ADR 25 §7), and the sort\n * may use those fields. Without a template only time fields can sort.\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 [templates, setTemplates] = useState<readonly ApprovalTemplateRecord[]>(\n [],\n );\n const [templateId, setTemplateId] = useState<string | null>(null);\n const [templateSchema, setTemplateSchema] =\n useState<FormDefinitionSchema | null>(null);\n const [schemaLoading, setSchemaLoading] = useState(false);\n const [schemaError, setSchemaError] = useState<string | null>(null);\n const [fieldDrafts, setFieldDrafts] = useState<FieldFilterDrafts>({});\n const [sortDraft, setSortDraft] = useState<SortDraft>(DEFAULT_SORT_DRAFT);\n const refreshRequestIdRef = useRef(0);\n\n const fieldPaths = useMemo(\n (): readonly FilterableFieldPath[] =>\n templateSchema ? readFilterableFieldPaths(templateSchema) : [],\n [templateSchema],\n );\n const fieldFilters = useMemo(\n (): readonly ApprovalInstanceFieldFilter[] | undefined =>\n templateId === null\n ? undefined\n : buildApprovalInstanceFieldFilters(fieldPaths, fieldDrafts),\n [fieldDrafts, fieldPaths, templateId],\n );\n const sort = useMemo(\n (): readonly ApprovalInstanceSort[] | undefined =>\n toApprovalInstanceSort(sortDraft),\n [sortDraft],\n );\n const sortOptions = useMemo(\n (): readonly SortFieldOption[] =>\n readSortFieldOptions(fieldPaths, templateId !== null),\n [fieldPaths, templateId],\n );\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 fieldFilters,\n page: instancePage,\n pageSize: instancePageSize,\n searchText,\n sort,\n state: stateFilter.state,\n templateId,\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 }, [\n fieldFilters,\n instancePage,\n instancePageSize,\n searchText,\n sort,\n stateFilter,\n templateId,\n view,\n ]);\n\n useEffect((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n\n useEffect((): (() => void) | void => {\n let cancelled = false;\n\n void (async (): Promise<void> => {\n try {\n const loaded = await listApprovalTemplates();\n\n if (!cancelled) {\n setTemplates(loaded);\n }\n } catch {\n if (!cancelled) {\n setTemplates([]);\n }\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, []);\n\n // Race guard: a schema response for a previously picked template is dropped\n // when the template changes again (cleanup flips `cancelled`).\n useEffect((): (() => void) | void => {\n if (templateId === null) {\n setTemplateSchema(null);\n setSchemaLoading(false);\n setSchemaError(null);\n\n return;\n }\n\n let cancelled = false;\n setSchemaLoading(true);\n setSchemaError(null);\n\n void (async (): Promise<void> => {\n try {\n const schema = await readApprovalTemplateCurrentFormSchema(templateId);\n\n if (!cancelled) {\n setTemplateSchema(schema);\n }\n } catch (schemaLoadError: unknown) {\n if (!cancelled) {\n setTemplateSchema(null);\n setSchemaError(readErrorMessage(schemaLoadError));\n }\n } finally {\n if (!cancelled) {\n setSchemaLoading(false);\n }\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, [templateId]);\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 handleTemplateChange = useCallback(\n (option: SelectChoice | null): void => {\n setTemplateId(option?.id ?? null);\n // Drop the previous template's fields at once, so nothing is built from\n // them while the new schema loads.\n setTemplateSchema(null);\n setFieldDrafts({});\n setInstancePage(1);\n // A form-field sort belongs to one template. Time-field sorts survive.\n setSortDraft((previous): SortDraft =>\n isSortFieldAvailable(previous.field, [], false)\n ? previous\n : { ...previous, field: DEFAULT_SORT_DRAFT.field },\n );\n },\n [],\n );\n\n const handleFilterReset = useCallback((): void => {\n setSearchText('');\n setStateFilter(readStateFilterOption(defaultState));\n setTemplateId(null);\n setTemplateSchema(null);\n setFieldDrafts({});\n setSortDraft(DEFAULT_SORT_DRAFT);\n setInstancePage(1);\n }, [defaultState]);\n const handleFilterSubmit = useCallback((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n const handleFieldDraftChange = useCallback(\n (path: string, draft: FieldFilterDraft): void => {\n setFieldDrafts((previous): FieldFilterDrafts => ({\n ...previous,\n [path]: draft,\n }));\n setInstancePage(1);\n },\n [],\n );\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 const templateOptions = useMemo(\n (): readonly SelectChoice[] =>\n templates.map((template) => ({ id: template.id, name: template.name })),\n [templates],\n );\n const selectedTemplate = templateOptions.find(\n (option) => option.id === templateId,\n );\n const fieldFilterLines = useMemo(\n (): readonly ReactElement<FilterLineProps>[] =>\n chunkElements(\n readFieldFilterElements(\n fieldPaths,\n fieldDrafts,\n handleFieldDraftChange,\n ),\n FIELD_FILTERS_PER_LINE,\n ).map((elements, index): ReactElement<FilterLineProps> => (\n <FilterLine key={`field-filter-line-${index}`}>\n {[...elements]}\n </FilterLine>\n )),\n [fieldDrafts, fieldPaths, handleFieldDraftChange],\n );\n const sortFieldChoices = useMemo(\n (): readonly SelectChoice[] =>\n sortOptions.map((option) => ({ id: option.id, name: option.label })),\n [sortOptions],\n );\n const selectedSortField =\n sortFieldChoices.find((option) => option.id === sortDraft.field) ??\n sortFieldChoices.find((option) => option.id === DEFAULT_SORT_OPTION.id);\n\n const searchFilterLine = (\n <FilterLine key=\"search\">\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceSearchText\"\n >\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): 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 controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\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 <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"範本\"\n name=\"instanceTemplate\"\n >\n <Select\n clearable\n fullWidth\n onChange={handleTemplateChange}\n options={[...templateOptions]}\n placeholder=\"全部範本\"\n size=\"sub\"\n value={selectedTemplate ?? null}\n />\n </FormField>\n </Filter>\n </FilterLine>\n );\n\n const sortFilterLine = (\n <FilterLine key=\"sort\">\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"排序欄位\"\n name=\"instanceSortField\"\n >\n <Select\n clearable={false}\n fullWidth\n onChange={(option: SelectChoice | null): void => {\n setSortDraft((previous): SortDraft => ({\n ...previous,\n field: option?.id ?? DEFAULT_SORT_DRAFT.field,\n }));\n setInstancePage(1);\n }}\n options={[...sortFieldChoices]}\n placeholder=\"排序欄位\"\n size=\"sub\"\n value={selectedSortField ?? null}\n />\n </FormField>\n </Filter>\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"排序方向\"\n name=\"instanceSortDirection\"\n >\n <RadioGroup\n name=\"instanceSortDirection\"\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setSortDraft((previous): SortDraft => ({\n ...previous,\n direction: readSortDirection(event.target.value),\n }));\n setInstancePage(1);\n }}\n options={[...SORT_DIRECTION_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={sortDraft.direction}\n />\n </FormField>\n </Filter>\n </FilterLine>\n );\n\n return (\n <>\n <PageHeader>\n <ContentHeader description={description} title={title} />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea\n className={styles.instanceFilterArea}\n onReset={handleFilterReset}\n onSubmit={handleFilterSubmit}\n resetText=\"清除條件\"\n rowAlign=\"end\"\n size=\"sub\"\n submitText=\"重新查詢\"\n >\n {[searchFilterLine, ...fieldFilterLines, sortFilterLine]}\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n {schemaError ? (\n <Typography color=\"text-error\" variant=\"body\">\n {schemaError}\n </Typography>\n ) : null}\n {templateId !== null &&\n !schemaLoading &&\n !schemaError &&\n templateSchema &&\n fieldPaths.length === 0 ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n 此範本沒有可篩選的欄位。\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 chunkElements<T>(\n items: readonly T[],\n size: number,\n): readonly (readonly T[])[] {\n return items.reduce<readonly (readonly T[])[]>(\n (lines, item): readonly (readonly T[])[] => {\n const last = lines[lines.length - 1];\n\n return last && last.length < size\n ? [...lines.slice(0, -1), [...last, item]]\n : [...lines, [item]];\n },\n [],\n );\n}\n\nfunction readSortDirection(value: string): ApprovalInstanceSortDirection {\n return value === 'ASC' ? 'ASC' : 'DESC';\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-Cv5I1YPX.cjs","names":["APPROVAL_INSTANCE_SORT_TIME_FIELDS","isFilterableFieldSortable","Input","Filter","FormField","ControlFieldSlotLayout","FormFieldLayout","Select","DateRangePicker","formatDatePickerValue","DateTimeRangePicker","formatDateTimePickerValue","useRouterAdapter","useBPMRoutes","useState","useRef","useMemo","readFilterableFieldPaths","useCallback","listApprovalInstancesPage","listApprovalTemplates","readApprovalTemplateCurrentFormSchema","resolveMembers","Typography","formatDateTime","FilterLine","Filter","FormField","ControlFieldSlotLayout","FormFieldLayout","Input","Select","RadioGroup","_Fragment","PageHeader","ContentHeader","SectionGroup","Section","FilterArea","styles","Table","readApprovalInstanceCaseTitle","require_rolldown_runtime","require_router_adapter","require_routes_config","require_format_date_time","react","_mezzanine_ui_react","_rytass_bpm_core_client","react_jsx_runtime","_rytass_bpm_core_client_workflow","_rytass_bpm_core_shared","_mezzanine_ui_core_form","_mezzanine_ui_react_ContentHeader","_rytass_bpm_core_client_template","_rytass_bpm_core_client_form","readFieldFilterControl","item","EMPTY_FIELD_FILTER_DRAFT","DECIMAL_PATTERN","isDecimalText","readDecimal","value","plain","readIdList","text","toApprovalInstanceFieldFilter","draft","ids","from","to","buildApprovalInstanceFieldFilters","paths","filter","drafts","DEFAULT_SORT_DRAFT","DEFAULT_SORT_OPTION","SORT_TIME_LABELS","readSortFieldOptions","timeOptions","field","fieldOptions","templateSelected","isSortFieldAvailable","toApprovalInstanceSort","BOOLEAN_CHOICES","readFieldFilterElements","change","onDraftChange","next","control","renderFilter","event","readNumberHint","renderControl","readIdNoun","key","choices","option","selected","label","choice","target","approval_instance_list_page_module_default","INSTANCE_PAGE_SIZE_OPTIONS","FIELD_FILTERS_PER_LINE","STATE_FILTER_OPTIONS","SORT_DIRECTION_OPTIONS","ApprovalInstanceListPage","router","routes","error","setError","initiatorProfilesById","setInitiatorProfilesById","instancePage","setInstancePage","instancePageSize","setInstancePageSize","instanceTotalCount","setInstanceTotalCount","loading","setLoading","rows","setRows","searchText","setSearchText","stateFilter","setStateFilter","readStateFilterOption","defaultState","templates","setTemplates","templateId","setTemplateId","templateSchema","setTemplateSchema","schemaLoading","setSchemaLoading","schemaError","setSchemaError","fieldDrafts","setFieldDrafts","sortDraft","setSortDraft","refreshRequestIdRef","fieldPaths","fieldFilters","sort","sortOptions","refreshInstances","requestId","result","readApprovalInstanceRow","requestError","readErrorMessage","view","cancelled","loaded","schema","schemaLoadError","initiatorMemberIds","row","profiles","profile","handleTemplateChange","previous","handleFilterReset","handleFilterSubmit","handleFieldDraftChange","path","columns","readInstanceStateColor","record","readInitiatorLabel","tableActions","templateOptions","template","selectedTemplate","fieldFilterLines","chunkElements","elements","index","sortFieldChoices","selectedSortField","searchFilterLine","searchPlaceholder","readSelectedStateFilterOption","readStateFilterLabel","sortFilterLine","readSortDirection","emptyMessage","page","pageSize","total","items","last","lines","size","instance","readInstanceStateLabel","isStateFilterOption","state","trimmedMemberId","initiatorMemberId"],"sources":["../../src/components/approval-instance-field-filters.ts","../../src/components/approval-instance-field-filter-controls.tsx","../../src/components/approval-instance-list-page.tsx"],"sourcesContent":["import {\n APPROVAL_INSTANCE_SORT_TIME_FIELDS,\n isFilterableFieldSortable,\n type ApprovalInstanceFieldFilter,\n type ApprovalInstanceSort,\n type ApprovalInstanceSortDirection,\n type FilterableFieldPath,\n type FormFieldOption,\n} from '@rytass/bpm-core-shared';\n\n/**\n * Pure logic behind the list page's field filters (ADR 25 §7). Turns the\n * filterable fields of a template's schema into controls, and the control\n * values into `fieldFilters` and `sort` for `listApprovalInstancesPage`. The\n * operator for each control is one `readFilterOperatorsForFieldType` allows.\n */\n\n/** Which control a filterable field gets on the list page. */\nexport type FieldFilterControl =\n /** Text, textarea and dynamic (data-source) options: CONTAINS on a text input. */\n | { readonly kind: 'contains' }\n /** Static options: IN on a multi-select. */\n | { readonly kind: 'option-in'; readonly options: readonly FormFieldOption[] }\n /** Member and org unit: IN on comma-separated ids (no picker yet). */\n | { readonly kind: 'id-in' }\n /** Number and money: RANGE with two number inputs. */\n | { readonly kind: 'number-range' }\n /** Date: RANGE with a date range picker. */\n | { readonly kind: 'date-range' }\n /** Datetime: RANGE with a datetime range picker. */\n | { readonly kind: 'datetime-range' }\n /** Boolean: EQUALS with a yes / no select. */\n | { readonly kind: 'boolean' };\n\nexport function readFieldFilterControl(\n item: FilterableFieldPath,\n): FieldFilterControl {\n switch (item.kind) {\n case 'number':\n return { kind: 'number-range' };\n case 'date':\n return { kind: 'date-range' };\n case 'datetime':\n return { kind: 'datetime-range' };\n case 'boolean':\n return { kind: 'boolean' };\n case 'member':\n case 'org_unit':\n return { kind: 'id-in' };\n case 'option':\n return item.staticOptions\n ? { kind: 'option-in', options: item.staticOptions }\n : { kind: 'contains' };\n case 'text':\n return { kind: 'contains' };\n }\n}\n\n/**\n * One field's entered condition. `from` and `to` hold canonical values\n * (decimal string, `YYYY-MM-DD`, or ISO 8601) as the controls produce them.\n */\nexport interface FieldFilterDraft {\n readonly from: string;\n readonly to: string;\n /** CONTAINS text, or comma-separated ids for IN on member and org unit. */\n readonly text: string;\n /** Selected option values (IN), or `['true']` / `['false']` for boolean. */\n readonly values: readonly string[];\n}\n\nexport const EMPTY_FIELD_FILTER_DRAFT: FieldFilterDraft = {\n from: '',\n text: '',\n to: '',\n values: [],\n};\n\n/** Drafts keyed by the field path, so a path can be looked up directly. */\nexport type FieldFilterDrafts = Readonly<Record<string, FieldFilterDraft>>;\n\nconst DECIMAL_PATTERN = /^-?\\d+(\\.\\d+)?$/u;\n\n/**\n * Whether a number control's text is a decimal. Surrounding spaces and\n * thousands separators (`1,000`) are accepted and dropped before sending.\n */\nexport function isDecimalText(value: string): boolean {\n return readDecimal(value) !== null;\n}\n\nfunction readDecimal(value: string): string | null {\n const plain = value.trim().replace(/,/gu, '');\n\n return DECIMAL_PATTERN.test(plain) ? plain : null;\n}\n\nfunction readIdList(text: string): readonly string[] {\n return text\n .split(',')\n .map((item) => item.trim())\n .filter((item) => item.length > 0);\n}\n\n/**\n * The condition one draft asks for, or `null` when it is empty. Empty means\n * no condition, so it must not reach the server as a filter.\n */\nexport function toApprovalInstanceFieldFilter(\n item: FilterableFieldPath,\n draft: FieldFilterDraft,\n): ApprovalInstanceFieldFilter | null {\n const control = readFieldFilterControl(item);\n\n switch (control.kind) {\n case 'contains': {\n const text = draft.text.trim();\n\n return text\n ? { fieldKey: item.path, operator: 'CONTAINS', values: [text] }\n : null;\n }\n case 'option-in':\n return draft.values.length > 0\n ? { fieldKey: item.path, operator: 'IN', values: [...draft.values] }\n : null;\n case 'id-in': {\n const ids = readIdList(draft.text);\n\n return ids.length > 0\n ? { fieldKey: item.path, operator: 'IN', values: ids }\n : null;\n }\n case 'number-range': {\n const from = readDecimal(draft.from);\n const to = readDecimal(draft.to);\n\n return from || to\n ? {\n fieldKey: item.path,\n operator: 'RANGE',\n ...(from ? { from } : {}),\n ...(to ? { to } : {}),\n }\n : null;\n }\n case 'date-range':\n case 'datetime-range': {\n const from = draft.from.trim();\n const to = draft.to.trim();\n\n return from || to\n ? {\n fieldKey: item.path,\n operator: 'RANGE',\n ...(from ? { from } : {}),\n ...(to ? { to } : {}),\n }\n : null;\n }\n case 'boolean': {\n const value = draft.values[0];\n\n return value === 'true' || value === 'false'\n ? { fieldKey: item.path, operator: 'EQUALS', values: [value] }\n : null;\n }\n }\n}\n\n/** Every non-empty condition, in schema order. */\nexport function buildApprovalInstanceFieldFilters(\n paths: readonly FilterableFieldPath[],\n drafts: FieldFilterDrafts,\n): readonly ApprovalInstanceFieldFilter[] {\n return paths.flatMap((item): readonly ApprovalInstanceFieldFilter[] => {\n const filter = toApprovalInstanceFieldFilter(\n item,\n drafts[item.path] ?? EMPTY_FIELD_FILTER_DRAFT,\n );\n\n return filter ? [filter] : [];\n });\n}\n\n/** The sort the user picked. An empty `field` keeps the server default. */\nexport interface SortDraft {\n readonly direction: ApprovalInstanceSortDirection;\n readonly field: string;\n}\n\nexport const DEFAULT_SORT_DRAFT: SortDraft = { direction: 'DESC', field: '' };\n\nexport interface SortFieldOption {\n readonly id: string;\n readonly label: string;\n}\n\nexport const DEFAULT_SORT_OPTION: SortFieldOption = {\n id: '',\n label: '預設(建立時間,新到舊)',\n};\n\nconst SORT_TIME_LABELS: Readonly<\n Record<(typeof APPROVAL_INSTANCE_SORT_TIME_FIELDS)[number], string>\n> = {\n completedAt: '完成時間',\n submittedAt: '送出時間',\n updatedAt: '最後更新',\n};\n\n/**\n * Sort choices. Time fields are always offered. Form fields are offered only\n * with a template selected, because the server needs a template to sort by a\n * form field.\n */\nexport function readSortFieldOptions(\n paths: readonly FilterableFieldPath[],\n templateSelected: boolean,\n): readonly SortFieldOption[] {\n const timeOptions = APPROVAL_INSTANCE_SORT_TIME_FIELDS.map(\n (field): SortFieldOption => ({ id: field, label: SORT_TIME_LABELS[field] }),\n );\n const fieldOptions = templateSelected\n ? paths\n .filter(isFilterableFieldSortable)\n .map((item): SortFieldOption => ({ id: item.path, label: item.label }))\n : [];\n\n return [DEFAULT_SORT_OPTION, ...timeOptions, ...fieldOptions];\n}\n\n/** Whether a sort on `field` is valid for the current template selection. */\nexport function isSortFieldAvailable(\n field: string,\n paths: readonly FilterableFieldPath[],\n templateSelected: boolean,\n): boolean {\n if (field === '') {\n return true;\n }\n\n if (\n (APPROVAL_INSTANCE_SORT_TIME_FIELDS as readonly string[]).includes(field)\n ) {\n return true;\n }\n\n return templateSelected && paths.some((item) => item.path === field);\n}\n\nexport function toApprovalInstanceSort(\n draft: SortDraft,\n): readonly ApprovalInstanceSort[] | undefined {\n return draft.field === ''\n ? undefined\n : [{ direction: draft.direction, field: draft.field }];\n}\n","'use client';\n\nimport type { ReactElement } from 'react';\nimport {\n DateRangePicker,\n DateTimeRangePicker,\n Filter,\n FormField,\n Input,\n Select,\n type FilterProps,\n} from '@mezzanine-ui/react';\nimport {\n ControlFieldSlotLayout,\n FormFieldLayout,\n} from '@mezzanine-ui/core/form';\nimport {\n formatDatePickerValue,\n formatDateTimePickerValue,\n} from '@rytass/bpm-core-client/form';\nimport type { FilterableFieldPath } from '@rytass/bpm-core-shared';\nimport {\n EMPTY_FIELD_FILTER_DRAFT,\n isDecimalText,\n readFieldFilterControl,\n type FieldFilterDraft,\n type FieldFilterDrafts,\n} from './approval-instance-field-filters';\n\nexport type FieldFilterDraftChange = (\n path: string,\n draft: FieldFilterDraft,\n) => void;\n\ntype SelectChoice = Readonly<{ id: string; name: string }>;\n\nconst BOOLEAN_CHOICES: readonly SelectChoice[] = [\n { id: 'true', name: '是' },\n { id: 'false', name: '否' },\n];\n\n/**\n * One list-page `Filter` per condition the template's filterable fields need.\n * A number field yields two (lower and upper bound) because Mezzanine has no\n * number range control. Returned as elements so the caller can place them\n * directly inside `FilterLine`, which only accepts `Filter` children.\n */\nexport function readFieldFilterElements(\n paths: readonly FilterableFieldPath[],\n drafts: FieldFilterDrafts,\n onDraftChange: FieldFilterDraftChange,\n): readonly ReactElement<FilterProps>[] {\n return paths.flatMap((item): readonly ReactElement<FilterProps>[] => {\n const draft = drafts[item.path] ?? EMPTY_FIELD_FILTER_DRAFT;\n const change = (next: FieldFilterDraft): void => {\n onDraftChange(item.path, next);\n };\n const control = readFieldFilterControl(item);\n\n if (control.kind === 'number-range') {\n return [\n renderFilter(\n `${item.path}:from`,\n `${item.label}(下限)`,\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, from: event.target.value });\n }}\n placeholder={`${item.label}:下限`}\n size=\"sub\"\n value={draft.from}\n variant=\"base\"\n />,\n readNumberHint(draft.from),\n ),\n renderFilter(\n `${item.path}:to`,\n `${item.label}(上限)`,\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, to: event.target.value });\n }}\n placeholder={`${item.label}:上限`}\n size=\"sub\"\n value={draft.to}\n variant=\"base\"\n />,\n readNumberHint(draft.to),\n ),\n ];\n }\n\n return [\n renderFilter(\n item.path,\n item.label,\n renderControl(item, draft, change, item.label),\n control.kind === 'id-in'\n ? `多個${readIdNoun(item)}以逗號分隔`\n : undefined,\n ),\n ];\n });\n}\n\nfunction renderFilter(\n key: string,\n label: string,\n control: ReactElement,\n hintText?: string,\n): ReactElement<FilterProps> {\n return (\n <Filter key={key} span={2}>\n <FormField\n fullWidth\n hintText={hintText}\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label={label}\n name={`fieldFilter-${key}`}\n >\n {control}\n </FormField>\n </Filter>\n );\n}\n\n/** What an id-in field holds: members, or org units for an `org_unit` field. */\nfunction readIdNoun(item: FilterableFieldPath): string {\n return item.kind === 'org_unit' ? '組織' : '成員';\n}\n\nfunction readNumberHint(value: string): string | undefined {\n return !value || isDecimalText(value) ? undefined : '請輸入數字';\n}\n\nfunction renderControl(\n item: FilterableFieldPath,\n draft: FieldFilterDraft,\n change: (next: FieldFilterDraft) => void,\n label: string,\n): ReactElement {\n // The list page hides FormField labels to keep its filter rows compact, so\n // each control names its field in the placeholder, as the state filter does.\n const control = readFieldFilterControl(item);\n\n if (control.kind === 'option-in') {\n const choices = control.options.map((option): SelectChoice => ({\n id: option.value,\n name: option.label,\n }));\n\n return (\n <Select\n clearable\n fullWidth\n mode=\"multiple\"\n onChange={(selected: readonly SelectChoice[]): void => {\n change({ ...draft, values: selected.map((option) => option.id) });\n }}\n options={[...choices]}\n placeholder={`${label}:全部`}\n size=\"sub\"\n value={choices.filter((choice) => draft.values.includes(choice.id))}\n />\n );\n }\n\n if (control.kind === 'boolean') {\n const selected = BOOLEAN_CHOICES.find((choice) =>\n draft.values.includes(choice.id),\n );\n\n return (\n <Select\n clearable\n fullWidth\n onChange={(option: SelectChoice | null): void => {\n change({ ...draft, values: option ? [option.id] : [] });\n }}\n options={[...BOOLEAN_CHOICES]}\n placeholder={`${label}:全部`}\n size=\"sub\"\n value={selected ?? null}\n />\n );\n }\n\n if (control.kind === 'date-range') {\n return (\n <DateRangePicker\n clearable\n fullWidth\n inputFromPlaceholder={`${label}:起`}\n inputToPlaceholder=\"迄\"\n onChange={(target?: readonly (string | undefined)[]): void => {\n change({\n ...draft,\n from: formatDatePickerValue(target?.[0]) ?? '',\n to: formatDatePickerValue(target?.[1]) ?? '',\n });\n }}\n size=\"sub\"\n value={draft.from && draft.to ? [draft.from, draft.to] : []}\n />\n );\n }\n\n if (control.kind === 'datetime-range') {\n return (\n <DateTimeRangePicker\n clearable\n fullWidth\n onChange={(value): void => {\n change({\n ...draft,\n from: formatDateTimePickerValue(value[0]) ?? '',\n to: formatDateTimePickerValue(value[1]) ?? '',\n });\n }}\n size=\"sub\"\n value={[draft.from || undefined, draft.to || undefined]}\n />\n );\n }\n\n if (control.kind === 'id-in') {\n return (\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, text: event.target.value });\n }}\n placeholder={`${label}:${readIdNoun(item)} ID`}\n size=\"sub\"\n value={draft.text}\n variant=\"base\"\n />\n );\n }\n\n // contains (text, textarea and data-source options)\n return (\n <Input\n fullWidth\n onChange={(event): void => {\n change({ ...draft, text: event.target.value });\n }}\n placeholder={`${label}:包含`}\n size=\"sub\"\n value={draft.text}\n variant=\"base\"\n />\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 RadioGroup,\n Section,\n SectionGroup,\n Select,\n Table,\n Typography,\n type FilterLineProps,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport {\n ControlFieldSlotLayout,\n FormFieldLayout,\n} 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 listApprovalTemplates,\n type ApprovalTemplateRecord,\n} from '@rytass/bpm-core-client/template';\nimport {\n listApprovalInstancesPage,\n readApprovalInstanceCaseTitle,\n readApprovalTemplateCurrentFormSchema,\n type ApprovalInstanceRecord,\n type ApprovalInstanceState,\n type ApprovalInstanceView,\n} from '@rytass/bpm-core-client/workflow';\nimport {\n readFilterableFieldPaths,\n type ApprovalInstanceFieldFilter,\n type ApprovalInstanceSort,\n type ApprovalInstanceSortDirection,\n type FilterableFieldPath,\n type FormDefinitionSchema,\n} from '@rytass/bpm-core-shared';\nimport { useRouterAdapter } from '../lib/router-adapter';\nimport { useBPMRoutes } from '../lib/routes-config';\nimport { formatDateTime } from '../lib/format-date-time';\nimport {\n buildApprovalInstanceFieldFilters,\n DEFAULT_SORT_DRAFT,\n DEFAULT_SORT_OPTION,\n isSortFieldAvailable,\n readSortFieldOptions,\n toApprovalInstanceSort,\n type FieldFilterDraft,\n type FieldFilterDrafts,\n type SortDraft,\n type SortFieldOption,\n} from './approval-instance-field-filters';\nimport { readFieldFilterElements } from './approval-instance-field-filter-controls';\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\ntype SelectChoice = Readonly<{ id: string; name: string }>;\n\nconst INSTANCE_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst FIELD_FILTERS_PER_LINE = 3;\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];\nconst SORT_DIRECTION_OPTIONS: readonly (SelectChoice & {\n id: ApprovalInstanceSortDirection;\n})[] = [\n { id: 'DESC', name: '新到舊' },\n { id: 'ASC', name: '舊到新' },\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 *\n * With a template selected, the filter bar also carries one control per\n * filterable field of that template's current form (ADR 25 §7), and the sort\n * may use those fields. Without a template only time fields can sort.\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 [templates, setTemplates] = useState<readonly ApprovalTemplateRecord[]>(\n [],\n );\n const [templateId, setTemplateId] = useState<string | null>(null);\n const [templateSchema, setTemplateSchema] =\n useState<FormDefinitionSchema | null>(null);\n const [schemaLoading, setSchemaLoading] = useState(false);\n const [schemaError, setSchemaError] = useState<string | null>(null);\n const [fieldDrafts, setFieldDrafts] = useState<FieldFilterDrafts>({});\n const [sortDraft, setSortDraft] = useState<SortDraft>(DEFAULT_SORT_DRAFT);\n const refreshRequestIdRef = useRef(0);\n\n const fieldPaths = useMemo(\n (): readonly FilterableFieldPath[] =>\n templateSchema ? readFilterableFieldPaths(templateSchema) : [],\n [templateSchema],\n );\n const fieldFilters = useMemo(\n (): readonly ApprovalInstanceFieldFilter[] | undefined =>\n templateId === null\n ? undefined\n : buildApprovalInstanceFieldFilters(fieldPaths, fieldDrafts),\n [fieldDrafts, fieldPaths, templateId],\n );\n const sort = useMemo(\n (): readonly ApprovalInstanceSort[] | undefined =>\n toApprovalInstanceSort(sortDraft),\n [sortDraft],\n );\n const sortOptions = useMemo(\n (): readonly SortFieldOption[] =>\n readSortFieldOptions(fieldPaths, templateId !== null),\n [fieldPaths, templateId],\n );\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 fieldFilters,\n page: instancePage,\n pageSize: instancePageSize,\n searchText,\n sort,\n state: stateFilter.state,\n templateId,\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 }, [\n fieldFilters,\n instancePage,\n instancePageSize,\n searchText,\n sort,\n stateFilter,\n templateId,\n view,\n ]);\n\n useEffect((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n\n useEffect((): (() => void) | void => {\n let cancelled = false;\n\n void (async (): Promise<void> => {\n try {\n const loaded = await listApprovalTemplates();\n\n if (!cancelled) {\n setTemplates(loaded);\n }\n } catch {\n if (!cancelled) {\n setTemplates([]);\n }\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, []);\n\n // Race guard: a schema response for a previously picked template is dropped\n // when the template changes again (cleanup flips `cancelled`).\n useEffect((): (() => void) | void => {\n if (templateId === null) {\n setTemplateSchema(null);\n setSchemaLoading(false);\n setSchemaError(null);\n\n return;\n }\n\n let cancelled = false;\n setSchemaLoading(true);\n setSchemaError(null);\n\n void (async (): Promise<void> => {\n try {\n const schema = await readApprovalTemplateCurrentFormSchema(templateId);\n\n if (!cancelled) {\n setTemplateSchema(schema);\n }\n } catch (schemaLoadError: unknown) {\n if (!cancelled) {\n setTemplateSchema(null);\n setSchemaError(readErrorMessage(schemaLoadError));\n }\n } finally {\n if (!cancelled) {\n setSchemaLoading(false);\n }\n }\n })();\n\n return (): void => {\n cancelled = true;\n };\n }, [templateId]);\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 handleTemplateChange = useCallback(\n (option: SelectChoice | null): void => {\n setTemplateId(option?.id ?? null);\n // Drop the previous template's fields at once, so nothing is built from\n // them while the new schema loads.\n setTemplateSchema(null);\n setFieldDrafts({});\n setInstancePage(1);\n // A form-field sort belongs to one template. Time-field sorts survive.\n setSortDraft((previous): SortDraft =>\n isSortFieldAvailable(previous.field, [], false)\n ? previous\n : { ...previous, field: DEFAULT_SORT_DRAFT.field },\n );\n },\n [],\n );\n\n const handleFilterReset = useCallback((): void => {\n setSearchText('');\n setStateFilter(readStateFilterOption(defaultState));\n setTemplateId(null);\n setTemplateSchema(null);\n setFieldDrafts({});\n setSortDraft(DEFAULT_SORT_DRAFT);\n setInstancePage(1);\n }, [defaultState]);\n const handleFilterSubmit = useCallback((): void => {\n void refreshInstances();\n }, [refreshInstances]);\n const handleFieldDraftChange = useCallback(\n (path: string, draft: FieldFilterDraft): void => {\n setFieldDrafts((previous): FieldFilterDrafts => ({\n ...previous,\n [path]: draft,\n }));\n setInstancePage(1);\n },\n [],\n );\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 const templateOptions = useMemo(\n (): readonly SelectChoice[] =>\n templates.map((template) => ({ id: template.id, name: template.name })),\n [templates],\n );\n const selectedTemplate = templateOptions.find(\n (option) => option.id === templateId,\n );\n const fieldFilterLines = useMemo(\n (): readonly ReactElement<FilterLineProps>[] =>\n chunkElements(\n readFieldFilterElements(\n fieldPaths,\n fieldDrafts,\n handleFieldDraftChange,\n ),\n FIELD_FILTERS_PER_LINE,\n ).map((elements, index): ReactElement<FilterLineProps> => (\n <FilterLine key={`field-filter-line-${index}`}>\n {[...elements]}\n </FilterLine>\n )),\n [fieldDrafts, fieldPaths, handleFieldDraftChange],\n );\n const sortFieldChoices = useMemo(\n (): readonly SelectChoice[] =>\n sortOptions.map((option) => ({ id: option.id, name: option.label })),\n [sortOptions],\n );\n const selectedSortField =\n sortFieldChoices.find((option) => option.id === sortDraft.field) ??\n sortFieldChoices.find((option) => option.id === DEFAULT_SORT_OPTION.id);\n\n const searchFilterLine = (\n <FilterLine key=\"search\">\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n name=\"instanceSearchText\"\n >\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): 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 controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\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 <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"範本\"\n name=\"instanceTemplate\"\n >\n <Select\n clearable\n fullWidth\n onChange={handleTemplateChange}\n options={[...templateOptions]}\n placeholder=\"全部範本\"\n size=\"sub\"\n value={selectedTemplate ?? null}\n />\n </FormField>\n </Filter>\n </FilterLine>\n );\n\n const sortFilterLine = (\n <FilterLine key=\"sort\">\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"排序欄位\"\n name=\"instanceSortField\"\n >\n <Select\n clearable={false}\n fullWidth\n onChange={(option: SelectChoice | null): void => {\n setSortDraft((previous): SortDraft => ({\n ...previous,\n field: option?.id ?? DEFAULT_SORT_DRAFT.field,\n }));\n setInstancePage(1);\n }}\n options={[...sortFieldChoices]}\n placeholder=\"排序欄位\"\n size=\"sub\"\n value={selectedSortField ?? null}\n />\n </FormField>\n </Filter>\n <Filter span={2}>\n <FormField\n fullWidth\n controlFieldSlotLayout={ControlFieldSlotLayout.SUB}\n layout={FormFieldLayout.VERTICAL}\n label=\"排序方向\"\n name=\"instanceSortDirection\"\n >\n <RadioGroup\n name=\"instanceSortDirection\"\n onChange={(event: ChangeEvent<HTMLInputElement>): void => {\n setSortDraft((previous): SortDraft => ({\n ...previous,\n direction: readSortDirection(event.target.value),\n }));\n setInstancePage(1);\n }}\n options={[...SORT_DIRECTION_OPTIONS]}\n size=\"sub\"\n type=\"segment\"\n value={sortDraft.direction}\n />\n </FormField>\n </Filter>\n </FilterLine>\n );\n\n return (\n <>\n <PageHeader>\n <ContentHeader description={description} title={title} />\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea\n className={styles.instanceFilterArea}\n onReset={handleFilterReset}\n onSubmit={handleFilterSubmit}\n resetText=\"清除條件\"\n rowAlign=\"end\"\n size=\"sub\"\n submitText=\"重新查詢\"\n >\n {[searchFilterLine, ...fieldFilterLines, sortFilterLine]}\n </FilterArea>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n {schemaError ? (\n <Typography color=\"text-error\" variant=\"body\">\n {schemaError}\n </Typography>\n ) : null}\n {templateId !== null &&\n !schemaLoading &&\n !schemaError &&\n templateSchema &&\n fieldPaths.length === 0 ? (\n <Typography color=\"text-neutral\" variant=\"body\">\n 此範本沒有可篩選的欄位。\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 chunkElements<T>(\n items: readonly T[],\n size: number,\n): readonly (readonly T[])[] {\n return items.reduce<readonly (readonly T[])[]>(\n (lines, item): readonly (readonly T[])[] => {\n const last = lines[lines.length - 1];\n\n return last && last.length < size\n ? [...lines.slice(0, -1), [...last, item]]\n : [...lines, [item]];\n },\n [],\n );\n}\n\nfunction readSortDirection(value: string): ApprovalInstanceSortDirection {\n return value === 'ASC' ? 'ASC' : 'DESC';\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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"}