@rytass/bpm-core-react 0.18.1 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/chunks/{FormBuilderView-vQfVc08Z.js → FormBuilderView-CFRiw04B.js} +563 -475
  3. package/dist/chunks/FormBuilderView-CFRiw04B.js.map +1 -0
  4. package/dist/chunks/FormBuilderView-CpUgbSbt.cjs +3 -0
  5. package/dist/chunks/FormBuilderView-CpUgbSbt.cjs.map +1 -0
  6. package/dist/chunks/FormRendererView-GfbVWEGa.js +1321 -0
  7. package/dist/chunks/FormRendererView-GfbVWEGa.js.map +1 -0
  8. package/dist/chunks/FormRendererView-lYSaR8CQ.cjs +6 -0
  9. package/dist/chunks/FormRendererView-lYSaR8CQ.cjs.map +1 -0
  10. package/dist/chunks/{compose-DtfX8DQ9.cjs → compose-Bb5bUVFd.cjs} +2 -2
  11. package/dist/chunks/{compose-DtfX8DQ9.cjs.map → compose-Bb5bUVFd.cjs.map} +1 -1
  12. package/dist/chunks/{compose-D9ipJpF_.js → compose-egQlRjuZ.js} +5 -4
  13. package/dist/chunks/{compose-D9ipJpF_.js.map → compose-egQlRjuZ.js.map} +1 -1
  14. package/dist/chunks/{delegations-BfhCDz5e.js → delegations-9-yvWWws.js} +3 -2
  15. package/dist/chunks/delegations-9-yvWWws.js.map +1 -0
  16. package/dist/chunks/delegations-CVmtpkRn.cjs +2 -0
  17. package/dist/chunks/delegations-CVmtpkRn.cjs.map +1 -0
  18. package/dist/chunks/delegations-CrfjsH94.cjs +2 -0
  19. package/dist/chunks/delegations-CrfjsH94.cjs.map +1 -0
  20. package/dist/chunks/{delegations-DBh5TT6G.js → delegations-CxnNvTCc.js} +3 -2
  21. package/dist/chunks/delegations-CxnNvTCc.js.map +1 -0
  22. package/dist/chunks/designer-CX9bhYJS.cjs +69 -0
  23. package/dist/chunks/designer-CX9bhYJS.cjs.map +1 -0
  24. package/dist/chunks/{designer-ubnZbP-A.js → designer-CZDlQ9Nf.js} +2341 -1584
  25. package/dist/chunks/designer-CZDlQ9Nf.js.map +1 -0
  26. package/dist/chunks/{detail-C6XKZjlF.js → detail-ByZV1XaI.js} +853 -840
  27. package/dist/chunks/detail-ByZV1XaI.js.map +1 -0
  28. package/dist/chunks/detail-ChseSF55.cjs +5 -0
  29. package/dist/chunks/detail-ChseSF55.cjs.map +1 -0
  30. package/dist/chunks/{approver-filled-form-B4zPg9kz.cjs → instance-error-message-DTARGbGo.cjs} +2 -2
  31. package/dist/chunks/instance-error-message-DTARGbGo.cjs.map +1 -0
  32. package/dist/chunks/{approver-filled-form-Bgazhbvd.js → instance-error-message-q6gy5evy.js} +44 -36
  33. package/dist/chunks/instance-error-message-q6gy5evy.js.map +1 -0
  34. package/dist/chunks/workflow-chat-prompt-B4wCFKPB.js +25 -0
  35. package/dist/chunks/workflow-chat-prompt-B4wCFKPB.js.map +1 -0
  36. package/dist/chunks/workflow-chat-prompt-B803qVdt.cjs +27 -0
  37. package/dist/chunks/workflow-chat-prompt-B803qVdt.cjs.map +1 -0
  38. package/dist/next/workflow-chat-route.cjs +8 -19
  39. package/dist/next/workflow-chat-route.cjs.map +1 -1
  40. package/dist/next/workflow-chat-route.d.cts +92 -4
  41. package/dist/next/workflow-chat-route.d.ts +92 -4
  42. package/dist/next/workflow-chat-route.js +127 -19
  43. package/dist/next/workflow-chat-route.js.map +1 -1
  44. package/dist/pages/admin/delegations/index.cjs +1 -1
  45. package/dist/pages/admin/delegations/index.js +1 -1
  46. package/dist/pages/delegations/index.cjs +1 -1
  47. package/dist/pages/delegations/index.js +1 -1
  48. package/dist/pages/instances/detail/index.cjs +1 -1
  49. package/dist/pages/instances/detail/index.js +1 -1
  50. package/dist/pages/templates/compose/index.cjs +1 -1
  51. package/dist/pages/templates/compose/index.js +1 -1
  52. package/dist/pages/templates/designer/index.cjs +1 -1
  53. package/dist/pages/templates/designer/index.js +1 -1
  54. package/dist/views/admin/delegations/index.cjs +1 -1
  55. package/dist/views/admin/delegations/index.js +1 -1
  56. package/dist/views/admin/index.cjs +1 -1
  57. package/dist/views/admin/index.js +1 -1
  58. package/dist/views/delegations/index.cjs +1 -1
  59. package/dist/views/delegations/index.js +1 -1
  60. package/dist/views/forms/builder/index.cjs +1 -1
  61. package/dist/views/forms/builder/index.js +1 -1
  62. package/dist/views/forms/renderer/FormRendererView.d.cts +25 -2
  63. package/dist/views/forms/renderer/FormRendererView.d.ts +25 -2
  64. package/dist/views/forms/renderer/file-upload-field.d.cts +84 -0
  65. package/dist/views/forms/renderer/file-upload-field.d.ts +84 -0
  66. package/dist/views/forms/renderer/form-data-source-field.d.cts +9 -1
  67. package/dist/views/forms/renderer/form-data-source-field.d.ts +9 -1
  68. package/dist/views/forms/renderer/form-value-readers.d.cts +7 -0
  69. package/dist/views/forms/renderer/form-value-readers.d.ts +7 -0
  70. package/dist/views/forms/renderer/index.cjs +1 -1
  71. package/dist/views/forms/renderer/index.d.cts +1 -0
  72. package/dist/views/forms/renderer/index.d.ts +1 -0
  73. package/dist/views/forms/renderer/index.js +1 -1
  74. package/dist/views/instances/detail/index.cjs +1 -1
  75. package/dist/views/instances/detail/index.js +1 -1
  76. package/dist/views/instances/detail/sections/InstanceFormSection.d.cts +20 -3
  77. package/dist/views/instances/detail/sections/InstanceFormSection.d.ts +20 -3
  78. package/dist/views/instances/detail/sections/shared.d.cts +1 -2
  79. package/dist/views/instances/detail/sections/shared.d.ts +1 -2
  80. package/dist/views/instances/instance-error-message.d.cts +5 -0
  81. package/dist/views/instances/instance-error-message.d.ts +5 -0
  82. package/dist/views/instances/new/index.cjs +1 -1
  83. package/dist/views/instances/new/index.cjs.map +1 -1
  84. package/dist/views/instances/new/index.js +79 -76
  85. package/dist/views/instances/new/index.js.map +1 -1
  86. package/dist/views/templates/compose/index.cjs +1 -1
  87. package/dist/views/templates/compose/index.js +1 -1
  88. package/dist/views/templates/designer/designer-version-labels.d.cts +7 -0
  89. package/dist/views/templates/designer/designer-version-labels.d.ts +7 -0
  90. package/dist/views/templates/designer/expression/DialectUpgradeModal.d.cts +25 -0
  91. package/dist/views/templates/designer/expression/DialectUpgradeModal.d.ts +25 -0
  92. package/dist/views/templates/designer/expression/ExpressionField.d.cts +79 -0
  93. package/dist/views/templates/designer/expression/ExpressionField.d.ts +79 -0
  94. package/dist/views/templates/designer/expression/use-workflow-expression-references.d.cts +22 -0
  95. package/dist/views/templates/designer/expression/use-workflow-expression-references.d.ts +22 -0
  96. package/dist/views/templates/designer/expression/workflow-expression-edits.d.cts +58 -0
  97. package/dist/views/templates/designer/expression/workflow-expression-edits.d.ts +58 -0
  98. package/dist/views/templates/designer/expression/workflow-expression-tools.d.cts +14 -0
  99. package/dist/views/templates/designer/expression/workflow-expression-tools.d.ts +14 -0
  100. package/dist/views/templates/designer/field-access-matrix/FieldAccessMatrixDrawer.d.cts +7 -1
  101. package/dist/views/templates/designer/field-access-matrix/FieldAccessMatrixDrawer.d.ts +7 -1
  102. package/dist/views/templates/designer/index.cjs +1 -1
  103. package/dist/views/templates/designer/index.js +1 -1
  104. package/dist/views/templates/designer/notify-webhook-designer.d.cts +8 -0
  105. package/dist/views/templates/designer/notify-webhook-designer.d.ts +8 -0
  106. package/dist/views/templates/designer/use-workflow-designer-controller.d.cts +8 -2
  107. package/dist/views/templates/designer/use-workflow-designer-controller.d.ts +8 -2
  108. package/dist/views/templates/designer/workflow-chat-prompt.d.cts +18 -0
  109. package/dist/views/templates/designer/workflow-chat-prompt.d.ts +18 -0
  110. package/package.json +3 -3
  111. package/dist/chunks/FormBuilderView-CHaAQL5v.cjs +0 -3
  112. package/dist/chunks/FormBuilderView-CHaAQL5v.cjs.map +0 -1
  113. package/dist/chunks/FormBuilderView-vQfVc08Z.js.map +0 -1
  114. package/dist/chunks/FormRendererView-CTLlG13b.js +0 -1106
  115. package/dist/chunks/FormRendererView-CTLlG13b.js.map +0 -1
  116. package/dist/chunks/FormRendererView-ChmM_4Yc.cjs +0 -4
  117. package/dist/chunks/FormRendererView-ChmM_4Yc.cjs.map +0 -1
  118. package/dist/chunks/approver-filled-form-B4zPg9kz.cjs.map +0 -1
  119. package/dist/chunks/approver-filled-form-Bgazhbvd.js.map +0 -1
  120. package/dist/chunks/delegations-BfhCDz5e.js.map +0 -1
  121. package/dist/chunks/delegations-BmFGcuhA.cjs +0 -2
  122. package/dist/chunks/delegations-BmFGcuhA.cjs.map +0 -1
  123. package/dist/chunks/delegations-CcS0SHbf.cjs +0 -2
  124. package/dist/chunks/delegations-CcS0SHbf.cjs.map +0 -1
  125. package/dist/chunks/delegations-DBh5TT6G.js.map +0 -1
  126. package/dist/chunks/designer-Df79YzUn.cjs +0 -65
  127. package/dist/chunks/designer-Df79YzUn.cjs.map +0 -1
  128. package/dist/chunks/designer-ubnZbP-A.js.map +0 -1
  129. package/dist/chunks/detail-BBC9kqXd.cjs +0 -5
  130. package/dist/chunks/detail-BBC9kqXd.cjs.map +0 -1
  131. package/dist/chunks/detail-C6XKZjlF.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"FormRendererView-ChmM_4Yc.cjs","names":["isFormDataSourceFieldDefinition","useMemo","readFormDataSourceValueSignature","readFormDataSourceSelectedValues","readMissingFormDataSourceDependencies","useState","useRef","mergeFormDataSourceOptions","useCallback","previewFormFieldOptions","readFormFieldOptions","readSelectedFormDataSourceOptions","readFormDataSourceErrorMessage","readFormDataSourceErrorCode","previewResolveFormFieldOptions","resolveFormFieldOptions","readMissingFormDataSourceOptionValues","formatMemberLabel","readFormMemberIds","useState","useRef","resolveMembers","searchMembers","AutoComplete","useState","useRef","resolveOrgUnitOptions","searchOrgUnitOptions","AutoComplete","Typography","useState","buildFormRendererValues","useMemo","useRef","readVisibleFormRendererFields","Typography","isTableFieldDefinition","isFormRendererFieldRequired","isFormRendererFieldReadonly","isFormDataSourceFieldDefinition","BPMFormField","Button","readFormTableRows","readFormTableRowBounds","createFormTableRow","readFormTableCellPath","TrashIcon","Table","PlusIcon","isFormTableCellValue","Textarea","Toggle","isFormStaticOptionFieldDefinition","readFieldOptionAsSelectOption","Select","readSelectOption","Upload","DatePicker","formatDatePickerValue","readDatePickerValue","DateTimePicker","formatDateTimePickerValue","Input","clampOptionalNumber","parseOptionalNumberInput","readSelectedFormDataSourceOptions","readFormFieldSelectionMode","AutoComplete","RadioGroup","CheckboxGroup","require_bpm_form_field","require_format_member_label","react","_mezzanine_ui_react","_rytass_bpm_core_client","react_jsx_runtime","_mezzanine_ui_icons","_rytass_bpm_core_client_form","_rytass_bpm_core_shared_form","_rytass_bpm_core_client_organization","EMPTY_FIELD_STATE","NO_INVALID_VALUES","MIN_SCROLLABLE_OPTION_COUNT","RECOVERABLE_ERROR_CODES","useFormDataSourceField","dynamicField","input","queryFieldKey","snapshot","dynamicFieldSignature","snapshotSignature","snapshotOptions","formDataSignature","initialFormDataSignature","schemaSignature","uiSchemaSignature","contextKind","contextInstanceId","contextTemplateId","rowValuesSignature","initialRowValuesSignature","currentValue","currentValueSignature","selectedValues","hasValue","dataSourceRefreshSignature","binding","effectiveContext","valueChanged","bindingsChanged","isStale","needsResolve","quiescentStatus","readQuiescentFormDataSourceStatus","autoQueries","pendingQueryStatus","readPendingQueryStatus","remoteOptions","setRemoteOptions","searchText","setSearchText","nextCursor","setNextCursor","resolution","setResolution","status","setStatus","error","setError","loadedOptionCount","requestSequence","resolveSequence","optionsAbort","resolveAbort","lastRequest","queryFormData","readDefinedValues","queryRowValues","mergedOptions","currentResolution","resolvePending","invalidValues","readInvalidFormDataSourceValues","loadOptions","requestId","nextSearchTextToLoad","controller","isSupersededRequest","selectedOptions","currentOptions","cursor","result","loadOptionsRef","message","requestError","code","readFailedQueryStatus","runResolve","signature","runResolveRef","currentCursor","currentSearchText","currentError","onSearch","onReachBottom","retry","readFormDataSourceExposedStatus","readFormDataSourceFieldStatusMessage","state","isFormDataSourceFieldSubmissionBlocked","readFormDataSourceSubmissionBlockMessage","blocked","states","waiting","recoverable","sequence","values","key","value","defined","UNICODE_WHITESPACE_PATTERN","hasUnsafeCharacter","codePoint","character","ABSOLUTE_HTTP_PATTERN","resolveFormFieldLink","isSafeSiteRelativePath","isSafeAbsoluteHttpUrl","PLACEHOLDER_BASE","resolved","url","resolveMemberProfilesPerIdOnFailure","maxPerIdRequests","limits","concurrency","resolveMembers","memberIds","askedIds","settled","settleWithConcurrency","memberId","index","excludeFailedMemberIds","failedMemberIds","results","nextIndex","worker","items","run","UNKNOWN_MEMBER_NAME","readMemberFieldOption","member","readUnresolvedMemberFieldOption","mergeMemberFieldOptions","resolvedById","nextOptions","option","currentIds","addedOptions","candidate","upgradedOptions","readUnresolvedMemberIds","knownIds","knownOptions","readSelectedMemberFieldOptions","readMemberFieldValue","selected","multiple","MemberFieldControl","setKnownOptions","searchResults","setSearchResults","loading","setLoading","latestSearchRef","givenUpIdsRef","unresolvedKey","cancelled","profiles","placeholders","skippedIds","placeholder","current","handleSearch","options","commonProps","readonly","open","onChange","nextOption","UNKNOWN_ORG_UNIT_NAME","DELETED_ORG_UNIT_SUFFIX","readOrgUnitFieldOption","record","readPendingOrgUnitFieldOption","orgUnitId","readUnknownOrgUnitFieldOption","readStoredOrgUnitId","readOrgUnitIdKey","isSameOrgUnitId","left","right","readSelectedOrgUnitFieldOption","readUnresolvedOrgUnitId","mergeOrgUnitFieldOptions","namedById","readOrgUnitFieldValue","ORG_UNIT_SEARCH_FAILED_TEXT","OrgUnitFieldControl","searchFailed","setSearchFailed","unresolvedId","records","FORM_RENDERER_GRID_STYLE","FORM_RENDERER_EMPTY_STYLE","FORM_RENDERER_FIELD_STYLE","TEXTAREA_STYLE","applyFullWidthTextareaHost","element","FormRenderer","internalValues","setInternalValues","schema","valuesRef","visibleFields","uiSchema","readOwnFieldAccess","fieldAccess","field","currentValues","updateValue","nextValues","fieldKey","nextValue","singleColumn","FormRendererField","errors","fieldLabelMarkers","readFieldAccessReadonly","readFieldColumnSpan","emptyText","access","formReadonly","required","fields","fieldReadonly","dataSourceState","dataSourceContext","dataSourceInitialValues","dataSourceStatusMessage","dataSourceStateSignature","onDataSourceStateChange","labelMarker","FormTableField","renderControl","onUploadAttachment","DATA_SOURCE_FEEDBACK_STYLE","EXTERNAL_LINK_CUE","EXTERNAL_LINK_TITLE","LINK_STYLE","TABLE_FIELD_STYLE","TABLE_FIELD_ACTIONS_STYLE","TABLE_SCROLLER_STYLE","TABLE_COLUMN_MIN_WIDTH","TABLE_ACTIONS_COLUMN_WIDTH","TABLE_CELL_STYLE","rows","bounds","rowIdSequenceRef","rowIds","setRowIds","createRowId","commitRows","nextRows","handleAddRow","handleRemoveRow","rowIndex","handleCellChange","readNextTableRow","row","columnKey","cellValue","dataSource","columns","column","FormTableCell","readInitialTableRow","actions","readTableCellValue","rowValues","initialRowValues","path","statusMessage","stateSignature","sequenceRef","renderDynamicOptionControl","size","renderStaticControl","link","event","readStringValue","readStringArrayValue","readInputPlaceholder","files","file","readNumberInputValue","waitingForDependencies","interactiveSearch","optionControlDisabled","autoCompleteDisabled","item","type"],"sources":["../../src/views/forms/renderer/form-data-source-field.ts","../../src/views/forms/renderer/form-field-link.ts","../../src/lib/member-profile-resolution.ts","../../src/views/forms/renderer/member-field-options.ts","../../src/views/forms/renderer/member-field.tsx","../../src/views/forms/renderer/org-unit-field-options.ts","../../src/views/forms/renderer/org-unit-field.tsx","../../src/views/forms/renderer/FormRendererView.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n FormDataSourceValueSnapshots,\n FormDefinitionSchema,\n FormFieldDefinition,\n FormFieldOption,\n FormFieldValue,\n FormUiSchema,\n isFormDataSourceFieldDefinition,\n} from '@rytass/bpm-core-shared/form';\nimport {\n FormDataSourceErrorCode,\n FormDataSourceFieldStatus,\n FormRendererValues,\n mergeFormDataSourceOptions,\n previewResolveFormFieldOptions,\n readFormDataSourceErrorCode,\n readFormDataSourceErrorMessage,\n readMissingFormDataSourceDependencies,\n readMissingFormDataSourceOptionValues,\n previewFormFieldOptions,\n readFormDataSourceSelectedValues,\n readFormDataSourceValueSignature,\n readSelectedFormDataSourceOptions,\n readFormFieldOptions,\n resolveFormFieldOptions,\n} from '@rytass/bpm-core-client/form';\n\nexport type FormRendererDataSourceContext =\n | { readonly kind: 'preview' }\n | {\n readonly instanceId?: string | null;\n readonly kind: 'runtime';\n readonly templateId?: string | null;\n };\n\nexport interface FormDataSourceFieldState {\n /**\n * False when nothing can be re-requested — a read-only or context-less field\n * has no query to retry, so the view must not offer a dead retry action.\n */\n readonly canRetry: boolean;\n readonly error: string | null;\n readonly hasValue: boolean;\n readonly hasNextPage: boolean;\n readonly invalidValues: readonly string[];\n readonly onReachBottom: () => void;\n readonly onSearch: (searchText: string) => void;\n readonly options: readonly FormFieldOption[];\n readonly retry: () => void;\n readonly status: FormDataSourceFieldStatus;\n}\n\n/**\n * What makes a table cell different from a top-level field: it is addressed by\n * a schema path, snapshotted under an instance path, and its `ROW_FIELD`\n * bindings read from its own row rather than the form (ADR 16 §3.3, §3.5).\n */\nexport interface UseFormDataSourceFieldRow {\n /** The row as it was loaded, so a returned case can tell what changed. */\n readonly initialValues?: FormRendererValues;\n readonly values: FormRendererValues;\n}\n\nexport interface UseFormDataSourceFieldInput {\n readonly context?: FormRendererDataSourceContext;\n readonly field: FormFieldDefinition;\n /** Schema path used to address the field; defaults to its own key. */\n readonly fieldPath?: string;\n readonly formData: FormRendererValues;\n readonly initialFormData?: FormRendererValues;\n readonly initialValue?: FormFieldValue | undefined;\n readonly optionSnapshots?: FormDataSourceValueSnapshots;\n readonly readonly: boolean;\n /** Set only for a table cell. */\n readonly row?: UseFormDataSourceFieldRow;\n readonly schema: FormDefinitionSchema;\n /** Snapshot map key; defaults to the field key. */\n readonly snapshotKey?: string;\n readonly uiSchema: FormUiSchema;\n}\n\ninterface FormDataSourceResolution {\n readonly signature: string;\n readonly unresolvedValues: readonly string[];\n}\n\nconst EMPTY_FIELD_STATE: FormDataSourceFieldState = {\n canRetry: false,\n error: null,\n hasValue: false,\n hasNextPage: false,\n invalidValues: [],\n onReachBottom: (): void => undefined,\n onSearch: (): void => undefined,\n options: [],\n retry: (): void => undefined,\n status: 'IDLE',\n};\n\nconst NO_INVALID_VALUES: readonly string[] = [];\n\n/**\n * A dropdown only fires `onReachBottom` once its content overflows. A source\n * whose first page is smaller than the menu is therefore unreachable past page\n * one — `demo.cost-centers` serves 3 at a time, which fits exactly, so nothing\n * ever scrolls and the remaining options cannot be selected at all. Keep\n * pulling pages until there are enough rows to scroll, or the source runs out.\n */\nconst MIN_SCROLLABLE_OPTION_COUNT = 10;\n\n/**\n * Failures the filler can clear on their own by typing differently. They are\n * reported as an inline hint and never turn the field unavailable, because a\n * short search string says nothing about the validity of an existing value.\n */\nconst RECOVERABLE_ERROR_CODES: readonly FormDataSourceErrorCode[] = [\n 'FORM_DATA_SOURCE_SEARCH_NOT_SUPPORTED',\n 'FORM_DATA_SOURCE_SEARCH_TOO_SHORT',\n];\n\nexport function useFormDataSourceField(\n input: UseFormDataSourceFieldInput,\n): FormDataSourceFieldState {\n const dynamicField = isFormDataSourceFieldDefinition(input.field)\n ? input.field\n : null;\n const queryFieldKey = input.fieldPath ?? dynamicField?.fieldKey ?? '';\n const snapshot = dynamicField\n ? input.optionSnapshots?.[input.snapshotKey ?? dynamicField.fieldKey]\n : undefined;\n // The field object is rebuilt on every parent render, so depending on its\n // identity re-ran the load effect three extra times per change — each request\n // aborting the last. Depend on what actually decides the query instead.\n const dynamicFieldSignature = dynamicField\n ? JSON.stringify({\n dataSource: dynamicField.dataSource,\n fieldKey: dynamicField.fieldKey,\n type: dynamicField.type,\n })\n : '';\n const snapshotSignature = JSON.stringify(snapshot ?? null);\n const snapshotOptions = useMemo(\n (): readonly FormFieldOption[] => snapshot?.options ?? [],\n [snapshotSignature],\n );\n const formDataSignature = JSON.stringify(input.formData);\n const initialFormDataSignature = JSON.stringify(input.initialFormData ?? {});\n const schemaSignature = JSON.stringify(input.schema);\n const uiSchemaSignature = JSON.stringify(input.uiSchema);\n const contextKind = input.context?.kind;\n const contextInstanceId =\n input.context?.kind === 'runtime' ? input.context.instanceId : undefined;\n const contextTemplateId =\n input.context?.kind === 'runtime' ? input.context.templateId : undefined;\n const rowValuesSignature = JSON.stringify(input.row?.values ?? null);\n const initialRowValuesSignature = JSON.stringify(\n input.row?.initialValues ?? null,\n );\n // A cell's value lives on its row, never on the form data — a top-level field\n // may even share the column's key.\n const currentValue = dynamicField\n ? input.row\n ? input.row.values[dynamicField.fieldKey]\n : input.formData[dynamicField.fieldKey]\n : undefined;\n const currentValueSignature = readFormDataSourceValueSignature(currentValue);\n const selectedValues = useMemo(\n (): readonly string[] => readFormDataSourceSelectedValues(currentValue),\n [currentValueSignature],\n );\n const hasValue = selectedValues.length > 0;\n const dataSourceRefreshSignature = dynamicField\n ? JSON.stringify({\n bindings: dynamicField.dataSource.bindings.map((binding) =>\n binding.from.kind === 'FIELD'\n ? {\n fieldKey: binding.from.fieldKey,\n value: input.formData[binding.from.fieldKey],\n }\n : binding.from.kind === 'CONSTANT'\n ? { value: binding.from.value }\n : {\n columnKey: binding.from.columnKey,\n value: input.row?.values[binding.from.columnKey],\n },\n ),\n value:\n typeof input.initialFormData !== 'undefined' ||\n typeof input.initialValue !== 'undefined'\n ? currentValue\n : undefined,\n })\n : '';\n const effectiveContext = useMemo(\n (): FormRendererDataSourceContext | undefined => {\n if (contextKind === 'preview') {\n return { kind: 'preview' };\n }\n\n if (contextKind === 'runtime' && !input.readonly) {\n return {\n instanceId: contextInstanceId,\n kind: 'runtime',\n templateId: contextTemplateId,\n };\n }\n\n return undefined;\n },\n [\n contextInstanceId,\n contextKind,\n contextTemplateId,\n input.readonly,\n ],\n );\n const valueChanged = dynamicField\n ? (typeof input.initialFormData !== 'undefined' ||\n typeof input.initialValue !== 'undefined') &&\n readFormDataSourceValueSignature(input.initialValue) !==\n currentValueSignature\n : false;\n const bindingsChanged = dynamicField\n ? typeof input.initialFormData !== 'undefined' &&\n dynamicField.dataSource.bindings.some((binding) => {\n if (binding.from.kind === 'FIELD') {\n return (\n readFormDataSourceValueSignature(\n input.initialFormData?.[binding.from.fieldKey],\n ) !==\n readFormDataSourceValueSignature(\n input.formData[binding.from.fieldKey],\n )\n );\n }\n\n if (binding.from.kind === 'ROW_FIELD' && input.row?.initialValues) {\n return (\n readFormDataSourceValueSignature(\n input.row.initialValues[binding.from.columnKey],\n ) !==\n readFormDataSourceValueSignature(\n input.row.values[binding.from.columnKey],\n )\n );\n }\n\n return false;\n })\n : false;\n const isStale = Boolean(\n (snapshot || hasValue) && (valueChanged || bindingsChanged),\n );\n // A stale field with a value is only trustworthy once the host has resolved\n // it: display options may still carry the value through the snapshot merge.\n const needsResolve = Boolean(dynamicField && effectiveContext && isStale && hasValue);\n const quiescentStatus = readQuiescentFormDataSourceStatus({\n hasSnapshot: Boolean(snapshot),\n hasValue,\n isStale,\n readonly: input.readonly,\n });\n // AutoComplete only queries on search, so every other control is the one that\n // loads by itself as soon as it has a context.\n const autoQueries = Boolean(\n dynamicField && effectiveContext && dynamicField.type !== 'autocomplete',\n );\n // Optimistic, and only until the first answer arrives: a binding pointing at\n // an empty field is usually blocking, and showing the control as usable for\n // one round trip would flash an enabled, empty list. AutoComplete is excluded\n // because nothing would ever correct the guess — it cannot be searched while\n // disabled, so an optional binding would lock it for good.\n const waitsForDependencies =\n autoQueries &&\n dynamicField !== null &&\n readMissingFormDataSourceDependencies(\n dynamicField,\n input.formData,\n input.row?.values,\n ).length > 0;\n const pendingQueryStatus = readPendingQueryStatus(isStale, waitsForDependencies);\n const [remoteOptions, setRemoteOptions] = useState<readonly FormFieldOption[]>(\n [],\n );\n const [searchText, setSearchText] = useState('');\n const [nextCursor, setNextCursor] = useState<string | null>(null);\n const [resolution, setResolution] = useState<FormDataSourceResolution | null>(\n null,\n );\n const [status, setStatus] = useState<FormDataSourceFieldStatus>(\n (): FormDataSourceFieldStatus => {\n if (!dynamicField) {\n return 'IDLE';\n }\n\n // The very first paint already reflects the query the effect is about to\n // fire, so the control never appears usable before its options exist.\n return autoQueries ? pendingQueryStatus : quiescentStatus;\n },\n );\n const [error, setError] = useState<string | null>(null);\n const loadedOptionCount = useRef(0);\n const requestSequence = useRef(0);\n const resolveSequence = useRef(0);\n const optionsAbort = useRef<AbortController | null>(null);\n const resolveAbort = useRef<AbortController | null>(null);\n const lastRequest = useRef<{ readonly cursor: string | null; readonly searchText: string }>({\n cursor: null,\n searchText: '',\n });\n\n const queryFormData = useMemo(\n (): Readonly<Record<string, FormFieldValue>> =>\n readDefinedValues(input.formData),\n [formDataSignature],\n );\n const queryRowValues = useMemo(\n (): Readonly<Record<string, FormFieldValue>> | undefined =>\n input.row ? readDefinedValues(input.row.values) : undefined,\n [rowValuesSignature],\n );\n const mergedOptions = useMemo(\n (): readonly FormFieldOption[] =>\n mergeFormDataSourceOptions(snapshotOptions, remoteOptions),\n [remoteOptions, snapshotOptions],\n );\n const currentResolution =\n resolution && resolution.signature === dataSourceRefreshSignature\n ? resolution\n : null;\n const resolvePending = needsResolve && !currentResolution;\n const invalidValues = readInvalidFormDataSourceValues({\n options: mergedOptions,\n resolution: currentResolution,\n resolvePending,\n value: currentValue,\n });\n\n const loadOptions = useCallback(\n (nextSearchTextToLoad: string, cursor: string | null): void => {\n if (!dynamicField || !effectiveContext) {\n return;\n }\n\n const requestId = requestSequence.current + 1;\n requestSequence.current = requestId;\n lastRequest.current = {\n cursor,\n searchText: nextSearchTextToLoad,\n };\n optionsAbort.current?.abort();\n const controller = new AbortController();\n optionsAbort.current = controller;\n setStatus(pendingQueryStatus);\n setError(null);\n\n const request =\n effectiveContext.kind === 'preview'\n ? previewFormFieldOptions({\n cursor,\n fieldKey: queryFieldKey,\n formData: queryFormData,\n rowValues: queryRowValues,\n schema: input.schema,\n searchText: nextSearchTextToLoad,\n signal: controller.signal,\n uiSchema: input.uiSchema,\n })\n : readFormFieldOptions({\n cursor,\n fieldKey: queryFieldKey,\n formData: queryFormData,\n instanceId: effectiveContext.instanceId ?? null,\n rowValues: queryRowValues,\n searchText: nextSearchTextToLoad,\n signal: controller.signal,\n templateId: effectiveContext.templateId ?? null,\n });\n\n void request\n .then((result): void => {\n if (isSupersededRequest(controller, requestSequence, requestId)) {\n return;\n }\n\n setRemoteOptions((currentOptions) => {\n const selectedOptions = readSelectedFormDataSourceOptions(\n currentValue,\n currentOptions,\n );\n\n return mergeFormDataSourceOptions(\n selectedOptions,\n cursor ? currentOptions : [],\n result.options,\n );\n });\n setNextCursor(result.nextCursor);\n setError(null);\n loadedOptionCount.current = cursor\n ? loadedOptionCount.current + result.options.length\n : result.options.length;\n\n // An empty page that still hands back a cursor would loop forever.\n if (\n result.nextCursor &&\n result.options.length > 0 &&\n loadedOptionCount.current < MIN_SCROLLABLE_OPTION_COUNT\n ) {\n loadOptionsRef.current(nextSearchTextToLoad, result.nextCursor);\n }\n\n // Only the host knows which bound parameters are required, so a\n // control is disabled on its answer instead of on a browser guess.\n setStatus(\n result.waitingForFieldKeys.length\n ? 'WAITING_FOR_DEPENDENCIES'\n : 'VALID',\n );\n })\n .catch((requestError: unknown): void => {\n if (isSupersededRequest(controller, requestSequence, requestId)) {\n return;\n }\n\n // Anything that is not a DataSource code — a transport failure, or a\n // server-side error the host deliberately reduced to a generic\n // message — must not be rendered verbatim; it is English technical\n // copy the filler cannot act on.\n const message =\n readFormDataSourceErrorMessage(requestError) ??\n '選項來源暫時無法使用。';\n const code = readFormDataSourceErrorCode(requestError);\n\n setError(message);\n setStatus(\n readFailedQueryStatus(\n Boolean(code && RECOVERABLE_ERROR_CODES.includes(code)),\n hasValue,\n ),\n );\n });\n },\n [\n currentValueSignature,\n dynamicFieldSignature,\n effectiveContext,\n hasValue,\n pendingQueryStatus,\n queryFieldKey,\n queryFormData,\n queryRowValues,\n schemaSignature,\n uiSchemaSignature,\n ],\n );\n const loadOptionsRef = useRef(loadOptions);\n loadOptionsRef.current = loadOptions;\n\n const runResolve = useCallback((): void => {\n if (!dynamicField || !effectiveContext || selectedValues.length === 0) {\n return;\n }\n\n const signature = dataSourceRefreshSignature;\n const requestId = resolveSequence.current + 1;\n resolveSequence.current = requestId;\n resolveAbort.current?.abort();\n const controller = new AbortController();\n resolveAbort.current = controller;\n setError(null);\n\n const request =\n effectiveContext.kind === 'preview'\n ? previewResolveFormFieldOptions({\n fieldKey: queryFieldKey,\n formData: queryFormData,\n rowValues: queryRowValues,\n schema: input.schema,\n signal: controller.signal,\n uiSchema: input.uiSchema,\n values: selectedValues,\n })\n : resolveFormFieldOptions({\n fieldKey: queryFieldKey,\n formData: queryFormData,\n instanceId: effectiveContext.instanceId ?? null,\n rowValues: queryRowValues,\n signal: controller.signal,\n templateId: effectiveContext.templateId ?? null,\n values: selectedValues,\n });\n\n void request\n .then((result): void => {\n if (isSupersededRequest(controller, resolveSequence, requestId)) {\n return;\n }\n\n if (result.waitingForFieldKeys.length) {\n // The host could not consult the provider, so this is no answer about\n // the value: leave the field waiting rather than call it resolved.\n setStatus('WAITING_FOR_DEPENDENCIES');\n\n return;\n }\n\n // Authoritative labels for the values that survived, merged so the old\n // snapshot label of an unresolved value stays visible for recognition.\n setRemoteOptions((currentOptions) =>\n mergeFormDataSourceOptions(currentOptions, result.options),\n );\n setResolution({\n signature,\n unresolvedValues: result.unresolvedValues,\n });\n // `invalidValues` turns this into `INVALID` when anything went missing.\n setStatus('VALID');\n })\n .catch((requestError: unknown): void => {\n if (isSupersededRequest(controller, resolveSequence, requestId)) {\n return;\n }\n\n setStatus('UNAVAILABLE');\n setError(\n readFormDataSourceErrorMessage(requestError) ??\n '選項來源暫時無法使用。',\n );\n });\n }, [\n dataSourceRefreshSignature,\n dynamicFieldSignature,\n effectiveContext,\n queryFieldKey,\n queryFormData,\n queryRowValues,\n schemaSignature,\n selectedValues,\n uiSchemaSignature,\n ]);\n const runResolveRef = useRef(runResolve);\n runResolveRef.current = runResolve;\n\n useEffect((): void => {\n requestSequence.current += 1;\n optionsAbort.current?.abort();\n setRemoteOptions((currentOptions) =>\n readSelectedFormDataSourceOptions(currentValue, currentOptions),\n );\n setNextCursor((currentCursor) =>\n currentCursor === null ? currentCursor : null,\n );\n setSearchText((currentSearchText) =>\n currentSearchText ? '' : currentSearchText,\n );\n setError((currentError) => (currentError ? null : currentError));\n\n if (!dynamicField) {\n setStatus('IDLE');\n\n return;\n }\n\n if (!autoQueries) {\n // AutoComplete queries on search only, so it stays quiescent until the\n // filler types; a context-less field has nothing to query at all.\n setStatus(quiescentStatus);\n\n return;\n }\n\n loadOptionsRef.current('', null);\n }, [\n autoQueries,\n dynamicFieldSignature,\n effectiveContext,\n dataSourceRefreshSignature,\n initialFormDataSignature,\n initialRowValuesSignature,\n input.readonly,\n pendingQueryStatus,\n quiescentStatus,\n snapshotSignature,\n ]);\n\n useEffect((): void => {\n if (!needsResolve) {\n return;\n }\n\n runResolveRef.current();\n }, [dataSourceRefreshSignature, effectiveContext, needsResolve]);\n\n useEffect((): (() => void) => {\n return (): void => {\n optionsAbort.current?.abort();\n resolveAbort.current?.abort();\n };\n }, []);\n\n const onSearch = useCallback(\n (nextSearchTextToLoad: string): void => {\n setSearchText(nextSearchTextToLoad);\n setRemoteOptions((currentOptions) =>\n readSelectedFormDataSourceOptions(currentValue, currentOptions),\n );\n setNextCursor(null);\n loadOptions(nextSearchTextToLoad, null);\n },\n [loadOptions],\n );\n const onReachBottom = useCallback((): void => {\n if (nextCursor) {\n loadOptions(searchText, nextCursor);\n }\n }, [loadOptions, nextCursor, searchText]);\n const retry = useCallback((): void => {\n if (!effectiveContext) {\n return;\n }\n\n loadOptions(lastRequest.current.searchText, lastRequest.current.cursor);\n\n if (needsResolve) {\n runResolveRef.current();\n }\n }, [effectiveContext, loadOptions, needsResolve]);\n\n if (!dynamicField) {\n return EMPTY_FIELD_STATE;\n }\n\n return {\n canRetry: Boolean(effectiveContext),\n error,\n hasValue,\n hasNextPage: Boolean(nextCursor),\n invalidValues,\n onReachBottom,\n onSearch,\n options: mergedOptions,\n retry,\n status: readFormDataSourceExposedStatus({\n invalidValues,\n resolvePending,\n status,\n }),\n };\n}\n\nexport function readFormDataSourceFieldStatusMessage(\n state: FormDataSourceFieldState,\n): string | null {\n switch (state.status) {\n case 'WAITING_FOR_DEPENDENCIES':\n return '請先填寫相依欄位。';\n case 'STALE':\n return '選項需要重新載入或驗證。';\n case 'INVALID':\n return state.invalidValues.length\n ? `無法辨識選項:${state.invalidValues.join('、')}`\n : '目前選項已失效,請重新選擇。';\n case 'UNAVAILABLE':\n return state.error ?? '選項來源暫時無法使用。';\n case 'VALID':\n // A recoverable search hint outranks the empty-result copy: it is what\n // tells the filler how to get results at all.\n return state.error ?? (state.options.length ? null : '目前沒有可用選項。');\n default:\n return state.error;\n }\n}\n\nexport function isFormDataSourceFieldSubmissionBlocked(\n state: Pick<FormDataSourceFieldState, 'hasValue' | 'status'>,\n): boolean {\n return state.hasValue && state.status !== 'VALID';\n}\n\n/**\n * The copy to show when a submission is refused because of dynamic options.\n * `LOADING` and `STALE` clear on their own once the pending query answers, so\n * saying \"finish validating\" reads as a dead end for what is really a \"wait a\n * moment\"; the caller would otherwise show the same permanent-sounding line for\n * both cases.\n *\n * Returns `null` when nothing blocks the submission.\n */\nexport function readFormDataSourceSubmissionBlockMessage(\n states: readonly Pick<FormDataSourceFieldState, 'hasValue' | 'status'>[],\n): string | null {\n const blocked = states.filter(isFormDataSourceFieldSubmissionBlocked);\n\n if (blocked.length === 0) {\n return null;\n }\n\n return blocked.every(\n (state) => state.status === 'LOADING' || state.status === 'STALE',\n )\n ? '選項驗證中,請稍候再送出。'\n : '請先完成動態選項驗證。';\n}\n\n/**\n * The values the field holds that no option accounts for.\n *\n * A completed resolve is the authority — the loaded pages are not, because the\n * option snapshot is merged into them for display and would vouch for a value\n * the source has already dropped.\n */\nfunction readInvalidFormDataSourceValues(input: {\n readonly options: readonly FormFieldOption[];\n readonly resolution: FormDataSourceResolution | null;\n readonly resolvePending: boolean;\n readonly value: FormFieldValue | undefined;\n}): readonly string[] {\n if (input.resolution) {\n return input.resolution.unresolvedValues;\n }\n\n // While the host is being asked, its answer is the only one worth showing.\n if (input.resolvePending) {\n return NO_INVALID_VALUES;\n }\n\n return readMissingFormDataSourceOptionValues(input.value, input.options);\n}\n\n/**\n * Status of a field nobody is querying — read-only history, a Designer preview\n * before the first search, or a control the host gave no context for.\n */\nfunction readQuiescentFormDataSourceStatus(input: {\n readonly hasSnapshot: boolean;\n readonly hasValue: boolean;\n readonly isStale: boolean;\n readonly readonly: boolean;\n}): FormDataSourceFieldStatus {\n if (input.isStale) {\n return 'STALE';\n }\n\n if (input.hasSnapshot) {\n return 'VALID';\n }\n\n // A read-only value with no snapshot cannot be labelled from anywhere, which\n // is worth saying. An empty read-only field is simply blank — warning about a\n // source nobody is going to query would be noise.\n return input.readonly && input.hasValue ? 'UNAVAILABLE' : 'IDLE';\n}\n\n/**\n * Status held while a query is in flight.\n *\n * `waiting` is the browser's optimistic guess and only survives until the host\n * answers: `waitingForFieldKeys` from the response replaces it either way, so an\n * optional binding left empty clears within one round trip instead of locking\n * the control.\n */\nfunction readPendingQueryStatus(\n isStale: boolean,\n waiting: boolean,\n): FormDataSourceFieldStatus {\n if (waiting) {\n return 'WAITING_FOR_DEPENDENCIES';\n }\n\n return isStale ? 'STALE' : 'LOADING';\n}\n\n/**\n * Status after a rejected query. A recoverable failure leaves the field as\n * usable as it was — only a broken source makes it unavailable.\n */\nfunction readFailedQueryStatus(\n recoverable: boolean,\n hasValue: boolean,\n): FormDataSourceFieldStatus {\n if (!recoverable) {\n return 'UNAVAILABLE';\n }\n\n return hasValue ? 'VALID' : 'IDLE';\n}\n\nfunction readFormDataSourceExposedStatus(input: {\n readonly invalidValues: readonly string[];\n readonly resolvePending: boolean;\n readonly status: FormDataSourceFieldStatus;\n}): FormDataSourceFieldStatus {\n if (\n input.status === 'LOADING' ||\n input.status === 'UNAVAILABLE' ||\n input.status === 'WAITING_FOR_DEPENDENCIES'\n ) {\n return input.status;\n }\n\n if (input.invalidValues.length) {\n return 'INVALID';\n }\n\n return input.resolvePending ? 'STALE' : input.status;\n}\n\n/**\n * True when a response no longer belongs to the field: its request was aborted\n * by a newer one, or a newer one started before it settled. Such a response\n * must not touch state — an aborted request is not a failure to report.\n */\nfunction isSupersededRequest(\n controller: AbortController,\n sequence: { readonly current: number },\n requestId: number,\n): boolean {\n return controller.signal.aborted || sequence.current !== requestId;\n}\n\nfunction readDefinedValues(\n values: FormRendererValues,\n): Readonly<Record<string, FormFieldValue>> {\n return Object.entries(values).reduce<\n Readonly<Record<string, FormFieldValue>>\n >((defined, [key, value]) => {\n if (typeof value === 'undefined') {\n return defined;\n }\n\n return { ...defined, [key]: value };\n }, {});\n}\n","export interface FormFieldLink {\n /** Absolute links open in a new tab; site-relative ones stay in this tab. */\n readonly external: boolean;\n readonly href: string;\n}\n\nconst UNICODE_WHITESPACE_PATTERN = /\\s/u;\n\n// Whitespace and control characters are refused anywhere in the value: URL\n// parsers silently drop tabs and newlines, so \"/\\t/evil.example\" would turn\n// into the protocol-relative \"//evil.example\" after our `//` check passed.\nfunction hasUnsafeCharacter(value: string): boolean {\n return [...value].some((character): boolean => {\n const codePoint = character.codePointAt(0) ?? 0;\n\n return (\n // C0 controls and the space, DEL, C1 controls and the no-break space.\n codePoint <= 0x20 ||\n (codePoint >= 0x7f && codePoint <= 0xa0) ||\n UNICODE_WHITESPACE_PATTERN.test(character)\n );\n });\n}\n\nconst ABSOLUTE_HTTP_PATTERN = /^https?:\\/\\//iu;\n\n/**\n * Decides whether a `displayAs: 'link'` text value may become an anchor.\n * Returns `null` for everything that must stay plain text.\n *\n * Allowed: a site-relative path (`/` first, never `//`) or an absolute\n * `http(s)://` URL with a host and no embedded credentials. Everything else —\n * `javascript:`, `data:`, protocol-relative `//host`, backslash tricks\n * (`/\\host` is `//host` to a browser), padded or multi-line values, non-strings\n * — is refused rather than repaired.\n */\nexport function resolveFormFieldLink(value: unknown): FormFieldLink | null {\n if (\n typeof value !== 'string' ||\n !value ||\n value.includes('\\\\') ||\n hasUnsafeCharacter(value)\n ) {\n return null;\n }\n\n if (value.startsWith('/')) {\n return !value.startsWith('//') && isSafeSiteRelativePath(value)\n ? { external: false, href: value }\n : null;\n }\n\n return ABSOLUTE_HTTP_PATTERN.test(value) && isSafeAbsoluteHttpUrl(value)\n ? { external: true, href: value }\n : null;\n}\n\nconst PLACEHOLDER_BASE = 'http://placeholder.invalid';\n\n/**\n * Dot segments collapse before navigation: `/.//evil.example` resolves to the\n * path `//evil.example`, which a naive `startsWith('//')` check misses. Resolve\n * the value against a throw-away origin and require that it stays on that\n * origin and does not come out as a protocol-relative path.\n */\nfunction isSafeSiteRelativePath(value: string): boolean {\n try {\n const resolved = new URL(value, PLACEHOLDER_BASE);\n\n return (\n resolved.origin === PLACEHOLDER_BASE && !resolved.pathname.startsWith('//')\n );\n } catch {\n return false;\n }\n}\n\nfunction isSafeAbsoluteHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n\n return (\n (url.protocol === 'http:' || url.protocol === 'https:') &&\n Boolean(url.hostname) &&\n !url.username &&\n !url.password\n );\n } catch {\n return false;\n }\n}\n","import type { MemberProfileRecord } from '@rytass/bpm-core-client/template';\n\n/** Most ids asked for on their own after a failed batch, per round. */\nexport const MAX_PER_ID_REQUESTS = 20;\n\n/** Most per-id requests in flight at once. */\nexport const PER_ID_CONCURRENCY = 4;\n\nexport type MemberProfileResolution = Readonly<{\n /** Ids whose lookup failed even when asked for on their own. */\n failedIds: readonly string[];\n profiles: readonly MemberProfileRecord[];\n /**\n * Ids left unasked because a failed batch held more than the per-id budget.\n * They were not tried, so a later round may ask for them again.\n */\n skippedIds: readonly string[];\n}>;\n\nexport type MemberProfileResolutionLimits = Readonly<{\n concurrency?: number;\n maxPerIdRequests?: number;\n}>;\n\n/**\n * Looks up member profiles in one batch, and degrades per id when the batch\n * request fails.\n *\n * The host fails a whole lookup when it contains an id it cannot handle (a\n * foreign id stored in a template, say). Treating that as \"nobody resolved\"\n * labels every approver of the template 「未知會員」 for the sake of one bad\n * id, so a failed batch is retried one id at a time and only the ids that fail\n * on their own are reported back in `failedIds`.\n *\n * The retry is bounded: at most `maxPerIdRequests` ids are asked for on their\n * own (the rest come back in `skippedIds` without a request), and at most\n * `concurrency` of those requests run at once, so one bad id in a large\n * template cannot turn into a burst of requests.\n *\n * A single-id batch is not retried: it already is the per-id request.\n */\nexport async function resolveMemberProfilesPerIdOnFailure(\n memberIds: readonly string[],\n resolveMembers: (\n memberIds: readonly string[],\n ) => Promise<readonly MemberProfileRecord[]>,\n limits: MemberProfileResolutionLimits = {},\n): Promise<MemberProfileResolution> {\n const maxPerIdRequests = limits.maxPerIdRequests ?? MAX_PER_ID_REQUESTS;\n const concurrency = Math.max(1, limits.concurrency ?? PER_ID_CONCURRENCY);\n\n try {\n return {\n failedIds: [],\n profiles: await resolveMembers(memberIds),\n skippedIds: [],\n };\n } catch {\n if (memberIds.length <= 1) {\n return { failedIds: memberIds, profiles: [], skippedIds: [] };\n }\n }\n\n const askedIds = memberIds.slice(0, maxPerIdRequests);\n const settled = await settleWithConcurrency(\n askedIds,\n concurrency,\n (memberId) => resolveMembers([memberId]),\n );\n\n return {\n failedIds: askedIds.filter(\n (_, index) => settled[index].status === 'rejected',\n ),\n profiles: settled.flatMap((result) =>\n result.status === 'fulfilled' ? result.value : [],\n ),\n skippedIds: memberIds.slice(maxPerIdRequests),\n };\n}\n\n/**\n * The ids still worth asking the directory for: those that already failed on\n * their own in this session are left out, so they stay 「未知會員」 instead of\n * being re-requested every time the option list changes.\n *\n * A failed id stays 「未知會員」 for the rest of the session, even when the\n * failure was transient (a network blip, say), unless a later member search\n * returns it and replaces the placeholder. Ids skipped over the per-id cap\n * were never asked and must not be added to `failedMemberIds`.\n */\nexport function excludeFailedMemberIds(\n memberIds: readonly string[],\n failedMemberIds: ReadonlySet<string>,\n): readonly string[] {\n return memberIds.filter((memberId) => !failedMemberIds.has(memberId));\n}\n\nasync function settleWithConcurrency<TItem, TResult>(\n items: readonly TItem[],\n concurrency: number,\n run: (item: TItem) => Promise<TResult>,\n): Promise<readonly PromiseSettledResult<TResult>[]> {\n const results: PromiseSettledResult<TResult>[] = [];\n let nextIndex = 0;\n\n async function worker(): Promise<void> {\n while (nextIndex < items.length) {\n const index = nextIndex;\n\n nextIndex += 1;\n results[index] = await run(items[index]).then(\n (value): PromiseSettledResult<TResult> => ({\n status: 'fulfilled',\n value,\n }),\n (reason: unknown): PromiseSettledResult<TResult> => ({\n reason,\n status: 'rejected',\n }),\n );\n }\n }\n\n await Promise.all(\n Array.from({ length: Math.min(concurrency, items.length) }, worker),\n );\n\n return results;\n}\n","import type { FormFieldValue } from '@rytass/bpm-core-shared/form';\nimport { readFormMemberIds } from '@rytass/bpm-core-shared/form';\nimport { formatMemberLabel } from '../../../lib/format-member-label';\n\n/**\n * One pickable member, shaped for Mezzanine `AutoComplete` (`id` + `name`).\n * `unresolved` marks the placeholder written when a stored id has no profile,\n * so a real profile arriving later replaces it instead of the placeholder\n * sticking (the designer does the same for its approver list).\n */\nexport type MemberFieldOption = Readonly<{\n id: string;\n name: string;\n unresolved?: boolean;\n}>;\n\nexport const UNKNOWN_MEMBER_NAME = '未知會員';\n\nexport function readMemberFieldOption(member: {\n readonly email: string;\n readonly memberId: string;\n readonly name: string;\n}): MemberFieldOption {\n return {\n id: member.memberId,\n name: formatMemberLabel(member.name, member.email),\n };\n}\n\nexport function readUnresolvedMemberFieldOption(\n memberId: string,\n): MemberFieldOption {\n return { id: memberId, name: UNKNOWN_MEMBER_NAME, unresolved: true };\n}\n\n/**\n * Adds `nextOptions` to `currentOptions`. A resolved option replaces an\n * unresolved placeholder with the same id; otherwise the first one seen wins.\n * Returns `currentOptions` itself when nothing changed, so state setters can\n * bail out of a re-render.\n */\nexport function mergeMemberFieldOptions(\n currentOptions: readonly MemberFieldOption[],\n nextOptions: readonly MemberFieldOption[],\n): readonly MemberFieldOption[] {\n const resolvedById = new Map(\n nextOptions\n .filter((option) => !option.unresolved)\n .map((option) => [option.id, option] as const),\n );\n const currentIds = new Set(currentOptions.map((option) => option.id));\n const addedOptions = nextOptions.filter(\n (option, index) =>\n !currentIds.has(option.id) &&\n nextOptions.findIndex((candidate) => candidate.id === option.id) ===\n index,\n );\n const upgradedOptions = currentOptions.map(\n (option) =>\n (option.unresolved ? resolvedById.get(option.id) : undefined) ?? option,\n );\n const changed =\n addedOptions.length > 0 ||\n upgradedOptions.some((option, index) => option !== currentOptions[index]);\n\n return changed ? [...upgradedOptions, ...addedOptions] : currentOptions;\n}\n\n/** Ids in the stored value that no known option covers yet. */\nexport function readUnresolvedMemberIds(\n value: FormFieldValue | undefined,\n knownOptions: readonly MemberFieldOption[],\n): readonly string[] {\n const knownIds = new Set(\n knownOptions\n .filter((option) => !option.unresolved)\n .map((option) => option.id),\n );\n\n return readFormMemberIds(value).filter((memberId) => !knownIds.has(memberId));\n}\n\n/**\n * The stored value as options, in stored order. An id with no known profile\n * still shows up (as 「未知會員」) so the filler can see and remove it.\n */\nexport function readSelectedMemberFieldOptions(\n value: FormFieldValue | undefined,\n knownOptions: readonly MemberFieldOption[],\n): readonly MemberFieldOption[] {\n return readFormMemberIds(value).map(\n (memberId) =>\n knownOptions.find((option) => option.id === memberId) ??\n readUnresolvedMemberFieldOption(memberId),\n );\n}\n\n/**\n * What a picker change writes back. Nothing picked is `undefined`, the same as\n * every other optional field, so the renderer's own initial value (`[]` for a\n * multiple member field) is restored by `buildFormRendererValues`.\n */\nexport function readMemberFieldValue(\n selected: readonly MemberFieldOption[],\n multiple: boolean,\n): FormFieldValue | undefined {\n if (selected.length === 0) {\n return undefined;\n }\n\n return multiple ? selected.map((option) => option.id) : selected[0]?.id;\n}\n","'use client';\n\nimport { ReactElement, useEffect, useRef, useState } from 'react';\nimport { AutoComplete } from '@mezzanine-ui/react';\nimport { resolveMembers, searchMembers } from '@rytass/bpm-core-client';\nimport type { FormFieldValue } from '@rytass/bpm-core-shared/form';\nimport {\n excludeFailedMemberIds,\n resolveMemberProfilesPerIdOnFailure,\n} from '../../../lib/member-profile-resolution';\nimport {\n mergeMemberFieldOptions,\n readMemberFieldOption,\n readMemberFieldValue,\n readSelectedMemberFieldOptions,\n readUnresolvedMemberFieldOption,\n readUnresolvedMemberIds,\n type MemberFieldOption,\n} from './member-field-options';\n\nexport interface MemberFieldControlProps {\n readonly multiple: boolean;\n readonly name: string;\n readonly onChange: (value: FormFieldValue | undefined) => void;\n readonly placeholder?: string;\n readonly readonly: boolean;\n readonly size?: 'main' | 'sub';\n readonly value: FormFieldValue | undefined;\n}\n\n/**\n * 人員選擇 picker. Searching and name lookup reuse the directory queries the\n * admin member picker and the workflow designer already call\n * (`searchMembers` / `resolveMembers`); nothing new is added server side.\n *\n * Stored values are member ids. The names shown here are resolved on mount and\n * whenever a stored id has no profile yet, so a read-only view of a submitted\n * form (instance detail) shows names, not ids.\n */\nexport function MemberFieldControl({\n multiple,\n name,\n onChange,\n placeholder,\n readonly,\n size,\n value,\n}: MemberFieldControlProps): ReactElement {\n const [knownOptions, setKnownOptions] = useState<\n readonly MemberFieldOption[]\n >([]);\n const [searchResults, setSearchResults] = useState<\n readonly MemberFieldOption[]\n >([]);\n const [loading, setLoading] = useState(false);\n const latestSearchRef = useRef(0);\n // Ids the directory could not name; they stay 「未知會員」 and are not asked\n // for again when the other ids of the value resolve and the key changes.\n // That holds for the rest of the session, a transient failure included,\n // unless a later search returns the member (the merge then replaces the\n // placeholder). Ids skipped over the per-id cap were never asked, so they are\n // not given up on.\n const givenUpIdsRef = useRef<Set<string>>(new Set());\n const unresolvedKey = readUnresolvedMemberIds(value, knownOptions).join('\\n');\n\n useEffect((): (() => void) => {\n const memberIds = excludeFailedMemberIds(\n unresolvedKey ? unresolvedKey.split('\\n') : [],\n givenUpIdsRef.current,\n );\n let cancelled = false;\n\n if (memberIds.length === 0) {\n return (): void => undefined;\n }\n\n // One id the directory cannot handle fails the whole batch, so a failed\n // batch is retried per id: the other members keep their names.\n void resolveMemberProfilesPerIdOnFailure(memberIds, resolveMembers).then(\n ({ profiles, skippedIds }): void => {\n if (cancelled) {\n return;\n }\n\n const resolved = profiles.map(readMemberFieldOption);\n // Ids the directory did not return stay visible as placeholders.\n const placeholders = memberIds\n .filter(\n (memberId) => !resolved.some((option) => option.id === memberId),\n )\n .map(readUnresolvedMemberFieldOption);\n\n // Only the ids that were asked for and still came back without a\n // profile are given up on, so a failed lookup is not retried in a loop.\n // Ids skipped over the per-id cap were never asked: they keep a\n // placeholder but stay eligible for the next round, as the designer\n // does.\n placeholders\n .filter((placeholder) => !skippedIds.includes(placeholder.id))\n .forEach((placeholder) => givenUpIdsRef.current.add(placeholder.id));\n\n setKnownOptions((current) =>\n mergeMemberFieldOptions(current, [...resolved, ...placeholders]),\n );\n },\n );\n\n return (): void => {\n cancelled = true;\n };\n }, [unresolvedKey]);\n\n async function handleSearch(searchText: string): Promise<void> {\n const requestId = latestSearchRef.current + 1;\n\n latestSearchRef.current = requestId;\n setLoading(true);\n\n try {\n const options = (await searchMembers(searchText)).map(\n readMemberFieldOption,\n );\n\n if (latestSearchRef.current === requestId) {\n setSearchResults(options);\n setKnownOptions((current) => mergeMemberFieldOptions(current, options));\n }\n } catch {\n if (latestSearchRef.current === requestId) {\n setSearchResults([]);\n }\n } finally {\n if (latestSearchRef.current === requestId) {\n setLoading(false);\n }\n }\n }\n\n const selected = readSelectedMemberFieldOptions(value, knownOptions);\n // Selected members stay in the option list so a picked member can be\n // un-ticked from the dropdown, not only through its tag.\n const options = [...mergeMemberFieldOptions(selected, searchResults)];\n const commonProps = {\n asyncData: true,\n clearSearchText: true,\n disabled: readonly,\n disabledOptionsFilter: true,\n emptyText: '沒有符合的成員',\n globalPortal: true,\n inputProps: {\n autoCapitalize: 'none',\n autoCorrect: 'off',\n name,\n spellCheck: false,\n },\n loading,\n loadingText: '搜尋成員中...',\n onSearch: handleSearch,\n onVisibilityChange: (open: boolean): void => {\n if (open) {\n setSearchResults([]);\n void handleSearch('');\n }\n },\n options,\n placeholder: placeholder ?? '搜尋姓名或信箱',\n readOnly: readonly,\n searchDebounceTime: 300,\n size,\n } as const;\n\n return multiple ? (\n <AutoComplete\n {...commonProps}\n mode=\"multiple\"\n onChange={(nextOptions): void =>\n onChange(readMemberFieldValue(nextOptions, true))\n }\n value={[...selected]}\n />\n ) : (\n <AutoComplete\n {...commonProps}\n mode=\"single\"\n onChange={(nextOption): void =>\n onChange(readMemberFieldValue(nextOption ? [nextOption] : [], false))\n }\n value={selected[0] ?? null}\n />\n );\n}\n","import type { FormFieldValue } from '@rytass/bpm-core-shared/form';\nimport type { OrgUnitOptionRecord } from '@rytass/bpm-core-client/organization';\n\n/**\n * One pickable organization unit, shaped for Mezzanine `AutoComplete` (`id` +\n * `name`). `unresolved` marks a placeholder written while the stored id has no\n * name yet (the id itself is shown) or when the server cannot give one\n * (「未知組織」), so a real name arriving later replaces the placeholder.\n */\nexport type OrgUnitFieldOption = Readonly<{\n id: string;\n name: string;\n unresolved?: boolean;\n}>;\n\nexport const UNKNOWN_ORG_UNIT_NAME = '未知組織';\nexport const DELETED_ORG_UNIT_SUFFIX = '(已刪除)';\n\nexport function readOrgUnitFieldOption(\n record: OrgUnitOptionRecord,\n): OrgUnitFieldOption {\n return {\n id: record.id,\n name: record.deleted\n ? `${record.name}${DELETED_ORG_UNIT_SUFFIX}`\n : record.name,\n };\n}\n\n/** The id stands in for the name until the lookup comes back. */\nexport function readPendingOrgUnitFieldOption(\n orgUnitId: string,\n): OrgUnitFieldOption {\n return { id: orgUnitId, name: orgUnitId, unresolved: true };\n}\n\nexport function readUnknownOrgUnitFieldOption(\n orgUnitId: string,\n): OrgUnitFieldOption {\n return { id: orgUnitId, name: UNKNOWN_ORG_UNIT_NAME, unresolved: true };\n}\n\nfunction readStoredOrgUnitId(value: FormFieldValue | undefined): string | null {\n return typeof value === 'string' && value.trim() ? value : null;\n}\n\n// The server lowercases the ids it is given and answers with the lowercase\n// form, so a stored id is matched without regard to case.\nfunction readOrgUnitIdKey(orgUnitId: string): string {\n return orgUnitId.toLowerCase();\n}\n\nfunction isSameOrgUnitId(left: string, right: string): boolean {\n return readOrgUnitIdKey(left) === readOrgUnitIdKey(right);\n}\n\n/**\n * The stored value as an option. An id nobody has named yet shows as itself;\n * one the server could not name shows as 「未知組織」 (it is in `knownOptions` as\n * a placeholder), so the filler can still see and replace it.\n */\nexport function readSelectedOrgUnitFieldOption(\n value: FormFieldValue | undefined,\n knownOptions: readonly OrgUnitFieldOption[],\n): OrgUnitFieldOption | null {\n const orgUnitId = readStoredOrgUnitId(value);\n\n if (orgUnitId === null) {\n return null;\n }\n\n return (\n knownOptions.find((option) => isSameOrgUnitId(option.id, orgUnitId)) ??\n readPendingOrgUnitFieldOption(orgUnitId)\n );\n}\n\n/**\n * The stored id that no known option covers yet, or `null`. An id the server\n * could not name is covered by its 「未知組織」 placeholder, so it is not asked\n * for again.\n */\nexport function readUnresolvedOrgUnitId(\n value: FormFieldValue | undefined,\n knownOptions: readonly OrgUnitFieldOption[],\n): string | null {\n const orgUnitId = readStoredOrgUnitId(value);\n\n return orgUnitId !== null &&\n !knownOptions.some((option) => isSameOrgUnitId(option.id, orgUnitId))\n ? orgUnitId\n : null;\n}\n\n/**\n * Adds `nextOptions` to `currentOptions`. A named option replaces an unresolved\n * placeholder with the same id, compared without regard to case like every\n * other id match here; otherwise the first one seen wins. Returns\n * `currentOptions` itself when nothing changed, so state setters can bail out\n * of a re-render.\n */\nexport function mergeOrgUnitFieldOptions(\n currentOptions: readonly OrgUnitFieldOption[],\n nextOptions: readonly OrgUnitFieldOption[],\n): readonly OrgUnitFieldOption[] {\n const namedById = new Map(\n nextOptions\n .filter((option) => !option.unresolved)\n .map((option) => [readOrgUnitIdKey(option.id), option] as const),\n );\n const currentIds = new Set(\n currentOptions.map((option) => readOrgUnitIdKey(option.id)),\n );\n const addedOptions = nextOptions.filter(\n (option, index) =>\n !currentIds.has(readOrgUnitIdKey(option.id)) &&\n nextOptions.findIndex((candidate) =>\n isSameOrgUnitId(candidate.id, option.id),\n ) === index,\n );\n const upgradedOptions = currentOptions.map(\n (option) =>\n (option.unresolved\n ? namedById.get(readOrgUnitIdKey(option.id))\n : undefined) ?? option,\n );\n const changed =\n addedOptions.length > 0 ||\n upgradedOptions.some((option, index) => option !== currentOptions[index]);\n\n return changed ? [...upgradedOptions, ...addedOptions] : currentOptions;\n}\n\n/**\n * What a picker change writes back. Nothing picked is `undefined`, the same as\n * every other optional field.\n */\nexport function readOrgUnitFieldValue(\n selected: OrgUnitFieldOption | null,\n): FormFieldValue | undefined {\n return selected ? selected.id : undefined;\n}\n","'use client';\n\nimport { ReactElement, useEffect, useRef, useState } from 'react';\nimport { AutoComplete, Typography } from '@mezzanine-ui/react';\nimport {\n resolveOrgUnitOptions,\n searchOrgUnitOptions,\n} from '@rytass/bpm-core-client/organization';\nimport type { FormFieldValue } from '@rytass/bpm-core-shared/form';\nimport {\n mergeOrgUnitFieldOptions,\n readOrgUnitFieldOption,\n readOrgUnitFieldValue,\n readSelectedOrgUnitFieldOption,\n readUnknownOrgUnitFieldOption,\n readUnresolvedOrgUnitId,\n type OrgUnitFieldOption,\n} from './org-unit-field-options';\n\nexport interface OrgUnitFieldControlProps {\n readonly name: string;\n readonly onChange: (value: FormFieldValue | undefined) => void;\n readonly placeholder?: string;\n readonly readonly: boolean;\n readonly size?: 'main' | 'sub';\n readonly value: FormFieldValue | undefined;\n}\n\nexport const ORG_UNIT_SEARCH_FAILED_TEXT = '搜尋組織失敗,請稍後再試。';\n\n/**\n * 組織選擇 picker. The stored value is one organization unit id. Searching and\n * name lookup use the picker queries open to any signed-in member\n * (`searchOrgUnitOptions` / `resolveOrgUnitOptions`), not the designer-only\n * organization directory.\n *\n * A stored id shows as itself until its name comes back, then as the name\n * (「(已刪除)」 after it for a retired unit). An id the server cannot name\n * — unknown, or the lookup failed — shows as 「未知組織」 and is not asked for\n * again unless a later search returns the unit.\n */\nexport function OrgUnitFieldControl({\n name,\n onChange,\n placeholder,\n readonly,\n size,\n value,\n}: OrgUnitFieldControlProps): ReactElement {\n const [knownOptions, setKnownOptions] = useState<\n readonly OrgUnitFieldOption[]\n >([]);\n const [searchResults, setSearchResults] = useState<\n readonly OrgUnitFieldOption[]\n >([]);\n const [loading, setLoading] = useState(false);\n const [searchFailed, setSearchFailed] = useState(false);\n const latestSearchRef = useRef(0);\n const unresolvedId = readUnresolvedOrgUnitId(value, knownOptions);\n\n useEffect((): (() => void) => {\n let cancelled = false;\n\n if (unresolvedId === null) {\n return (): void => undefined;\n }\n\n void resolveOrgUnitOptions([unresolvedId])\n .then((records): readonly OrgUnitFieldOption[] => {\n const resolved = records.map(readOrgUnitFieldOption);\n\n // The id came back without a unit: it stays 「未知組織」 and is not\n // asked for again.\n return resolved.some(\n (option) => option.id.toLowerCase() === unresolvedId.toLowerCase(),\n )\n ? resolved\n : [...resolved, readUnknownOrgUnitFieldOption(unresolvedId)];\n })\n .catch((): readonly OrgUnitFieldOption[] => [\n readUnknownOrgUnitFieldOption(unresolvedId),\n ])\n .then((options): void => {\n if (!cancelled) {\n setKnownOptions((current) =>\n mergeOrgUnitFieldOptions(current, options),\n );\n }\n });\n\n return (): void => {\n cancelled = true;\n };\n }, [unresolvedId]);\n\n async function handleSearch(searchText: string): Promise<void> {\n const requestId = latestSearchRef.current + 1;\n\n latestSearchRef.current = requestId;\n setLoading(true);\n\n try {\n const options = (await searchOrgUnitOptions(searchText)).map(\n readOrgUnitFieldOption,\n );\n\n if (latestSearchRef.current === requestId) {\n setSearchResults(options);\n setSearchFailed(false);\n setKnownOptions((current) =>\n mergeOrgUnitFieldOptions(current, options),\n );\n }\n } catch {\n // The picked unit is untouched: only the list of hits is emptied.\n if (latestSearchRef.current === requestId) {\n setSearchResults([]);\n setSearchFailed(true);\n }\n } finally {\n if (latestSearchRef.current === requestId) {\n setLoading(false);\n }\n }\n }\n\n const selected = readSelectedOrgUnitFieldOption(value, knownOptions);\n // The picked unit stays in the option list so it can be un-ticked from the\n // dropdown, not only through its tag.\n const options = [\n ...mergeOrgUnitFieldOptions(selected ? [selected] : [], searchResults),\n ];\n\n return (\n <div>\n <AutoComplete\n asyncData\n clearSearchText\n disabled={readonly}\n disabledOptionsFilter\n emptyText=\"沒有符合的組織\"\n globalPortal\n inputProps={{\n autoCapitalize: 'none',\n autoCorrect: 'off',\n name,\n spellCheck: false,\n }}\n loading={loading}\n loadingText=\"搜尋組織中...\"\n mode=\"single\"\n onChange={(nextOption): void =>\n onChange(readOrgUnitFieldValue(nextOption ?? null))\n }\n onSearch={handleSearch}\n onVisibilityChange={(open: boolean): void => {\n if (open) {\n setSearchResults([]);\n void handleSearch('');\n }\n }}\n options={options}\n placeholder={placeholder ?? '搜尋組織名稱'}\n readOnly={readonly}\n searchDebounceTime={300}\n size={size}\n value={selected}\n />\n {searchFailed ? (\n <Typography color=\"text-error\" variant=\"caption\">\n {ORG_UNIT_SEARCH_FAILED_TEXT}\n </Typography>\n ) : null}\n </div>\n );\n}\n","'use client';\n\nimport {\n ChangeEvent,\n CSSProperties,\n ReactElement,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n AutoComplete,\n Button,\n CheckboxGroup,\n DatePicker,\n DateTimePicker,\n Input,\n RadioGroup,\n Select,\n Table,\n Textarea,\n Toggle,\n Typography,\n Upload,\n} from '@mezzanine-ui/react';\nimport type { UploadFile } from '@mezzanine-ui/react/Upload';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { PlusIcon, TrashIcon } from '@mezzanine-ui/icons';\nimport {\n FormDataSourceOptionFieldDefinition,\n FormDataSourceValueSnapshots,\n FormDefinitionSchema,\n FormFieldDefinition,\n FormFieldValue,\n FormTableRowValue,\n FormUiSchema,\n TableColumnDefinition,\n TableFieldDefinition,\n isFormDataSourceFieldDefinition,\n isFormStaticOptionFieldDefinition,\n isFormTableCellValue,\n isTableFieldDefinition,\n readFormFieldSelectionMode,\n} from '@rytass/bpm-core-shared/form';\nimport {\n buildFormRendererValues,\n clampOptionalNumber,\n createFormTableRow,\n formatDatePickerValue,\n formatDateTimePickerValue,\n FormRendererValues,\n isFormRendererFieldReadonly,\n isFormRendererFieldRequired,\n parseOptionalNumberInput,\n readDatePickerValue,\n readFieldOptionAsSelectOption,\n readFormTableCellPath,\n readFormTableRowBounds,\n readFormTableRows,\n readSelectedFormDataSourceOptions,\n readSelectOption,\n readVisibleFormRendererFields,\n} from '@rytass/bpm-core-client/form';\nimport type { ResolvedFieldAccess } from '@rytass/bpm-core-shared';\nimport { BPMFormField } from '../../../components/bpm-form-field';\nimport {\n FormDataSourceFieldState,\n FormRendererDataSourceContext,\n readFormDataSourceFieldStatusMessage,\n useFormDataSourceField,\n} from './form-data-source-field';\nimport { resolveFormFieldLink } from './form-field-link';\nimport { MemberFieldControl } from './member-field';\nimport { OrgUnitFieldControl } from './org-unit-field';\n\nexport interface FormRendererProps {\n readonly dataSourceContext?: FormRendererDataSourceContext;\n readonly dataSourceInitialValues?: FormRendererValues;\n readonly emptyText?: string;\n readonly errors?: Readonly<Record<string, string>>;\n /**\n * Per top-level field, what this viewer may see and edit (ADR 20 §3.3,\n * `resolveFieldAccess`). A field with `visible: false` is not rendered; one\n * with `editable: true` stays editable inside a `readonly` form, and one\n * with `editable: false` is read-only in an editable form. The form's own\n * rules (`hidden`, `visibleWhen`, `readonlyWhen`) still apply. A field the\n * map leaves out follows `readonly`; leaving the prop out changes nothing.\n */\n readonly fieldAccess?: Readonly<Record<string, ResolvedFieldAccess>>;\n /**\n * Short text shown after a field's label (Mezzanine `labelOptionalMarker`),\n * per top-level field — e.g. 「本關補填」 on the fields an approver fills.\n */\n readonly fieldLabelMarkers?: Readonly<Record<string, string>>;\n readonly maxWidth?: CSSProperties['maxWidth'];\n readonly onChange?: (values: FormRendererValues) => void;\n readonly onDataSourceStateChange?: (\n fieldKey: string,\n state: Pick<\n FormDataSourceFieldState,\n 'hasValue' | 'invalidValues' | 'status'\n >,\n ) => void;\n readonly onUploadAttachment?: (\n field: FormFieldDefinition,\n file: File,\n ) => Promise<{ readonly id: string }>;\n readonly optionSnapshots?: FormDataSourceValueSnapshots;\n readonly readonly?: boolean;\n readonly schema: FormDefinitionSchema;\n readonly singleColumn?: boolean;\n readonly uiSchema: FormUiSchema;\n readonly value?: FormRendererValues;\n}\n\n/**\n * Controls inside a table cell take the table's own `sub` size, so a row is\n * sized by its cells rather than by whichever control kept the default.\n * `DatePicker` and `DateTimePicker` accept no size, so a date column stays at\n * the default height — a Mezzanine limitation, not a choice.\n */\ntype ControlSize = 'main' | 'sub';\n\nconst FORM_RENDERER_GRID_STYLE: CSSProperties = {\n alignItems: 'start',\n display: 'grid',\n gap: 12,\n gridTemplateColumns: 'repeat(12, minmax(0, 1fr))',\n};\n\nconst FORM_RENDERER_EMPTY_STYLE: CSSProperties = {\n gridColumn: '1 / -1',\n};\n\nconst FORM_RENDERER_FIELD_STYLE: CSSProperties = {\n alignItems: 'start',\n display: 'grid',\n gap: 8,\n minWidth: 0,\n};\n\nconst TEXTAREA_STYLE: CSSProperties = {\n minWidth: '100%',\n width: '100%',\n};\n\nfunction applyFullWidthTextareaHost(element: HTMLDivElement | null): void {\n if (!element) {\n return;\n }\n\n element.style.width = '100%';\n}\n\nexport function FormRenderer({\n dataSourceContext,\n dataSourceInitialValues,\n emptyText = '尚未建立欄位。',\n errors = {},\n fieldAccess,\n fieldLabelMarkers,\n maxWidth,\n onChange,\n onDataSourceStateChange,\n onUploadAttachment,\n optionSnapshots,\n readonly = false,\n schema,\n singleColumn = false,\n uiSchema,\n value,\n}: FormRendererProps): ReactElement {\n const [internalValues, setInternalValues] = useState<FormRendererValues>(() =>\n buildFormRendererValues(schema.fields, value ?? {}),\n );\n const values = useMemo(\n (): FormRendererValues =>\n buildFormRendererValues(schema.fields, value ?? internalValues),\n [internalValues, schema.fields, value],\n );\n const valuesRef = useRef<FormRendererValues>(values);\n const visibleFields = useMemo(\n (): readonly FormFieldDefinition[] =>\n readVisibleFormRendererFields(schema, uiSchema, values).filter(\n (field) =>\n readOwnFieldAccess(fieldAccess, field.fieldKey)?.visible !== false,\n ),\n [fieldAccess, schema, uiSchema, values],\n );\n\n useEffect((): void => {\n valuesRef.current = values;\n }, [values]);\n\n useEffect((): void => {\n setInternalValues((currentValues) =>\n buildFormRendererValues(schema.fields, value ?? currentValues),\n );\n }, [schema.fields, value]);\n\n function updateValue(\n fieldKey: string,\n nextValue: FormFieldValue | undefined,\n ): void {\n const nextValues = {\n ...valuesRef.current,\n [fieldKey]: nextValue,\n };\n valuesRef.current = nextValues;\n\n if (!value) {\n setInternalValues(nextValues);\n }\n\n onChange?.(nextValues);\n }\n\n if (!visibleFields.length) {\n return (\n <div style={FORM_RENDERER_GRID_STYLE}>\n <Typography\n color=\"text-neutral\"\n style={FORM_RENDERER_EMPTY_STYLE}\n variant=\"body\"\n >\n {schema.fields.length > 0 ? '目前條件下沒有可填寫欄位。' : emptyText}\n </Typography>\n </div>\n );\n }\n\n return (\n <div\n style={{\n ...FORM_RENDERER_GRID_STYLE,\n // `maxWidth` is applied per field rather than to the grid, so a table\n // field can use the full content width while every other field keeps\n // the single-column reading width (docs/17 P4).\n ...(singleColumn ? { width: '100%' } : {}),\n }}\n >\n {visibleFields.map((field) => (\n <FormRendererField\n dataSourceContext={dataSourceContext}\n dataSourceInitialValues={dataSourceInitialValues}\n field={field}\n error={errors[field.fieldKey] ?? null}\n errors={errors}\n fields={schema.fields}\n key={field.fieldKey}\n labelMarker={\n fieldLabelMarkers !== undefined &&\n Object.prototype.hasOwnProperty.call(\n fieldLabelMarkers,\n field.fieldKey,\n )\n ? fieldLabelMarkers[field.fieldKey]\n : undefined\n }\n onChange={updateValue}\n onDataSourceStateChange={onDataSourceStateChange}\n onUploadAttachment={onUploadAttachment}\n optionSnapshots={optionSnapshots}\n readonly={readFieldAccessReadonly(\n readOwnFieldAccess(fieldAccess, field.fieldKey),\n readonly,\n )}\n schema={schema}\n style={{\n ...FORM_RENDERER_FIELD_STYLE,\n gridColumn: `span ${\n singleColumn ? 12 : readFieldColumnSpan(field, uiSchema)\n }`,\n ...(singleColumn && !isTableFieldDefinition(field)\n ? { maxWidth }\n : {}),\n }}\n value={values[field.fieldKey]}\n values={values}\n uiSchema={uiSchema}\n />\n ))}\n </div>\n );\n}\n\n/** Own-property read: a field key like `constructor` must not hit the prototype. */\nfunction readOwnFieldAccess(\n fieldAccess: Readonly<Record<string, ResolvedFieldAccess>> | undefined,\n fieldKey: string,\n): ResolvedFieldAccess | undefined {\n return fieldAccess !== undefined &&\n Object.prototype.hasOwnProperty.call(fieldAccess, fieldKey)\n ? fieldAccess[fieldKey]\n : undefined;\n}\n\nfunction readFieldAccessReadonly(\n access: ResolvedFieldAccess | undefined,\n formReadonly: boolean,\n): boolean {\n return access === undefined ? formReadonly : !access.editable;\n}\n\nfunction FormRendererField({\n dataSourceContext,\n dataSourceInitialValues,\n error,\n errors,\n field,\n fields,\n labelMarker,\n onChange,\n onDataSourceStateChange,\n onUploadAttachment,\n optionSnapshots,\n readonly,\n schema,\n style,\n value,\n values,\n uiSchema,\n}: {\n readonly dataSourceContext?: FormRendererDataSourceContext;\n readonly dataSourceInitialValues?: FormRendererValues;\n readonly error: string | null;\n /**\n * The whole error map, not just this field's line: a table addresses its\n * cells by instance path, so it needs the keys of its own rows too\n * (ADR 16 §3.3).\n */\n readonly errors: Readonly<Record<string, string>>;\n readonly field: FormFieldDefinition;\n readonly fields: readonly FormFieldDefinition[];\n readonly labelMarker?: string;\n readonly onChange: (\n fieldKey: string,\n value: FormFieldValue | undefined,\n ) => void;\n readonly onDataSourceStateChange?: (\n fieldKey: string,\n state: Pick<\n FormDataSourceFieldState,\n 'hasValue' | 'invalidValues' | 'status'\n >,\n ) => void;\n readonly onUploadAttachment?:\n | ((\n field: FormFieldDefinition,\n file: File,\n ) => Promise<{ readonly id: string }>)\n | undefined;\n readonly optionSnapshots?: FormDataSourceValueSnapshots;\n readonly readonly: boolean;\n readonly schema: FormDefinitionSchema;\n readonly style: CSSProperties;\n readonly uiSchema: FormUiSchema;\n readonly value: FormFieldValue | undefined;\n readonly values: FormRendererValues;\n}): ReactElement {\n const required = isFormRendererFieldRequired(field, fields, values);\n const fieldReadonly =\n readonly || isFormRendererFieldReadonly(field, fields, values);\n const dataSourceState = useFormDataSourceField({\n context: dataSourceContext,\n field,\n formData: values,\n initialFormData: dataSourceInitialValues,\n initialValue: dataSourceInitialValues?.[field.fieldKey],\n optionSnapshots,\n readonly: fieldReadonly,\n schema,\n uiSchema,\n });\n const dataSourceStatusMessage = readFormDataSourceFieldStatusMessage(\n dataSourceState,\n );\n const dataSourceStateSignature = JSON.stringify({\n hasValue: dataSourceState.hasValue,\n invalidValues: dataSourceState.invalidValues,\n status: dataSourceState.status,\n });\n\n useEffect((): void => {\n if (!isFormDataSourceFieldDefinition(field)) {\n return;\n }\n\n onDataSourceStateChange?.(field.fieldKey, {\n hasValue: dataSourceState.hasValue,\n invalidValues: dataSourceState.invalidValues,\n status: dataSourceState.status,\n });\n }, [\n dataSourceStateSignature,\n field.fieldKey,\n onDataSourceStateChange,\n ]);\n\n return (\n <div data-form-field-key={field.fieldKey} style={style}>\n <BPMFormField\n label={field.label}\n {...(labelMarker ? { labelOptionalMarker: labelMarker } : {})}\n name={field.fieldKey}\n required={required}\n >\n {isTableFieldDefinition(field) ? (\n <FormTableField\n dataSourceContext={dataSourceContext}\n dataSourceInitialValues={dataSourceInitialValues}\n errors={errors}\n field={field}\n onChange={onChange}\n onDataSourceStateChange={onDataSourceStateChange}\n optionSnapshots={optionSnapshots}\n readonly={fieldReadonly}\n schema={schema}\n uiSchema={uiSchema}\n value={value}\n values={values}\n />\n ) : (\n renderControl(\n field,\n value,\n fieldReadonly,\n onChange,\n onUploadAttachment,\n dataSourceContext,\n dataSourceState,\n )\n )}\n </BPMFormField>\n {error ? (\n <Typography color=\"text-error\" variant=\"caption\">\n {error}\n </Typography>\n ) : null}\n {dataSourceStatusMessage ? (\n <div style={DATA_SOURCE_FEEDBACK_STYLE}>\n <Typography\n color={\n dataSourceState.status === 'INVALID' ||\n dataSourceState.status === 'UNAVAILABLE'\n ? 'text-error'\n : 'text-neutral'\n }\n variant=\"caption\"\n >\n {dataSourceStatusMessage}\n </Typography>\n {dataSourceState.status === 'UNAVAILABLE' && dataSourceState.canRetry ? (\n <Button\n onClick={dataSourceState.retry}\n size=\"sub\"\n type=\"button\"\n variant=\"base-ghost\"\n >\n 重試\n </Button>\n ) : null}\n </div>\n ) : null}\n </div>\n );\n}\n\nconst EXTERNAL_LINK_CUE = '(另開新分頁)';\nconst EXTERNAL_LINK_TITLE = '另開新分頁';\n\nconst LINK_STYLE: CSSProperties = {\n color: 'inherit',\n textDecoration: 'underline',\n wordBreak: 'break-all',\n};\n\nconst DATA_SOURCE_FEEDBACK_STYLE: CSSProperties = {\n alignItems: 'center',\n display: 'flex',\n gap: 8,\n};\n\nconst TABLE_FIELD_STYLE: CSSProperties = {\n display: 'grid',\n gap: 8,\n minWidth: 0,\n width: '100%',\n};\n\nconst TABLE_FIELD_ACTIONS_STYLE: CSSProperties = {\n display: 'flex',\n justifyContent: 'flex-start',\n};\n\nconst TABLE_SCROLLER_STYLE: CSSProperties = {\n maxWidth: '100%',\n overflowX: 'auto',\n};\n\n// Mezzanine `Table` fills whatever box it is given, so an `overflow-x: auto`\n// wrapper on its own never scrolls: the columns just keep shrinking. Keeping\n// the table out of the single-column reading width is what stops a control\n// from overflowing its own cell; this floor is the defence for tables wide\n// enough that even the full width would squeeze them, and it is what makes the\n// wrapper actually scroll when that happens.\nconst TABLE_COLUMN_MIN_WIDTH = 160;\nconst TABLE_ACTIONS_COLUMN_WIDTH = 56;\n\nconst TABLE_CELL_STYLE: CSSProperties = {\n display: 'grid',\n gap: 4,\n minWidth: 0,\n};\n\ntype FormTableRowRecord = Readonly<\n Record<string, unknown> & {\n index: number;\n key: string;\n }\n>;\n\n/**\n * Rows carry no identity in `formData` — array order is display order\n * (ADR 16 §3.2) — so the React key and, from P3, the per-cell DataSource state\n * key come from an ephemeral id minted here and never written to the value.\n */\nfunction FormTableField({\n dataSourceContext,\n dataSourceInitialValues,\n errors,\n field,\n onChange,\n onDataSourceStateChange,\n optionSnapshots,\n readonly,\n schema,\n uiSchema,\n value,\n values,\n}: {\n readonly dataSourceContext?: FormRendererDataSourceContext;\n readonly dataSourceInitialValues?: FormRendererValues;\n readonly errors: Readonly<Record<string, string>>;\n readonly field: TableFieldDefinition;\n readonly onChange: (\n fieldKey: string,\n value: FormFieldValue | undefined,\n ) => void;\n readonly onDataSourceStateChange?: (\n fieldKey: string,\n state: Pick<\n FormDataSourceFieldState,\n 'hasValue' | 'invalidValues' | 'status'\n >,\n ) => void;\n readonly optionSnapshots?: FormDataSourceValueSnapshots;\n readonly readonly: boolean;\n readonly schema: FormDefinitionSchema;\n readonly uiSchema: FormUiSchema;\n readonly value: FormFieldValue | undefined;\n readonly values: FormRendererValues;\n}): ReactElement {\n const rows = readFormTableRows(value);\n const bounds = readFormTableRowBounds(field);\n const rowIdSequenceRef = useRef(0);\n const [rowIds, setRowIds] = useState<readonly string[]>(() =>\n rows.map((): string => createRowId(rowIdSequenceRef)),\n );\n\n // The value can also change from outside (a parent reset, a returned case\n // loading its saved rows). Re-mint ids only then, so ordinary edits keep the\n // per-row identity stable.\n useEffect((): void => {\n if (rowIds.length === rows.length) {\n return;\n }\n\n setRowIds(rows.map((): string => createRowId(rowIdSequenceRef)));\n }, [rowIds.length, rows.length]);\n\n function commitRows(nextRows: readonly FormTableRowValue[]): void {\n onChange(field.fieldKey, nextRows);\n }\n\n function handleAddRow(): void {\n setRowIds([...rowIds, createRowId(rowIdSequenceRef)]);\n commitRows([...rows, createFormTableRow(field.columns)]);\n }\n\n function handleRemoveRow(rowIndex: number): void {\n setRowIds(rowIds.filter((_, index) => index !== rowIndex));\n commitRows(rows.filter((_, index) => index !== rowIndex));\n }\n\n function handleCellChange(\n rowIndex: number,\n columnKey: string,\n cellValue: FormFieldValue | undefined,\n ): void {\n commitRows(\n rows.map((row, index) =>\n index === rowIndex\n ? readNextTableRow(row, columnKey, cellValue)\n : row,\n ),\n );\n }\n\n const dataSource: readonly FormTableRowRecord[] = rows.map((row, index) => ({\n ...row,\n index,\n key: rowIds[index] ?? `row-${index}`,\n }));\n const columns: TableColumn<FormTableRowRecord>[] = field.columns.map(\n (column) => ({\n key: column.fieldKey,\n render: (row): ReactElement => (\n <FormTableCell\n column={column}\n dataSourceContext={dataSourceContext}\n error={\n errors[\n readFormTableCellPath(field.fieldKey, row.index, column.fieldKey)\n ] ?? null\n }\n fieldPath={`${field.fieldKey}.${column.fieldKey}`}\n formData={values}\n initialFormData={dataSourceInitialValues}\n initialRowValues={readInitialTableRow(\n dataSourceInitialValues?.[field.fieldKey],\n row.index,\n )}\n // Keyed by the ephemeral row id, so a cell's DataSource state follows\n // its row rather than its position (ADR 16 §3.9).\n key={`${rowIds[row.index] ?? row.index}-${column.fieldKey}`}\n onChange={(cellValue): void =>\n handleCellChange(row.index, column.fieldKey, cellValue)\n }\n onDataSourceStateChange={onDataSourceStateChange}\n optionSnapshots={optionSnapshots}\n path={readFormTableCellPath(\n field.fieldKey,\n row.index,\n column.fieldKey,\n )}\n readonly={readonly}\n rowValues={rows[row.index] ?? {}}\n schema={schema}\n uiSchema={uiSchema}\n />\n ),\n title: column.label || column.fieldKey,\n }),\n );\n const actions: TableActions<FormTableRowRecord> | undefined = readonly\n ? undefined\n : {\n render: (row): ReturnType<TableActions<FormTableRowRecord>['render']> => [\n {\n disabled: (): boolean => rows.length <= bounds.minRows,\n icon: TrashIcon,\n iconType: 'icon-only',\n name: '刪除此列',\n onClick: (): void => handleRemoveRow(row.index),\n variant: 'destructive-ghost',\n },\n ],\n width: 56,\n };\n\n return (\n <div style={TABLE_FIELD_STYLE}>\n {/*\n Mezzanine `Table`'s `scroll` is vertical only, so a wide table is kept\n inside its own horizontal scroller rather than pushing the page sideways.\n The wrapper adds no styling of its own to the table (ADR 16 §3.9 assumed\n `scroll` covered this; see docs/17).\n */}\n <div style={TABLE_SCROLLER_STYLE}>\n <div\n style={{\n minWidth:\n field.columns.length * TABLE_COLUMN_MIN_WIDTH +\n (actions ? TABLE_ACTIONS_COLUMN_WIDTH : 0),\n }}\n >\n <Table\n {...(actions ? { actions } : {})}\n columns={columns}\n dataSource={[...dataSource]}\n // Cells carry form controls rather than text, so the row height\n // comes from the roomy container token, not the compact default.\n rowHeightPreset=\"roomy\"\n showHeader\n size=\"sub\"\n />\n </div>\n </div>\n {readonly ? null : (\n <div style={TABLE_FIELD_ACTIONS_STYLE}>\n <Button\n disabled={rows.length >= bounds.maxRows}\n icon={PlusIcon}\n iconType=\"leading\"\n onClick={handleAddRow}\n size=\"sub\"\n type=\"button\"\n variant=\"base-secondary\"\n >\n {field.addRowLabel || '新增一列'}\n </Button>\n </div>\n )}\n </div>\n );\n}\n\nfunction FormTableCell({\n column,\n dataSourceContext,\n error,\n fieldPath,\n formData,\n initialFormData,\n initialRowValues,\n onChange,\n onDataSourceStateChange,\n optionSnapshots,\n path,\n readonly,\n rowValues,\n schema,\n uiSchema,\n}: {\n readonly column: TableColumnDefinition;\n readonly dataSourceContext?: FormRendererDataSourceContext;\n readonly error: string | null;\n readonly fieldPath: string;\n readonly formData: FormRendererValues;\n readonly initialFormData?: FormRendererValues;\n readonly initialRowValues?: FormTableRowValue;\n readonly onChange: (value: FormFieldValue | undefined) => void;\n readonly onDataSourceStateChange?: (\n fieldKey: string,\n state: Pick<\n FormDataSourceFieldState,\n 'hasValue' | 'invalidValues' | 'status'\n >,\n ) => void;\n readonly optionSnapshots?: FormDataSourceValueSnapshots;\n readonly path: string;\n readonly readonly: boolean;\n readonly rowValues: FormTableRowValue;\n readonly schema: FormDefinitionSchema;\n readonly uiSchema: FormUiSchema;\n}): ReactElement {\n const value = readTableCellValue(rowValues, column.fieldKey);\n const dataSourceState = useFormDataSourceField({\n context: dataSourceContext,\n field: column,\n fieldPath,\n formData,\n initialFormData,\n initialValue: initialRowValues\n ? readTableCellValue(initialRowValues, column.fieldKey)\n : undefined,\n optionSnapshots,\n readonly,\n row: {\n initialValues: initialRowValues,\n values: rowValues,\n },\n schema,\n snapshotKey: path,\n uiSchema,\n });\n const statusMessage = readFormDataSourceFieldStatusMessage(dataSourceState);\n const stateSignature = JSON.stringify({\n hasValue: dataSourceState.hasValue,\n invalidValues: dataSourceState.invalidValues,\n status: dataSourceState.status,\n });\n\n useEffect((): (() => void) | void => {\n if (!isFormDataSourceFieldDefinition(column)) {\n return;\n }\n\n onDataSourceStateChange?.(path, {\n hasValue: dataSourceState.hasValue,\n invalidValues: dataSourceState.invalidValues,\n status: dataSourceState.status,\n });\n\n // A deleted row must stop blocking the submit it can no longer be fixed in.\n return (): void => {\n onDataSourceStateChange?.(path, {\n hasValue: false,\n invalidValues: [],\n status: 'IDLE',\n });\n };\n }, [onDataSourceStateChange, path, stateSignature]);\n\n return (\n <div data-form-field-key={path} style={TABLE_CELL_STYLE}>\n {renderControl(\n column,\n value,\n readonly,\n (_columnKey, nextValue): void => onChange(nextValue),\n undefined,\n dataSourceContext,\n dataSourceState,\n 'sub',\n )}\n {error ? (\n <Typography color=\"text-error\" variant=\"caption\">\n {error}\n </Typography>\n ) : null}\n {statusMessage ? (\n <div style={DATA_SOURCE_FEEDBACK_STYLE}>\n <Typography\n color={\n dataSourceState.status === 'INVALID' ||\n dataSourceState.status === 'UNAVAILABLE'\n ? 'text-error'\n : 'text-neutral'\n }\n variant=\"caption\"\n >\n {statusMessage}\n </Typography>\n {dataSourceState.status === 'UNAVAILABLE' &&\n dataSourceState.canRetry ? (\n <Button\n onClick={dataSourceState.retry}\n size=\"sub\"\n type=\"button\"\n variant=\"base-ghost\"\n >\n 重試\n </Button>\n ) : null}\n </div>\n ) : null}\n </div>\n );\n}\n\n/**\n * The row as it was loaded, used to tell a returned case's stored values from\n * edits. Absent when the table itself is new.\n */\nfunction readInitialTableRow(\n value: FormFieldValue | undefined,\n rowIndex: number,\n): FormTableRowValue | undefined {\n return readFormTableRows(value)[rowIndex];\n}\n\nfunction createRowId(sequenceRef: { current: number }): string {\n sequenceRef.current += 1;\n\n return `table-row-${sequenceRef.current}`;\n}\n\n/**\n * Clearing a cell removes the key instead of storing `undefined`: the row is\n * serialised to JSON, where `undefined` would vanish anyway, and an absent key\n * is what an untouched cell looks like.\n */\nfunction readNextTableRow(\n row: FormTableRowValue,\n columnKey: string,\n value: FormFieldValue | undefined,\n): FormTableRowValue {\n if (typeof value === 'undefined' || !isFormTableCellValue(value)) {\n return Object.fromEntries(\n Object.entries(row).filter(([key]) => key !== columnKey),\n );\n }\n\n return { ...row, [columnKey]: value };\n}\n\nfunction readTableCellValue(\n row: FormTableRowValue | undefined,\n columnKey: string,\n): FormFieldValue | undefined {\n if (!row || !Object.prototype.hasOwnProperty.call(row, columnKey)) {\n return undefined;\n }\n\n return row[columnKey];\n}\n\nfunction renderControl(\n field: FormFieldDefinition,\n value: FormFieldValue | undefined,\n readonly: boolean,\n onChange: (fieldKey: string, value: FormFieldValue | undefined) => void,\n onUploadAttachment:\n | ((\n field: FormFieldDefinition,\n file: File,\n ) => Promise<{ readonly id: string }>)\n | undefined,\n dataSourceContext: FormRendererDataSourceContext | undefined,\n dataSourceState: FormDataSourceFieldState,\n size: ControlSize = 'main',\n): ReactElement {\n if (isFormDataSourceFieldDefinition(field)) {\n return renderDynamicOptionControl(\n field,\n value,\n readonly,\n onChange,\n dataSourceContext,\n dataSourceState,\n size,\n );\n }\n\n return renderStaticControl(\n field,\n value,\n readonly,\n onChange,\n onUploadAttachment,\n size,\n );\n}\n\n/**\n * Every control that does not depend on a DataSource. Split out of\n * {@link renderControl} so a table cell can reuse the same controls without\n * carrying a field-level DataSource state it has no use for (ADR 16 §3.9).\n */\nfunction renderStaticControl(\n field: FormFieldDefinition,\n value: FormFieldValue | undefined,\n readonly: boolean,\n onChange: (fieldKey: string, value: FormFieldValue | undefined) => void,\n onUploadAttachment:\n | ((\n field: FormFieldDefinition,\n file: File,\n ) => Promise<{ readonly id: string }>)\n | undefined,\n size: ControlSize = 'main',\n): ReactElement {\n const link =\n readonly &&\n (field.type === 'text' || field.type === 'textarea') &&\n field.displayAs === 'link'\n ? resolveFormFieldLink(value)\n : null;\n\n // Only a safe href becomes an anchor; anything else falls through to the\n // ordinary read-only control, i.e. plain text.\n if (link) {\n return (\n // Typography types its props as a paragraph's, so it cannot take `href`;\n // it supplies the brand colour and the anchor inherits it.\n <Typography color=\"text-brand\" component=\"span\" variant=\"body\">\n <a\n href={link.href}\n style={LINK_STYLE}\n {...(link.external\n ? {\n // The cue lives in attributes, not in a child node, so\n // selecting or copying the link yields just the URL.\n 'aria-label': `${link.href}${EXTERNAL_LINK_CUE}`,\n rel: 'noopener noreferrer',\n target: '_blank',\n title: EXTERNAL_LINK_TITLE,\n }\n : {})}\n >\n {link.href}\n </a>\n </Typography>\n );\n }\n\n if (field.type === 'textarea') {\n return (\n <Textarea\n onChange={(event: ChangeEvent<HTMLTextAreaElement>): void =>\n onChange(field.fieldKey, event.target.value)\n }\n placeholder={field.placeholder ?? '請輸入多行文字'}\n readOnly={readonly}\n ref={applyFullWidthTextareaHost}\n rows={3}\n style={TEXTAREA_STYLE}\n value={readStringValue(value)}\n />\n );\n }\n\n if (field.type === 'boolean') {\n return (\n <Toggle\n checked={value === true}\n disabled={readonly}\n label=\"啟用\"\n onChange={(event: ChangeEvent<HTMLInputElement>): void =>\n onChange(field.fieldKey, event.target.checked)\n }\n size={size}\n />\n );\n }\n\n if (isFormStaticOptionFieldDefinition(field) && field.type === 'checkbox') {\n const options = field.options.map(readFieldOptionAsSelectOption);\n\n return (\n <Select\n mode=\"multiple\"\n onChange={(options): void =>\n onChange(\n field.fieldKey,\n options?.length\n ? options.map((option) => option.id)\n : undefined,\n )\n }\n options={options}\n placeholder={field.placeholder ?? '請選擇一或多個選項'}\n readOnly={readonly}\n size={size}\n value={options.filter((option) =>\n readStringArrayValue(value).includes(option.id),\n )}\n />\n );\n }\n\n if (isFormStaticOptionFieldDefinition(field)) {\n const options = field.options.map(readFieldOptionAsSelectOption);\n\n return (\n <Select\n onChange={(option): void =>\n onChange(field.fieldKey, option?.id ?? undefined)\n }\n options={options}\n placeholder={field.placeholder ?? '請選擇'}\n readOnly={readonly}\n size={size}\n value={readSelectOption(options, readStringValue(value))}\n />\n );\n }\n\n if (field.type === 'member') {\n return (\n <MemberFieldControl\n multiple={field.multiple === true}\n name={field.fieldKey}\n onChange={(nextValue): void => onChange(field.fieldKey, nextValue)}\n placeholder={field.placeholder}\n readonly={readonly}\n size={size}\n value={value}\n />\n );\n }\n\n if (field.type === 'org_unit') {\n return (\n <OrgUnitFieldControl\n name={field.fieldKey}\n onChange={(nextValue): void => onChange(field.fieldKey, nextValue)}\n placeholder={field.placeholder ?? readInputPlaceholder(field.type)}\n readonly={readonly}\n size={size}\n value={value}\n />\n );\n }\n\n if (field.type === 'file_upload') {\n return (\n <Upload\n accept={field.acceptedMimeTypes?.join(',')}\n disabled={readonly}\n maxFiles={field.maxFiles}\n mode=\"button-list\"\n {...((field.maxFiles ?? 1) > 1 ? { multiple: true } : {})}\n onChange={(files): void =>\n onChange(\n field.fieldKey,\n files.length ? files.map((file) => file.id) : undefined,\n )\n }\n onUpload={async (files): Promise<UploadFile[]> =>\n Promise.all(\n files.map(async (file): Promise<UploadFile> => {\n const uploadedFile = onUploadAttachment\n ? await onUploadAttachment(field, file)\n : { id: `${file.name}-${file.lastModified}` };\n\n return {\n file,\n id: uploadedFile.id,\n progress: 100,\n status: 'done',\n };\n }),\n )\n }\n size=\"sub\"\n />\n );\n }\n\n if (field.type === 'date') {\n return (\n <DatePicker\n format=\"YYYY-MM-DD\"\n fullWidth\n onChange={(nextValue): void =>\n onChange(field.fieldKey, formatDatePickerValue(nextValue))\n }\n placeholder={field.placeholder ?? readInputPlaceholder(field.type)}\n readOnly={readonly}\n value={readDatePickerValue(value)}\n />\n );\n }\n\n if (field.type === 'datetime') {\n return (\n <DateTimePicker\n formatDate=\"YYYY-MM-DD\"\n formatTime=\"HH:mm\"\n fullWidth\n hideSecond\n onChange={(nextValue): void =>\n onChange(field.fieldKey, formatDateTimePickerValue(nextValue))\n }\n placeholderLeft=\"選擇日期\"\n placeholderRight=\"選擇時間\"\n readOnly={readonly}\n value={readDatePickerValue(value)}\n />\n );\n }\n\n if (field.type === 'number' || field.type === 'money') {\n return (\n <Input\n fullWidth\n max={field.maximum}\n min={field.minimum}\n size={size}\n onChange={(event: ChangeEvent<HTMLInputElement>): void =>\n onChange(\n field.fieldKey,\n clampOptionalNumber(parseOptionalNumberInput(event.target.value), {\n max: field.maximum,\n min: field.minimum,\n }),\n )\n }\n placeholder={field.placeholder ?? readInputPlaceholder(field.type)}\n {...(readonly ? { readonly: true as const } : {})}\n showSpinner\n value={readNumberInputValue(value)}\n variant=\"measure\"\n />\n );\n }\n\n return (\n <Input\n fullWidth\n onChange={(event: ChangeEvent<HTMLInputElement>): void =>\n onChange(field.fieldKey, event.target.value)\n }\n placeholder={field.placeholder ?? readInputPlaceholder(field.type)}\n {...(readonly ? { readonly: true as const } : {})}\n size={size}\n value={readStringValue(value)}\n variant=\"base\"\n />\n );\n}\n\nfunction renderDynamicOptionControl(\n field: FormDataSourceOptionFieldDefinition,\n value: FormFieldValue | undefined,\n readonly: boolean,\n onChange: (fieldKey: string, value: FormFieldValue | undefined) => void,\n dataSourceContext: FormRendererDataSourceContext | undefined,\n dataSourceState: FormDataSourceFieldState,\n size: ControlSize = 'main',\n): ReactElement {\n const options = dataSourceState.options.map(readFieldOptionAsSelectOption);\n const selectedOptions = readSelectedFormDataSourceOptions(\n value,\n dataSourceState.options,\n ).map(readFieldOptionAsSelectOption);\n const loading = dataSourceState.status === 'LOADING';\n const waitingForDependencies =\n dataSourceState.status === 'WAITING_FOR_DEPENDENCIES';\n const interactiveSearch = Boolean(dataSourceContext) && !readonly;\n const optionControlDisabled = readonly || waitingForDependencies;\n // AutoComplete alone has no pre-query gate — it only learns a dependency is\n // missing from the answer to a search the filler already typed. Disabling it\n // at that point strands that text in a locked input with no way to clear it,\n // so it stays editable and reports the wait through the status line instead.\n const autoCompleteDisabled = readonly;\n\n if (field.type === 'autocomplete') {\n if (readFormFieldSelectionMode(field) === 'multiple') {\n return (\n <AutoComplete\n asyncData={interactiveSearch}\n clearSearchText\n disabled={autoCompleteDisabled}\n disabledOptionsFilter={interactiveSearch}\n globalPortal\n loading={loading}\n mode=\"multiple\"\n onChange={(nextOptions): void =>\n onChange(\n field.fieldKey,\n nextOptions.length\n ? nextOptions.map((option) => option.id)\n : undefined,\n )\n }\n onReachBottom={\n dataSourceState.hasNextPage\n ? dataSourceState.onReachBottom\n : undefined\n }\n onSearch={interactiveSearch ? dataSourceState.onSearch : undefined}\n options={options}\n placeholder={field.placeholder ?? '請輸入或選擇'}\n readOnly={readonly}\n searchDebounceTime={300}\n size={size}\n value={selectedOptions}\n />\n );\n }\n\n return (\n <AutoComplete\n asyncData={interactiveSearch}\n clearSearchText\n disabled={autoCompleteDisabled}\n disabledOptionsFilter={interactiveSearch}\n globalPortal\n loading={loading}\n mode=\"single\"\n onChange={(nextOption): void =>\n onChange(field.fieldKey, nextOption?.id ?? undefined)\n }\n onReachBottom={\n dataSourceState.hasNextPage\n ? dataSourceState.onReachBottom\n : undefined\n }\n onSearch={interactiveSearch ? dataSourceState.onSearch : undefined}\n options={options}\n placeholder={field.placeholder ?? '請輸入或選擇'}\n readOnly={readonly}\n searchDebounceTime={300}\n size={size}\n value={selectedOptions[0] ?? null}\n />\n );\n }\n\n if (field.type === 'radio') {\n return (\n <RadioGroup\n disabled={optionControlDisabled}\n name={field.fieldKey}\n onChange={(event): void =>\n onChange(field.fieldKey, event.target.value || undefined)\n }\n options={options}\n value={readStringValue(value)}\n />\n );\n }\n\n if (field.type === 'checkbox') {\n return (\n <CheckboxGroup\n disabled={optionControlDisabled}\n name={field.fieldKey}\n onChange={(event): void =>\n onChange(\n field.fieldKey,\n event.target.values.length ? event.target.values : undefined,\n )\n }\n options={options.map((option) => ({\n label: option.name,\n value: option.id,\n }))}\n value={[...readStringArrayValue(value)]}\n />\n );\n }\n\n if (readFormFieldSelectionMode(field) === 'multiple') {\n return (\n <Select\n disabled={optionControlDisabled}\n loading={loading}\n mode=\"multiple\"\n onChange={(nextOptions): void =>\n onChange(\n field.fieldKey,\n nextOptions.length\n ? nextOptions.map((option) => option.id)\n : undefined,\n )\n }\n onReachBottom={\n dataSourceState.hasNextPage\n ? dataSourceState.onReachBottom\n : undefined\n }\n options={options}\n placeholder={field.placeholder ?? '請選擇一或多個選項'}\n readOnly={readonly}\n size={size}\n value={selectedOptions}\n />\n );\n }\n\n return (\n <Select\n disabled={optionControlDisabled}\n loading={loading}\n onChange={(nextOption): void =>\n onChange(field.fieldKey, nextOption?.id ?? undefined)\n }\n onReachBottom={\n dataSourceState.hasNextPage\n ? dataSourceState.onReachBottom\n : undefined\n }\n options={options}\n placeholder={field.placeholder ?? '請選擇'}\n readOnly={readonly}\n size={size}\n value={selectedOptions[0] ?? null}\n />\n );\n}\n\nfunction readFieldColumnSpan(\n field: FormFieldDefinition,\n uiSchema: FormUiSchema,\n): number {\n const width =\n uiSchema.layout.find((item) => item.fieldKey === field.fieldKey)?.width ??\n 'FULL';\n const spans: Readonly<Record<typeof width, number>> = {\n FULL: 12,\n HALF: 6,\n THIRD: 4,\n };\n\n return spans[width];\n}\n\nfunction readStringValue(value: FormFieldValue | undefined): string {\n return typeof value === 'string' ? value : '';\n}\n\nfunction readStringArrayValue(\n value: FormFieldValue | undefined,\n): readonly string[] {\n // A table value is also an array, so the element check is what separates a\n // multi-select value from table rows here.\n return Array.isArray(value) && value.every((item) => typeof item === 'string')\n ? value\n : [];\n}\n\nfunction readNumberInputValue(value: FormFieldValue | undefined): string {\n return typeof value === 'number' ? String(value) : '';\n}\n\nfunction readInputPlaceholder(type: FormFieldDefinition['type']): string {\n const placeholders: Readonly<Record<FormFieldDefinition['type'], string>> = {\n boolean: '',\n autocomplete: '請輸入或選擇',\n checkbox: '請選擇一或多個選項',\n date: '請選擇日期',\n datetime: '請選擇日期與時間',\n file_upload: '請上傳附件',\n member: '搜尋姓名或信箱',\n money: '請輸入金額',\n number: '請輸入數字',\n org_unit: '搜尋組織名稱',\n radio: '請選擇一個選項',\n select: '請選擇一個選項',\n table: '',\n text: '請輸入文字',\n textarea: '請輸入多行文字',\n };\n\n return placeholders[type];\n}\n\nexport { FormRenderer as FormRendererView };\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"approver-filled-form-B4zPg9kz.cjs","names":["readApproverFilledFieldKeys","omitFormDataKeys","readNodeFillableFieldKeys","readNodeRequiredFieldKeys","isTableFieldDefinition","isOrgUnitFieldDefinition","readFormTableRows","isFormDataValueEmpty","isFormStaticOptionFieldDefinition","_rytass_bpm_core_shared","_rytass_bpm_core_client_form","_rytass_bpm_core_shared_form","readApplicantFormSchema","approverFilledKeys","definition","schema","field","readApplicantSubmission","values","readFillValidationSchema","fillableKeys","node","displaySchema","readFillDisplaySchema","requiredKeys","userTaskNodes","readFillLabelMarkers","viewerKeys","viewerFillNode","fieldKey","markers","readFillPayload","readFillChanges","seededTables","seeded","stored","draft","isFillConflictMessage","message","FORM_DATA_PATH_PATTERN","MISSING_FIELDS_PREFIX","readFillPathField","columnKey","path","fields","candidate","column","readFillPathErrorText","atLeast","reason","atMost","shortest","longest","readFillServerErrors","fillableFields","listed","missing","parts","part","UNKNOWN_FILL_ORG_UNIT_TEXT","readFillOrgUnitIds","value","readFillValueText","rows","totals","row","sum","label","total","orgUnitNames","items","optionLabels","option","memberNames","item","readFillSummary"],"sources":["../../src/views/instances/approver-filled-form.ts"],"sourcesContent":["import {\n isFormDataValueEmpty,\n omitFormDataKeys,\n readApproverFilledFieldKeys,\n readNodeFillableFieldKeys,\n readNodeRequiredFieldKeys,\n type UserTaskNode,\n type WorkflowDefinition,\n} from '@rytass/bpm-core-shared';\nimport {\n isFormStaticOptionFieldDefinition,\n isOrgUnitFieldDefinition,\n isTableFieldDefinition,\n type FormDefinitionSchema,\n type FormFieldDefinition,\n type FormFieldValue,\n type TableColumnDefinition,\n} from '@rytass/bpm-core-shared/form';\nimport { readFormTableRows } from '@rytass/bpm-core-client/form';\nimport type { WorkflowFormData } from '@rytass/bpm-core-client/workflow';\n\n/**\n * The form as the applicant sees it: without the fields some approver fills in.\n * The server drops those keys from a submission, so rendering one would swallow\n * what the applicant types, and a `minRows` or `required` on it would block a\n * launch nobody can complete. Returns an equal schema when nothing is\n * approver-filled; never mutates its input.\n */\nexport function readApplicantFormSchema(\n schema: FormDefinitionSchema,\n definition: WorkflowDefinition,\n): FormDefinitionSchema {\n const approverFilledKeys = readApproverFilledFieldKeys(definition);\n\n return {\n ...schema,\n fields: schema.fields.filter(\n (field) => !approverFilledKeys.has(field.fieldKey),\n ),\n };\n}\n\n/** The payload the applicant may send: `values` without approver-filled keys. */\nexport function readApplicantSubmission(\n values: WorkflowFormData,\n definition: WorkflowDefinition,\n): WorkflowFormData {\n // Dropping keys cannot change a remaining value's type, which is all the\n // shared helper's `unknown` return forgets.\n return omitFormDataKeys(\n values,\n readApproverFilledFieldKeys(definition),\n ) as WorkflowFormData;\n}\n\n/**\n * The form an approver's fill is validated against: only the fields `node`\n * lets them fill in, each required exactly when the node says `REQUIRED`. A\n * required table needs at least one row, the rule `validateFormRendererValues`\n * already applies, so the result agrees with the server's fill check. Never\n * mutates its input.\n */\nexport function readFillValidationSchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const displaySchema = readFillDisplaySchema(schema, node);\n\n // Derived from the display schema so the check and the asterisk read the\n // same rule.\n return {\n ...displaySchema,\n fields: displaySchema.fields.filter((field) =>\n fillableKeys.has(field.fieldKey),\n ),\n };\n}\n\n/**\n * The schema the fill node's approver sees: every field as the form defines\n * it, except that the node's fillable fields are required exactly when the\n * node says `REQUIRED`, so the asterisk matches what approving will check.\n */\nexport function readFillDisplaySchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const requiredKeys = readNodeRequiredFieldKeys(node);\n\n return {\n ...schema,\n fields: schema.fields.map((field) =>\n fillableKeys.has(field.fieldKey)\n ? {\n ...field,\n required: requiredKeys.has(field.fieldKey),\n // The node alone decides; the server ignores the form's rule.\n requiredWhen: undefined,\n }\n : field,\n ),\n };\n}\n\nconst userTaskNodes = (definition: WorkflowDefinition): readonly UserTaskNode[] =>\n definition.nodes.filter(\n (node): node is UserTaskNode => node.type === 'userTask',\n );\n\n/**\n * The marker shown after each approver-filled field's label: 「本關補填」 on\n * the fields `viewerFillNode` lets the viewer fill, 「<node>補填」 on every\n * other approver-filled field (the first node in the definition that fills\n * it names it).\n */\nexport function readFillLabelMarkers(\n definition: WorkflowDefinition,\n viewerFillNode: UserTaskNode | null,\n): Readonly<Record<string, string>> {\n const viewerKeys =\n viewerFillNode === null\n ? new Set<string>()\n : readNodeFillableFieldKeys(viewerFillNode);\n\n return userTaskNodes(definition).reduce<Readonly<Record<string, string>>>(\n (markers, node) => ({\n ...Object.fromEntries(\n [...readNodeFillableFieldKeys(node)].map((fieldKey) => [\n fieldKey,\n viewerKeys.has(fieldKey)\n ? '本關補填'\n : `${node.data.label || node.id}補填`,\n ]),\n ),\n ...markers,\n }),\n {},\n );\n}\n\n/**\n * What approving sends as `fill.formData`: the keys of `values` that `node`\n * lets its approver fill, and nothing else (the server refuses any other key).\n * `values` is what the approver changed, not the rendered form — a renderer\n * default the approver never touched (a table's empty rows, a boolean's\n * `false`) is not theirs to sign, and would otherwise show up as filled in.\n */\nexport function readFillPayload(\n values: WorkflowFormData,\n node: UserTaskNode,\n): Readonly<Record<string, unknown>> {\n return Object.fromEntries(\n [...readNodeFillableFieldKeys(node)]\n .filter((fieldKey) =>\n Object.prototype.hasOwnProperty.call(values, fieldKey),\n )\n // A cleared field reads `undefined`, which JSON drops, and the server\n // would keep the old value; `null` clears it.\n .map((fieldKey) => [fieldKey, values[fieldKey] ?? null]),\n );\n}\n\n/**\n * What the approver's fill amounts to: the fields they changed (`draft`), plus\n * each fill table the page seeds with `minRows` rows that the case does not\n * store yet. The page shows those rows and validates them, so they are sent\n * too — left out, the server would refuse the approval for missing rows the\n * approver can see on screen.\n */\nexport function readFillChanges({\n draft,\n node,\n schema,\n stored,\n values,\n}: {\n readonly draft: WorkflowFormData;\n readonly node: UserTaskNode;\n readonly schema: FormDefinitionSchema;\n readonly stored: WorkflowFormData;\n readonly values: WorkflowFormData;\n}): WorkflowFormData {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const seededTables = schema.fields.filter((field) => {\n const seeded = values[field.fieldKey];\n\n return (\n isTableFieldDefinition(field) &&\n fillableKeys.has(field.fieldKey) &&\n !Object.prototype.hasOwnProperty.call(stored, field.fieldKey) &&\n Array.isArray(seeded) &&\n seeded.length > 0\n );\n });\n\n return {\n ...Object.fromEntries(\n seededTables.map((field) => [field.fieldKey, values[field.fieldKey]]),\n ),\n ...draft,\n };\n}\n\n/** The server message of a fill against form data someone else changed. */\nexport function isFillConflictMessage(message: string): boolean {\n return message.includes('form data changed; refresh and retry');\n}\n\nconst FORM_DATA_PATH_PATTERN =\n /^formData\\.([A-Za-z_][A-Za-z0-9_]*(?:\\[\\d+\\]\\.[A-Za-z_][A-Za-z0-9_]*)?) (.+)$/;\nconst MISSING_FIELDS_PREFIX = 'Form data is missing required fields: ';\n\n/**\n * The field or table column a server error path such as `note` or\n * `items[0].name` points at, so its own settings can word the error.\n */\nfunction readFillPathField(\n path: string,\n fields: readonly FormFieldDefinition[],\n): FormFieldDefinition | TableColumnDefinition | undefined {\n const [fieldKey, columnKey] = path.split(/\\[\\d+\\]\\./);\n const field = fields.find((candidate) => candidate.fieldKey === fieldKey);\n\n return columnKey === undefined\n ? field\n : field && isTableFieldDefinition(field)\n ? field.columns.find((column) => column.fieldKey === columnKey)\n : undefined;\n}\n\nfunction readFillPathErrorText(\n path: string,\n reason: string,\n fields: readonly FormFieldDefinition[],\n): string {\n const atLeast = /^must include at least (\\d+) row\\(s\\)$/.exec(reason);\n const atMost = /^must include at most (\\d+) row\\(s\\)$/.exec(reason);\n const shortest = /^must be at least (\\d+) characters$/.exec(reason);\n const longest = /^must be at most (\\d+) characters$/.exec(reason);\n\n if (reason === 'is required') {\n return '此欄為必填。';\n }\n\n if (atLeast) {\n return `至少需要 ${atLeast[1]} 列。`;\n }\n\n if (atMost) {\n return `最多 ${atMost[1]} 列。`;\n }\n\n if (shortest) {\n return `至少需要 ${shortest[1]} 個字。`;\n }\n\n if (longest) {\n return `最多 ${longest[1]} 個字。`;\n }\n\n if (reason === 'does not match the required format') {\n const field = readFillPathField(path, fields);\n const message =\n field && (field.type === 'text' || field.type === 'textarea')\n ? field.patternErrorMessage?.trim()\n : undefined;\n\n return message || '格式不正確。';\n }\n\n // The server ran out of time on the format check; the value may be fine.\n if (reason === 'could not be checked against the required format in time') {\n return '格式檢查逾時,請縮短內容後再試,或請表單設計者簡化格式。';\n }\n\n return path.includes('[') ? '此欄內容不正確。' : '此欄位內容不正確。';\n}\n\n/**\n * Turns the server's fill validation message (docs/08 §8.5, error table 2)\n * into field errors keyed the way `FormRenderer` addresses them, so a check\n * the page missed still lands on the field. `null` when any part does not\n * parse — the caller then shows the message as it is.\n */\nexport function readFillServerErrors(\n message: string,\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): Readonly<Record<string, string>> | null {\n const fillableFields = schema.fields.filter((field) =>\n readNodeFillableFieldKeys(node).has(field.fieldKey),\n );\n\n if (message.startsWith(MISSING_FIELDS_PREFIX)) {\n // Labels may hold commas, so match each fillable field's label (or key)\n // against the list rather than splitting it.\n const listed = `, ${message.slice(MISSING_FIELDS_PREFIX.length)}, `;\n const missing = fillableFields.filter((field) =>\n listed.includes(`, ${field.label || field.fieldKey}, `),\n );\n\n return missing.length > 0\n ? Object.fromEntries(\n missing.map((field) => [\n field.fieldKey,\n `${field.label || field.fieldKey}為必填欄位。`,\n ]),\n )\n : null;\n }\n\n const parts = message.split('; ').map((part) => FORM_DATA_PATH_PATTERN.exec(part));\n\n if (parts.length === 0 || parts.some((part) => part === null)) {\n return null;\n }\n\n return Object.fromEntries(\n parts.flatMap((part) =>\n part === null\n ? []\n : [[part[1], readFillPathErrorText(part[1], part[2], fillableFields)]],\n ),\n );\n}\n\nexport interface FillSummaryItem {\n readonly fieldKey: string;\n readonly label: string;\n readonly text: string;\n}\n\nexport const UNKNOWN_FILL_ORG_UNIT_TEXT = '未知組織';\n\n/**\n * The org unit ids among `values`, once each and in form order, so the caller\n * can look up their names before building the summary.\n */\nexport function readFillOrgUnitIds(\n schema: FormDefinitionSchema,\n values: WorkflowFormData,\n): readonly string[] {\n return [\n ...new Set(\n schema.fields\n .filter(isOrgUnitFieldDefinition)\n .map((field) => values[field.fieldKey])\n .filter(\n (value): value is string =>\n typeof value === 'string' && value.trim().length > 0,\n ),\n ),\n ];\n}\n\nfunction readFillValueText(\n field: FormFieldDefinition,\n value: FormFieldValue | undefined,\n memberNames: ReadonlyMap<string, string>,\n orgUnitNames: ReadonlyMap<string, string>,\n): string {\n if (isTableFieldDefinition(field)) {\n const rows = readFormTableRows(value);\n const totals = field.columns\n .filter((column) => column.type === 'number' || column.type === 'money')\n .map((column) => ({\n label: column.label || column.fieldKey,\n total: rows.reduce(\n (sum, row) =>\n typeof row[column.fieldKey] === 'number' &&\n Number.isFinite(row[column.fieldKey])\n ? sum + (row[column.fieldKey] as number)\n : sum,\n 0,\n ),\n }));\n\n return [\n `${rows.length} 列`,\n ...(rows.length > 0\n ? totals.map(\n ({ label, total }) => `${label}合計 ${total.toLocaleString('zh-TW')}`,\n )\n : []),\n ].join(',');\n }\n\n if (isFormDataValueEmpty(value)) {\n return '未填寫';\n }\n\n if (typeof value === 'boolean') {\n return value ? '是' : '否';\n }\n\n if (isOrgUnitFieldDefinition(field)) {\n // `orgUnitNames` (`useOrgUnitNames`) maps an id still being looked up to\n // a loading text; an id the lookup did not name is 「未知組織」. Never the\n // raw id.\n return typeof value === 'string'\n ? (orgUnitNames.get(value) ?? UNKNOWN_FILL_ORG_UNIT_TEXT)\n : UNKNOWN_FILL_ORG_UNIT_TEXT;\n }\n\n const items = Array.isArray(value) ? value : [value];\n const optionLabels = isFormStaticOptionFieldDefinition(field)\n ? new Map(field.options.map((option) => [option.value, option.label]))\n : field.type === 'member'\n ? memberNames\n : new Map<string, string>();\n\n return items\n .map((item) =>\n typeof item === 'string' ? (optionLabels.get(item) ?? item) : String(item),\n )\n .join('、');\n}\n\n/**\n * One line per field `node` lets its approver fill, in form order, for the\n * approve dialog: what will be sent along with the approval. A member field\n * names each member found in `memberNames` and shows the id otherwise; an org\n * unit field names its unit from `orgUnitNames` and shows 「未知組織」 otherwise.\n */\nexport function readFillSummary(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n values: WorkflowFormData,\n memberNames: ReadonlyMap<string, string> = new Map(),\n orgUnitNames: ReadonlyMap<string, string> = new Map(),\n): readonly FillSummaryItem[] {\n const fillableKeys = readNodeFillableFieldKeys(node);\n\n return schema.fields\n .filter(\n (field) =>\n fillableKeys.has(field.fieldKey) &&\n Object.prototype.hasOwnProperty.call(values, field.fieldKey),\n )\n .map((field) => ({\n fieldKey: field.fieldKey,\n label: field.label || field.fieldKey,\n text: readFillValueText(\n field,\n values[field.fieldKey],\n memberNames,\n orgUnitNames,\n ),\n }));\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"approver-filled-form-Bgazhbvd.js","names":["readApplicantFormSchema","approverFilledKeys","readApproverFilledFieldKeys","definition","schema","field","readApplicantSubmission","omitFormDataKeys","values","readFillValidationSchema","fillableKeys","readNodeFillableFieldKeys","node","displaySchema","readFillDisplaySchema","requiredKeys","readNodeRequiredFieldKeys","userTaskNodes","readFillLabelMarkers","viewerKeys","viewerFillNode","fieldKey","markers","readFillPayload","readFillChanges","seededTables","seeded","isTableFieldDefinition","stored","draft","isFillConflictMessage","message","FORM_DATA_PATH_PATTERN","MISSING_FIELDS_PREFIX","readFillPathField","columnKey","path","fields","candidate","column","readFillPathErrorText","atLeast","reason","atMost","shortest","longest","readFillServerErrors","fillableFields","listed","missing","parts","part","UNKNOWN_FILL_ORG_UNIT_TEXT","readFillOrgUnitIds","isOrgUnitFieldDefinition","value","readFillValueText","rows","readFormTableRows","totals","row","sum","label","total","isFormDataValueEmpty","orgUnitNames","items","optionLabels","isFormStaticOptionFieldDefinition","option","memberNames","item","readFillSummary"],"sources":["../../src/views/instances/approver-filled-form.ts"],"sourcesContent":["import {\n isFormDataValueEmpty,\n omitFormDataKeys,\n readApproverFilledFieldKeys,\n readNodeFillableFieldKeys,\n readNodeRequiredFieldKeys,\n type UserTaskNode,\n type WorkflowDefinition,\n} from '@rytass/bpm-core-shared';\nimport {\n isFormStaticOptionFieldDefinition,\n isOrgUnitFieldDefinition,\n isTableFieldDefinition,\n type FormDefinitionSchema,\n type FormFieldDefinition,\n type FormFieldValue,\n type TableColumnDefinition,\n} from '@rytass/bpm-core-shared/form';\nimport { readFormTableRows } from '@rytass/bpm-core-client/form';\nimport type { WorkflowFormData } from '@rytass/bpm-core-client/workflow';\n\n/**\n * The form as the applicant sees it: without the fields some approver fills in.\n * The server drops those keys from a submission, so rendering one would swallow\n * what the applicant types, and a `minRows` or `required` on it would block a\n * launch nobody can complete. Returns an equal schema when nothing is\n * approver-filled; never mutates its input.\n */\nexport function readApplicantFormSchema(\n schema: FormDefinitionSchema,\n definition: WorkflowDefinition,\n): FormDefinitionSchema {\n const approverFilledKeys = readApproverFilledFieldKeys(definition);\n\n return {\n ...schema,\n fields: schema.fields.filter(\n (field) => !approverFilledKeys.has(field.fieldKey),\n ),\n };\n}\n\n/** The payload the applicant may send: `values` without approver-filled keys. */\nexport function readApplicantSubmission(\n values: WorkflowFormData,\n definition: WorkflowDefinition,\n): WorkflowFormData {\n // Dropping keys cannot change a remaining value's type, which is all the\n // shared helper's `unknown` return forgets.\n return omitFormDataKeys(\n values,\n readApproverFilledFieldKeys(definition),\n ) as WorkflowFormData;\n}\n\n/**\n * The form an approver's fill is validated against: only the fields `node`\n * lets them fill in, each required exactly when the node says `REQUIRED`. A\n * required table needs at least one row, the rule `validateFormRendererValues`\n * already applies, so the result agrees with the server's fill check. Never\n * mutates its input.\n */\nexport function readFillValidationSchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const displaySchema = readFillDisplaySchema(schema, node);\n\n // Derived from the display schema so the check and the asterisk read the\n // same rule.\n return {\n ...displaySchema,\n fields: displaySchema.fields.filter((field) =>\n fillableKeys.has(field.fieldKey),\n ),\n };\n}\n\n/**\n * The schema the fill node's approver sees: every field as the form defines\n * it, except that the node's fillable fields are required exactly when the\n * node says `REQUIRED`, so the asterisk matches what approving will check.\n */\nexport function readFillDisplaySchema(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): FormDefinitionSchema {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const requiredKeys = readNodeRequiredFieldKeys(node);\n\n return {\n ...schema,\n fields: schema.fields.map((field) =>\n fillableKeys.has(field.fieldKey)\n ? {\n ...field,\n required: requiredKeys.has(field.fieldKey),\n // The node alone decides; the server ignores the form's rule.\n requiredWhen: undefined,\n }\n : field,\n ),\n };\n}\n\nconst userTaskNodes = (definition: WorkflowDefinition): readonly UserTaskNode[] =>\n definition.nodes.filter(\n (node): node is UserTaskNode => node.type === 'userTask',\n );\n\n/**\n * The marker shown after each approver-filled field's label: 「本關補填」 on\n * the fields `viewerFillNode` lets the viewer fill, 「<node>補填」 on every\n * other approver-filled field (the first node in the definition that fills\n * it names it).\n */\nexport function readFillLabelMarkers(\n definition: WorkflowDefinition,\n viewerFillNode: UserTaskNode | null,\n): Readonly<Record<string, string>> {\n const viewerKeys =\n viewerFillNode === null\n ? new Set<string>()\n : readNodeFillableFieldKeys(viewerFillNode);\n\n return userTaskNodes(definition).reduce<Readonly<Record<string, string>>>(\n (markers, node) => ({\n ...Object.fromEntries(\n [...readNodeFillableFieldKeys(node)].map((fieldKey) => [\n fieldKey,\n viewerKeys.has(fieldKey)\n ? '本關補填'\n : `${node.data.label || node.id}補填`,\n ]),\n ),\n ...markers,\n }),\n {},\n );\n}\n\n/**\n * What approving sends as `fill.formData`: the keys of `values` that `node`\n * lets its approver fill, and nothing else (the server refuses any other key).\n * `values` is what the approver changed, not the rendered form — a renderer\n * default the approver never touched (a table's empty rows, a boolean's\n * `false`) is not theirs to sign, and would otherwise show up as filled in.\n */\nexport function readFillPayload(\n values: WorkflowFormData,\n node: UserTaskNode,\n): Readonly<Record<string, unknown>> {\n return Object.fromEntries(\n [...readNodeFillableFieldKeys(node)]\n .filter((fieldKey) =>\n Object.prototype.hasOwnProperty.call(values, fieldKey),\n )\n // A cleared field reads `undefined`, which JSON drops, and the server\n // would keep the old value; `null` clears it.\n .map((fieldKey) => [fieldKey, values[fieldKey] ?? null]),\n );\n}\n\n/**\n * What the approver's fill amounts to: the fields they changed (`draft`), plus\n * each fill table the page seeds with `minRows` rows that the case does not\n * store yet. The page shows those rows and validates them, so they are sent\n * too — left out, the server would refuse the approval for missing rows the\n * approver can see on screen.\n */\nexport function readFillChanges({\n draft,\n node,\n schema,\n stored,\n values,\n}: {\n readonly draft: WorkflowFormData;\n readonly node: UserTaskNode;\n readonly schema: FormDefinitionSchema;\n readonly stored: WorkflowFormData;\n readonly values: WorkflowFormData;\n}): WorkflowFormData {\n const fillableKeys = readNodeFillableFieldKeys(node);\n const seededTables = schema.fields.filter((field) => {\n const seeded = values[field.fieldKey];\n\n return (\n isTableFieldDefinition(field) &&\n fillableKeys.has(field.fieldKey) &&\n !Object.prototype.hasOwnProperty.call(stored, field.fieldKey) &&\n Array.isArray(seeded) &&\n seeded.length > 0\n );\n });\n\n return {\n ...Object.fromEntries(\n seededTables.map((field) => [field.fieldKey, values[field.fieldKey]]),\n ),\n ...draft,\n };\n}\n\n/** The server message of a fill against form data someone else changed. */\nexport function isFillConflictMessage(message: string): boolean {\n return message.includes('form data changed; refresh and retry');\n}\n\nconst FORM_DATA_PATH_PATTERN =\n /^formData\\.([A-Za-z_][A-Za-z0-9_]*(?:\\[\\d+\\]\\.[A-Za-z_][A-Za-z0-9_]*)?) (.+)$/;\nconst MISSING_FIELDS_PREFIX = 'Form data is missing required fields: ';\n\n/**\n * The field or table column a server error path such as `note` or\n * `items[0].name` points at, so its own settings can word the error.\n */\nfunction readFillPathField(\n path: string,\n fields: readonly FormFieldDefinition[],\n): FormFieldDefinition | TableColumnDefinition | undefined {\n const [fieldKey, columnKey] = path.split(/\\[\\d+\\]\\./);\n const field = fields.find((candidate) => candidate.fieldKey === fieldKey);\n\n return columnKey === undefined\n ? field\n : field && isTableFieldDefinition(field)\n ? field.columns.find((column) => column.fieldKey === columnKey)\n : undefined;\n}\n\nfunction readFillPathErrorText(\n path: string,\n reason: string,\n fields: readonly FormFieldDefinition[],\n): string {\n const atLeast = /^must include at least (\\d+) row\\(s\\)$/.exec(reason);\n const atMost = /^must include at most (\\d+) row\\(s\\)$/.exec(reason);\n const shortest = /^must be at least (\\d+) characters$/.exec(reason);\n const longest = /^must be at most (\\d+) characters$/.exec(reason);\n\n if (reason === 'is required') {\n return '此欄為必填。';\n }\n\n if (atLeast) {\n return `至少需要 ${atLeast[1]} 列。`;\n }\n\n if (atMost) {\n return `最多 ${atMost[1]} 列。`;\n }\n\n if (shortest) {\n return `至少需要 ${shortest[1]} 個字。`;\n }\n\n if (longest) {\n return `最多 ${longest[1]} 個字。`;\n }\n\n if (reason === 'does not match the required format') {\n const field = readFillPathField(path, fields);\n const message =\n field && (field.type === 'text' || field.type === 'textarea')\n ? field.patternErrorMessage?.trim()\n : undefined;\n\n return message || '格式不正確。';\n }\n\n // The server ran out of time on the format check; the value may be fine.\n if (reason === 'could not be checked against the required format in time') {\n return '格式檢查逾時,請縮短內容後再試,或請表單設計者簡化格式。';\n }\n\n return path.includes('[') ? '此欄內容不正確。' : '此欄位內容不正確。';\n}\n\n/**\n * Turns the server's fill validation message (docs/08 §8.5, error table 2)\n * into field errors keyed the way `FormRenderer` addresses them, so a check\n * the page missed still lands on the field. `null` when any part does not\n * parse — the caller then shows the message as it is.\n */\nexport function readFillServerErrors(\n message: string,\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n): Readonly<Record<string, string>> | null {\n const fillableFields = schema.fields.filter((field) =>\n readNodeFillableFieldKeys(node).has(field.fieldKey),\n );\n\n if (message.startsWith(MISSING_FIELDS_PREFIX)) {\n // Labels may hold commas, so match each fillable field's label (or key)\n // against the list rather than splitting it.\n const listed = `, ${message.slice(MISSING_FIELDS_PREFIX.length)}, `;\n const missing = fillableFields.filter((field) =>\n listed.includes(`, ${field.label || field.fieldKey}, `),\n );\n\n return missing.length > 0\n ? Object.fromEntries(\n missing.map((field) => [\n field.fieldKey,\n `${field.label || field.fieldKey}為必填欄位。`,\n ]),\n )\n : null;\n }\n\n const parts = message.split('; ').map((part) => FORM_DATA_PATH_PATTERN.exec(part));\n\n if (parts.length === 0 || parts.some((part) => part === null)) {\n return null;\n }\n\n return Object.fromEntries(\n parts.flatMap((part) =>\n part === null\n ? []\n : [[part[1], readFillPathErrorText(part[1], part[2], fillableFields)]],\n ),\n );\n}\n\nexport interface FillSummaryItem {\n readonly fieldKey: string;\n readonly label: string;\n readonly text: string;\n}\n\nexport const UNKNOWN_FILL_ORG_UNIT_TEXT = '未知組織';\n\n/**\n * The org unit ids among `values`, once each and in form order, so the caller\n * can look up their names before building the summary.\n */\nexport function readFillOrgUnitIds(\n schema: FormDefinitionSchema,\n values: WorkflowFormData,\n): readonly string[] {\n return [\n ...new Set(\n schema.fields\n .filter(isOrgUnitFieldDefinition)\n .map((field) => values[field.fieldKey])\n .filter(\n (value): value is string =>\n typeof value === 'string' && value.trim().length > 0,\n ),\n ),\n ];\n}\n\nfunction readFillValueText(\n field: FormFieldDefinition,\n value: FormFieldValue | undefined,\n memberNames: ReadonlyMap<string, string>,\n orgUnitNames: ReadonlyMap<string, string>,\n): string {\n if (isTableFieldDefinition(field)) {\n const rows = readFormTableRows(value);\n const totals = field.columns\n .filter((column) => column.type === 'number' || column.type === 'money')\n .map((column) => ({\n label: column.label || column.fieldKey,\n total: rows.reduce(\n (sum, row) =>\n typeof row[column.fieldKey] === 'number' &&\n Number.isFinite(row[column.fieldKey])\n ? sum + (row[column.fieldKey] as number)\n : sum,\n 0,\n ),\n }));\n\n return [\n `${rows.length} 列`,\n ...(rows.length > 0\n ? totals.map(\n ({ label, total }) => `${label}合計 ${total.toLocaleString('zh-TW')}`,\n )\n : []),\n ].join(',');\n }\n\n if (isFormDataValueEmpty(value)) {\n return '未填寫';\n }\n\n if (typeof value === 'boolean') {\n return value ? '是' : '否';\n }\n\n if (isOrgUnitFieldDefinition(field)) {\n // `orgUnitNames` (`useOrgUnitNames`) maps an id still being looked up to\n // a loading text; an id the lookup did not name is 「未知組織」. Never the\n // raw id.\n return typeof value === 'string'\n ? (orgUnitNames.get(value) ?? UNKNOWN_FILL_ORG_UNIT_TEXT)\n : UNKNOWN_FILL_ORG_UNIT_TEXT;\n }\n\n const items = Array.isArray(value) ? value : [value];\n const optionLabels = isFormStaticOptionFieldDefinition(field)\n ? new Map(field.options.map((option) => [option.value, option.label]))\n : field.type === 'member'\n ? memberNames\n : new Map<string, string>();\n\n return items\n .map((item) =>\n typeof item === 'string' ? (optionLabels.get(item) ?? item) : String(item),\n )\n .join('、');\n}\n\n/**\n * One line per field `node` lets its approver fill, in form order, for the\n * approve dialog: what will be sent along with the approval. A member field\n * names each member found in `memberNames` and shows the id otherwise; an org\n * unit field names its unit from `orgUnitNames` and shows 「未知組織」 otherwise.\n */\nexport function readFillSummary(\n schema: FormDefinitionSchema,\n node: UserTaskNode,\n values: WorkflowFormData,\n memberNames: ReadonlyMap<string, string> = new Map(),\n orgUnitNames: ReadonlyMap<string, string> = new Map(),\n): readonly FillSummaryItem[] {\n const fillableKeys = readNodeFillableFieldKeys(node);\n\n return schema.fields\n .filter(\n (field) =>\n fillableKeys.has(field.fieldKey) &&\n Object.prototype.hasOwnProperty.call(values, field.fieldKey),\n )\n .map((field) => ({\n fieldKey: field.fieldKey,\n label: field.label || field.fieldKey,\n text: readFillValueText(\n field,\n values[field.fieldKey],\n memberNames,\n orgUnitNames,\n ),\n }));\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"delegations-BfhCDz5e.js","names":["_Fragment","styles","BPMFormField","formatMemberLabel","useAuth","Fragment$1","ContentHeader","delegations_module_default","DELEGATION_MODAL_FIELD_LAYOUT","FormFieldLayout","DELEGATION_PAGE_SIZE_OPTIONS","DELEGATION_STATUS_TABS","SCOPE_FILTER_OPTIONS","SCOPE_OPTIONS","DelegationsView","currentMemberId","member","agentMember","setAgentMember","useState","endAt","setEndAt","error","setError","loading","setLoading","memberLoading","setMemberLoading","memberOptions","setMemberOptions","modalOpen","setModalOpen","rulePage","setRulePage","rulePageSize","setRulePageSize","ruleStatus","setRuleStatus","ruleTotalCount","setRuleTotalCount","rules","setRules","saving","setSaving","scopeFilterType","setScopeFilterType","scopeTemplates","setScopeTemplates","scopeType","setScopeType","startAt","setStartAt","templateLoading","setTemplateLoading","templateOptions","setTemplateOptions","refreshRequestIdRef","useRef","refreshRules","useCallback","requestId","rulePageResult","members","listDelegationRulesPage","searchMembers","readMemberOption","requestError","readErrorMessage","membersById","useMemo","option","rows","rule","readScopeLabel","selectedScopeType","columns","MemberNameWithEmailTooltip","record","DelegationStatusBadge","Typography","formatDateTime","handleRevoke","revokeDelegationRule","tableActions","handleSearchMembers","searchText","handleSearchTemplates","nextOptions","listApprovalTemplates","readTemplateOption","mergeTemplateOptions","selectedOptions","filterTemplateOptions","openCreateModal","closeCreateModal","handleCreate","isInvalidDelegationDateRange","createDelegationRule","template","PageHeader","Button","PlusIcon","SectionGroup","Section","FilterArea","FilterLine","Filter","FormField","Select","readScopeFilterOption","Tab","readDelegationStatusTabKey","activeKey","TabItem","statusTab","Table","page","pageSize","from","to","total","Modal","MemberAutoCompleteField","readScopeOptionFromValue","AutoComplete","selectedTemplates","readTemplateOptionsFromValue","nextTemplates","currentOptions","open","DateTimePicker","formatDelegationDateTimePickerValue","nextValue","readDelegationDateTimePickerValue","onChange","readMemberOptionFromValue","readUniqueMemberOption","options","onSearch","email","Tooltip","tooltipProps","name","isRecord","value","displayName","id","normalizedSearchText","matches","item","availableOptions","currentOption","isSelectableDelegationScopeType","status","Badge","date","parseDelegationDateTimeValue","formatDateParts","padDatePart","startDate","endDate","parsedDate","datePart","timePart","year","month","day","hour","minute"],"sources":["../../src/views/delegations/delegations.module.scss","../../src/views/delegations/DelegationsView.tsx"],"sourcesContent":[".delegationFilterArea {\n :global(.mzn-filter-area__actions) {\n display: none;\n }\n\n :global(.mzn-form-field__label-area) {\n display: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n}\n\n.delegationModalFields {\n display: grid;\n gap: 12px;\n width: 100%;\n\n :global(.mzn-form-field--horizontal) {\n align-items: flex-start;\n }\n\n :global(.mzn-form-field--horizontal .mzn-form-field__label-area) {\n flex: 0 0 112px;\n width: 112px;\n min-width: 112px;\n white-space: nowrap;\n }\n\n :global(.mzn-form-field--horizontal .mzn-form-field__label) {\n flex-wrap: nowrap;\n white-space: nowrap;\n }\n\n :global(.mzn-form-field--horizontal .mzn-form-field__data-entry) {\n flex: 1 1 auto;\n align-items: stretch;\n min-width: 0;\n max-width: none;\n }\n\n :global(.mzn-form-field__control-field-slot--main) {\n width: 100%;\n min-width: 0;\n }\n\n :global(.mzn-autocomplete) {\n width: 100%;\n min-width: 0;\n }\n\n :global(.mzn-input),\n :global(.mzn-input-container),\n :global(.mzn-picker),\n :global(.mzn-select),\n :global(.mzn-select-trigger),\n :global(.mzn-text-field) {\n width: 100%;\n min-width: 0 !important;\n }\n\n :global(.mzn-select-trigger__input) {\n width: 100%;\n min-width: 0 !important;\n text-overflow: ellipsis;\n }\n}\n\n.memberNameWithTooltip {\n cursor: help;\n text-decoration: underline dotted;\n text-underline-offset: 3px;\n}\n","'use client';\n\nimport {\n Key,\n ReactElement,\n RefCallback,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n AutoComplete,\n Badge,\n Button,\n DateTimePicker,\n Filter,\n FilterArea,\n FilterLine,\n FormField,\n Modal,\n PageHeader,\n Section,\n SectionGroup,\n Select,\n Tab,\n TabItem,\n Table,\n Tooltip,\n Typography,\n} from '@mezzanine-ui/react';\nimport ContentHeader from '@mezzanine-ui/react/ContentHeader';\nimport { PlusIcon } from '@mezzanine-ui/icons';\nimport { FormFieldLayout } from '@mezzanine-ui/core/form';\nimport type { TableActions, TableColumn } from '@mezzanine-ui/core/table';\nimport { BPMFormField } from '../../components/bpm-form-field';\nimport { formatDateTime } from '../../lib/format-date-time';\nimport { formatMemberLabel } from '../../lib/format-member-label';\nimport { useAuth } from '../../lib/auth-provider';\nimport {\n DelegationRuleRecord,\n DelegationRuleStatus,\n DelegationScopeType,\n MemberProfileRecord,\n createDelegationRule,\n listDelegationRulesPage,\n revokeDelegationRule,\n searchMembers,\n} from '@rytass/bpm-core-client/workflow';\nimport {\n ApprovalTemplateRecord,\n listApprovalTemplates,\n} from '@rytass/bpm-core-client/template';\nimport styles from './delegations.module.scss';\n\ntype DelegationStatusTabKey = 'ALL' | DelegationRuleStatus;\n\ntype MemberOption = Readonly<{\n displayName: string;\n email: string | null;\n id: string;\n name: string;\n}>;\n\ntype ScopeOption = Readonly<{\n id: DelegationScopeType;\n name: string;\n}>;\n\ntype ScopeFilterOption = Readonly<{\n id: 'ALL_SCOPES' | DelegationScopeType;\n name: string;\n scopeType: DelegationScopeType | null;\n}>;\n\ntype TemplateOption = Readonly<{\n id: string;\n name: string;\n}>;\n\ntype DelegationRuleRow = Readonly<\n Record<string, unknown> &\n DelegationRuleRecord & {\n agentEmail: string | null;\n agentName: string;\n key: string;\n scopeLabel: string;\n }\n>;\n\nconst DELEGATION_MODAL_FIELD_LAYOUT = FormFieldLayout.HORIZONTAL;\nconst DELEGATION_PAGE_SIZE_OPTIONS = [10, 20, 50];\nconst DELEGATION_STATUS_TABS: readonly {\n readonly key: DelegationStatusTabKey;\n readonly label: string;\n}[] = [\n { key: 'ALL', label: '全部' },\n { key: 'ACTIVE', label: '啟用中' },\n { key: 'REVOKED', label: '已撤銷' },\n { key: 'EXPIRED', label: '已過期' },\n];\nconst SCOPE_FILTER_OPTIONS: readonly ScopeFilterOption[] = [\n { id: 'ALL_SCOPES', name: '全部範圍', scopeType: null },\n { id: 'ALL', name: '全部簽核', scopeType: 'ALL' },\n { id: 'TEMPLATE_LIST', name: '指定模板', scopeType: 'TEMPLATE_LIST' },\n];\nconst SCOPE_OPTIONS: readonly ScopeOption[] = [\n { id: 'ALL', name: '全部簽核' },\n { id: 'TEMPLATE_LIST', name: '指定模板' },\n];\n\nexport function DelegationsView(): ReactElement {\n const { member } = useAuth();\n const currentMemberId = member?.memberId ?? null;\n const [agentMember, setAgentMember] = useState<MemberOption | null>(null);\n const [endAt, setEndAt] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [memberLoading, setMemberLoading] = useState(false);\n const [memberOptions, setMemberOptions] = useState<readonly MemberOption[]>(\n [],\n );\n const [modalOpen, setModalOpen] = useState(false);\n const [rulePage, setRulePage] = useState(1);\n const [rulePageSize, setRulePageSize] = useState(10);\n const [ruleStatus, setRuleStatus] = useState<DelegationStatusTabKey>('ALL');\n const [ruleTotalCount, setRuleTotalCount] = useState(0);\n const [rules, setRules] = useState<readonly DelegationRuleRecord[]>([]);\n const [saving, setSaving] = useState(false);\n const [scopeFilterType, setScopeFilterType] = useState<ScopeFilterOption>(\n SCOPE_FILTER_OPTIONS[0],\n );\n const [scopeTemplates, setScopeTemplates] = useState<\n readonly TemplateOption[]\n >([]);\n const [scopeType, setScopeType] = useState<ScopeOption>(SCOPE_OPTIONS[0]);\n const [startAt, setStartAt] = useState('');\n const [templateLoading, setTemplateLoading] = useState(false);\n const [templateOptions, setTemplateOptions] = useState<\n readonly TemplateOption[]\n >([]);\n const refreshRequestIdRef = useRef(0);\n\n const refreshRules = useCallback(async (): Promise<void> => {\n const requestId = refreshRequestIdRef.current + 1;\n refreshRequestIdRef.current = requestId;\n\n if (!currentMemberId) {\n setLoading(false);\n return;\n }\n\n setLoading(true);\n setError(null);\n\n try {\n const [rulePageResult, members] = await Promise.all([\n listDelegationRulesPage({\n includeInactive: true,\n page: rulePage,\n pageSize: rulePageSize,\n principalMemberId: currentMemberId,\n scopeType: scopeFilterType.scopeType,\n status: ruleStatus === 'ALL' ? null : ruleStatus,\n }),\n searchMembers(''),\n ]);\n\n if (requestId === refreshRequestIdRef.current) {\n setRules(rulePageResult.rules);\n setRuleTotalCount(rulePageResult.totalCount);\n setMemberOptions(members.map(readMemberOption));\n }\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 }, [currentMemberId, rulePage, rulePageSize, ruleStatus, scopeFilterType]);\n\n useEffect((): void => {\n void refreshRules();\n }, [refreshRules]);\n\n const membersById = useMemo(\n (): ReadonlyMap<string, MemberOption> =>\n new Map(memberOptions.map((option) => [option.id, option])),\n [memberOptions],\n );\n const rows = useMemo(\n (): DelegationRuleRow[] =>\n rules.map((rule) => ({\n ...rule,\n agentEmail: membersById.get(rule.agentMemberId)?.email ?? null,\n agentName:\n membersById.get(rule.agentMemberId)?.displayName ??\n rule.agentMemberId,\n key: rule.id,\n scopeLabel: readScopeLabel(rule),\n })),\n [membersById, rules],\n );\n const selectedScopeType =\n SCOPE_OPTIONS.find((option) => option.id === scopeType.id) ??\n SCOPE_OPTIONS[0];\n const columns = useMemo(\n (): TableColumn<DelegationRuleRow>[] => [\n {\n key: 'agent',\n render: (record: DelegationRuleRow): ReactElement => (\n <MemberNameWithEmailTooltip\n email={record.agentEmail}\n name={record.agentName}\n />\n ),\n title: '代理人',\n width: 220,\n },\n { dataIndex: 'scopeLabel', key: 'scope', title: '代理範圍', width: 220 },\n {\n key: 'status',\n render: (record: DelegationRuleRow): ReactElement => (\n <DelegationStatusBadge status={record.status} />\n ),\n title: '狀態',\n width: 120,\n },\n {\n key: 'startAt',\n render: (record: DelegationRuleRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {formatDateTime(record.startAt)}\n </Typography>\n ),\n title: '開始時間',\n width: 220,\n },\n {\n key: 'endAt',\n render: (record: DelegationRuleRow): ReactElement => (\n <Typography component=\"span\" variant=\"body\">\n {record.endAt ? formatDateTime(record.endAt) : '-'}\n </Typography>\n ),\n title: '結束時間',\n width: 220,\n },\n ],\n [],\n );\n const handleRevoke = useCallback(\n async (id: string): Promise<void> => {\n if (!currentMemberId) {\n return;\n }\n\n setError(null);\n\n try {\n await revokeDelegationRule({\n id,\n revokedByMemberId: currentMemberId,\n });\n await refreshRules();\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n }\n },\n [currentMemberId, refreshRules],\n );\n const tableActions = useMemo(\n (): TableActions<DelegationRuleRow> => ({\n render: (\n record,\n ): ReturnType<TableActions<DelegationRuleRow>['render']> =>\n record.status === 'ACTIVE'\n ? [\n {\n name: '撤銷',\n onClick: (): void => void handleRevoke(record.id),\n },\n ]\n : [],\n variant: 'destructive-secondary',\n width: 88,\n }),\n [handleRevoke],\n );\n\n async function handleSearchMembers(searchText: string): Promise<void> {\n setMemberLoading(true);\n\n try {\n setMemberOptions((await searchMembers(searchText)).map(readMemberOption));\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setMemberLoading(false);\n }\n }\n\n async function handleSearchTemplates(\n searchText: string,\n selectedOptions: readonly TemplateOption[] = scopeTemplates,\n ): Promise<void> {\n setTemplateLoading(true);\n\n try {\n const nextOptions = (await listApprovalTemplates()).map(\n readTemplateOption,\n );\n\n setTemplateOptions(\n mergeTemplateOptions(\n selectedOptions,\n filterTemplateOptions(nextOptions, searchText),\n ),\n );\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setTemplateLoading(false);\n }\n }\n\n function openCreateModal(): void {\n setAgentMember(null);\n setEndAt('');\n setScopeTemplates([]);\n setScopeType(SCOPE_OPTIONS[0]);\n setStartAt('');\n setTemplateOptions([]);\n void handleSearchTemplates('', []);\n setModalOpen(true);\n }\n\n function closeCreateModal(): void {\n if (saving) {\n return;\n }\n\n setModalOpen(false);\n }\n\n async function handleCreate(): Promise<void> {\n if (!currentMemberId) {\n return;\n }\n\n if (!agentMember) {\n setError('請選擇代理人');\n return;\n }\n\n if (agentMember.id === currentMemberId) {\n setError('代理人不可設定為自己');\n return;\n }\n\n if (selectedScopeType.id === 'TEMPLATE_LIST' && scopeTemplates.length < 1) {\n setError('請選擇至少一個簽核模板');\n return;\n }\n\n if (isInvalidDelegationDateRange(startAt, endAt)) {\n setError('結束時間必須晚於起始時間');\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n await createDelegationRule({\n agentMemberId: agentMember.id,\n createdByMemberId: currentMemberId,\n endAt: endAt || null,\n principalMemberId: currentMemberId,\n priority: 100,\n requiresConfirmation: false,\n scopeConditionCel: null,\n scopeTemplateIds:\n selectedScopeType.id === 'TEMPLATE_LIST'\n ? scopeTemplates.map((template) => template.id)\n : [],\n scopeType: selectedScopeType.id,\n startAt: startAt || null,\n });\n setModalOpen(false);\n\n if (rulePage === 1) {\n await refreshRules();\n } else {\n setRulePage(1);\n }\n } catch (requestError: unknown) {\n setError(readErrorMessage(requestError));\n } finally {\n setSaving(false);\n }\n }\n\n return (\n <>\n <PageHeader>\n <ContentHeader\n description=\"設定自己的簽核代理,讓指定期間內的新待簽任務自動交由代理人處理。\"\n title=\"我的代理\"\n >\n <Button\n icon={PlusIcon}\n iconType=\"leading\"\n onClick={openCreateModal}\n variant=\"base-primary\"\n >\n 建立代理\n </Button>\n </ContentHeader>\n </PageHeader>\n\n <SectionGroup>\n <Section\n filterArea={\n <FilterArea className={styles.delegationFilterArea} size=\"sub\">\n <FilterLine>\n <Filter span={2}>\n <FormField\n fullWidth\n layout={FormFieldLayout.VERTICAL}\n name=\"scopeFilterType\"\n >\n <Select\n clearable={false}\n fullWidth\n onChange={(option): void => {\n setScopeFilterType(readScopeFilterOption(option));\n setRulePage(1);\n }}\n options={[...SCOPE_FILTER_OPTIONS]}\n placeholder=\"代理範圍\"\n size=\"sub\"\n value={scopeFilterType}\n />\n </FormField>\n </Filter>\n </FilterLine>\n </FilterArea>\n }\n tab={\n <Tab\n activeKey={ruleStatus}\n onChange={(activeKey): void => {\n setRuleStatus(readDelegationStatusTabKey(activeKey));\n setRulePage(1);\n }}\n >\n {DELEGATION_STATUS_TABS.map((statusTab) => (\n <TabItem key={statusTab.key}>{statusTab.label}</TabItem>\n ))}\n </Tab>\n }\n >\n {error ? (\n <Typography color=\"text-error\" variant=\"body\">\n {error}\n </Typography>\n ) : null}\n <Table\n actions={tableActions}\n columns={columns}\n dataSource={rows}\n fullWidth\n loading={loading}\n pagination={{\n current: rulePage,\n onChange: (page): void => {\n setRulePage(page);\n },\n onChangePageSize: (pageSize): void => {\n setRulePage(1);\n setRulePageSize(pageSize);\n },\n pageSize: rulePageSize,\n pageSizeLabel: '每頁筆數',\n pageSizeOptions: DELEGATION_PAGE_SIZE_OPTIONS,\n renderResultSummary: (from, to, total): string =>\n `顯示 ${from}-${to} 筆,共 ${total} 筆`,\n showPageSizeOptions: true,\n total: ruleTotalCount,\n }}\n />\n </Section>\n </SectionGroup>\n\n <Modal\n cancelText=\"取消\"\n confirmButtonProps={{\n disabled: !agentMember,\n }}\n confirmText=\"建立代理\"\n loading={saving}\n modalType=\"standard\"\n onCancel={closeCreateModal}\n onClose={closeCreateModal}\n onConfirm={(): void => void handleCreate()}\n open={modalOpen}\n showModalFooter\n showModalHeader\n size=\"regular\"\n supportingText=\"代理生效後,後續建立的待簽任務會依範圍自動指派給代理人。\"\n title=\"建立個人代理\"\n >\n <div className={styles.delegationModalFields}>\n <MemberAutoCompleteField\n label=\"代理人\"\n loading={memberLoading}\n name=\"agentMemberId\"\n onChange={setAgentMember}\n onSearch={handleSearchMembers}\n options={memberOptions}\n value={agentMember}\n />\n <BPMFormField label=\"代理範圍\" name=\"scopeType\" required>\n <Select\n clearable={false}\n fullWidth\n onChange={(option): void =>\n setScopeType(readScopeOptionFromValue(option))\n }\n options={[...SCOPE_OPTIONS]}\n placeholder=\"選擇代理範圍\"\n value={selectedScopeType}\n />\n </BPMFormField>\n {selectedScopeType.id === 'TEMPLATE_LIST' ? (\n <BPMFormField label=\"簽核模板\" name=\"scopeTemplateIds\" required>\n <AutoComplete\n asyncData\n disabledOptionsFilter\n emptyText=\"沒有符合的模板\"\n inputProps={{\n autoCapitalize: 'none',\n autoCorrect: 'off',\n name: 'scopeTemplateIds',\n spellCheck: false,\n }}\n loading={templateLoading}\n loadingText=\"搜尋模板中...\"\n mode=\"multiple\"\n onChange={(nextTemplates): void => {\n const selectedTemplates =\n readTemplateOptionsFromValue(nextTemplates);\n\n setScopeTemplates(selectedTemplates);\n setTemplateOptions((currentOptions) =>\n mergeTemplateOptions(selectedTemplates, currentOptions),\n );\n }}\n onSearch={handleSearchTemplates}\n onVisibilityChange={(open): void => {\n if (open) {\n void handleSearchTemplates('');\n }\n }}\n options={[...templateOptions]}\n overflowStrategy=\"wrap\"\n placeholder=\"搜尋並選取簽核模板\"\n searchDebounceTime={300}\n value={[...scopeTemplates]}\n />\n </BPMFormField>\n ) : null}\n <BPMFormField label=\"起始時間\" name=\"startAt\">\n <DateTimePicker\n formatDate=\"YYYY-MM-DD\"\n formatTime=\"HH:mm\"\n fullWidth\n hideSecond\n onChange={(nextValue): void =>\n setStartAt(formatDelegationDateTimePickerValue(nextValue))\n }\n placeholderLeft=\"留空立即生效\"\n placeholderRight=\"選擇時間\"\n value={readDelegationDateTimePickerValue(startAt)}\n />\n </BPMFormField>\n <BPMFormField label=\"結束時間\" name=\"endAt\">\n <DateTimePicker\n formatDate=\"YYYY-MM-DD\"\n formatTime=\"HH:mm\"\n fullWidth\n hideSecond\n onChange={(nextValue): void =>\n setEndAt(formatDelegationDateTimePickerValue(nextValue))\n }\n placeholderLeft=\"可留空\"\n placeholderRight=\"選擇時間\"\n value={readDelegationDateTimePickerValue(endAt)}\n />\n </BPMFormField>\n </div>\n </Modal>\n </>\n );\n}\n\nfunction MemberAutoCompleteField({\n label,\n loading,\n name,\n onChange,\n onSearch,\n options,\n value,\n}: {\n readonly label: string;\n readonly loading: boolean;\n readonly name: string;\n readonly onChange: (option: MemberOption | null) => void;\n readonly onSearch: (searchText: string) => Promise<void>;\n readonly options: readonly MemberOption[];\n readonly value: MemberOption | null;\n}): ReactElement {\n return (\n <BPMFormField\n label={label}\n layout={DELEGATION_MODAL_FIELD_LAYOUT}\n name={name}\n required\n >\n <AutoComplete\n asyncData\n disabledOptionsFilter\n emptyText=\"沒有符合的成員\"\n inputProps={{\n autoCapitalize: 'none',\n autoCorrect: 'off',\n name,\n spellCheck: false,\n }}\n loading={loading}\n loadingText=\"搜尋成員中...\"\n mode=\"single\"\n onChange={(option): void => onChange(readMemberOptionFromValue(option))}\n onSearch={onSearch}\n onSearchTextChange={(searchText): void =>\n onChange(readUniqueMemberOption(searchText, options))\n }\n onVisibilityChange={(open): void => {\n if (open) {\n void onSearch('');\n }\n }}\n options={[...options]}\n placeholder=\"搜尋姓名或信箱\"\n searchDebounceTime={300}\n value={value}\n />\n </BPMFormField>\n );\n}\n\nfunction MemberNameWithEmailTooltip({\n email,\n name,\n}: {\n readonly email: string | null;\n readonly name: string;\n}): ReactElement {\n if (!email) {\n return <span>{name}</span>;\n }\n\n return (\n <Tooltip title={email}>\n {({ ref, ...tooltipProps }): ReactElement => (\n // Spread the whole payload, as `TooltipProps` asks: picking only the\n // mouse handlers drops `aria-describedby`, so assistive tech never\n // learns the open tooltip describes this name.\n <span\n {...tooltipProps}\n className={styles.memberNameWithTooltip}\n ref={ref as RefCallback<HTMLSpanElement>}\n >\n {name}\n </span>\n )}\n </Tooltip>\n );\n}\n\nfunction readMemberOption(member: MemberProfileRecord): MemberOption {\n return {\n displayName: member.name,\n email: member.email,\n id: member.memberId,\n name: formatMemberLabel(member.name, member.email),\n };\n}\n\nfunction readMemberOptionFromValue(value: unknown): MemberOption | null {\n if (!isRecord(value)) {\n return null;\n }\n\n const displayName = value.displayName;\n const email = value.email;\n const id = value.id;\n const name = value.name;\n\n return typeof id === 'string' && typeof name === 'string'\n ? {\n displayName: typeof displayName === 'string' ? displayName : name,\n email: typeof email === 'string' ? email : null,\n id,\n name,\n }\n : null;\n}\n\nfunction readUniqueMemberOption(\n searchText: string,\n options: readonly MemberOption[],\n): MemberOption | null {\n const normalizedSearchText = searchText.trim().toLocaleLowerCase();\n\n if (!normalizedSearchText) {\n return null;\n }\n\n const matches = options.filter((option) =>\n [option.id, option.name, option.email ?? ''].some((value) =>\n value.toLocaleLowerCase().includes(normalizedSearchText),\n ),\n );\n\n return matches.length === 1 ? (matches[0] ?? null) : null;\n}\n\nfunction readTemplateOption(template: ApprovalTemplateRecord): TemplateOption {\n return {\n id: template.id,\n name: template.name,\n };\n}\n\nfunction readTemplateOptionsFromValue(\n value: readonly unknown[],\n): readonly TemplateOption[] {\n return value.flatMap((item): readonly TemplateOption[] => {\n if (!isRecord(item)) {\n return [];\n }\n\n const id = item.id;\n const name = item.name;\n\n return typeof id === 'string' && typeof name === 'string'\n ? [{ id, name }]\n : [];\n });\n}\n\nfunction filterTemplateOptions(\n options: readonly TemplateOption[],\n searchText: string,\n): readonly TemplateOption[] {\n const normalizedSearchText = searchText.trim().toLocaleLowerCase();\n\n if (!normalizedSearchText) {\n return options;\n }\n\n return options.filter((option) =>\n [option.id, option.name].some((value) =>\n value.toLocaleLowerCase().includes(normalizedSearchText),\n ),\n );\n}\n\nfunction mergeTemplateOptions(\n selectedOptions: readonly TemplateOption[],\n availableOptions: readonly TemplateOption[],\n): readonly TemplateOption[] {\n return [...selectedOptions, ...availableOptions].reduce<TemplateOption[]>(\n (options, option) =>\n options.some((currentOption) => currentOption.id === option.id)\n ? options\n : [...options, option],\n [],\n );\n}\n\nfunction readScopeFilterOption(value: unknown): ScopeFilterOption {\n if (!isRecord(value)) {\n return SCOPE_FILTER_OPTIONS[0];\n }\n\n const id = value.id;\n\n return (\n SCOPE_FILTER_OPTIONS.find((option) => option.id === id) ??\n SCOPE_FILTER_OPTIONS[0]\n );\n}\n\nfunction readScopeLabel(rule: DelegationRuleRecord): string {\n if (rule.scopeType === 'ALL') {\n return '全部簽核';\n }\n\n if (rule.scopeType === 'TEMPLATE_LIST') {\n return `指定模板:${rule.scopeTemplateIds.length}`;\n }\n\n return rule.scopeConditionCel ? `條件:${rule.scopeConditionCel}` : '條件式';\n}\n\nfunction readScopeOptionFromValue(value: unknown): ScopeOption {\n if (!isRecord(value) || !isSelectableDelegationScopeType(value.id)) {\n return SCOPE_OPTIONS[0];\n }\n\n return (\n SCOPE_OPTIONS.find((option) => option.id === value.id) ?? SCOPE_OPTIONS[0]\n );\n}\n\nfunction isSelectableDelegationScopeType(\n value: unknown,\n): value is ScopeOption['id'] {\n return value === 'ALL' || value === 'TEMPLATE_LIST';\n}\n\nfunction DelegationStatusBadge({\n status,\n}: {\n readonly status: DelegationRuleRecord['status'];\n}): ReactElement {\n if (status === 'ACTIVE') {\n return <Badge size=\"sub\" text=\"啟用中\" variant=\"dot-success\" />;\n }\n\n if (status === 'REVOKED') {\n return <Badge size=\"sub\" text=\"已撤銷\" variant=\"dot-inactive\" />;\n }\n\n if (status === 'EXPIRED') {\n return <Badge size=\"sub\" text=\"已過期\" variant=\"dot-warning\" />;\n }\n\n return <Badge size=\"sub\" text={status} variant=\"dot-info\" />;\n}\n\nfunction readDelegationStatusTabKey(activeKey: Key): DelegationStatusTabKey {\n if (\n activeKey === 'ACTIVE' ||\n activeKey === 'REVOKED' ||\n activeKey === 'EXPIRED'\n ) {\n return activeKey;\n }\n\n return 'ALL';\n}\n\nfunction readDelegationDateTimePickerValue(value: string): string | undefined {\n const date = value ? parseDelegationDateTimeValue(value) : null;\n\n return date\n ? `${formatDateParts(date)}T${padDatePart(date.getHours())}:${padDatePart(\n date.getMinutes(),\n )}`\n : undefined;\n}\n\nfunction formatDelegationDateTimePickerValue(\n value: string | undefined,\n): string {\n const date = value ? parseDelegationDateTimeValue(value) : null;\n\n return date ? date.toISOString() : '';\n}\n\nfunction isInvalidDelegationDateRange(startAt: string, endAt: string): boolean {\n if (!startAt || !endAt) {\n return false;\n }\n\n const startDate = parseDelegationDateTimeValue(startAt);\n const endDate = parseDelegationDateTimeValue(endAt);\n\n return !!startDate && !!endDate && endDate.getTime() <= startDate.getTime();\n}\n\nfunction parseDelegationDateTimeValue(value: string): Date | null {\n if (value.endsWith('Z') || /[+-]\\d{2}:\\d{2}$/.test(value)) {\n const parsedDate = new Date(value);\n\n return Number.isNaN(parsedDate.getTime()) ? null : parsedDate;\n }\n\n const [datePart = '', timePart = '00:00'] = value.split('T');\n const [year = 0, month = 1, day = 1] = datePart.split('-').map(Number);\n const [hour = 0, minute = 0] = timePart.split(':').map(Number);\n const parsedDate = new Date(year, month - 1, day, hour, minute);\n\n return Number.isNaN(parsedDate.getTime()) ? null : parsedDate;\n}\n\nfunction formatDateParts(date: Date): string {\n return `${date.getFullYear()}-${padDatePart(date.getMonth() + 1)}-${padDatePart(\n date.getDate(),\n )}`;\n}\n\nfunction padDatePart(value: number): string {\n return String(value).padStart(2, '0');\n}\n\nfunction readErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : '發生未知錯誤';\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === 'object' && value !== null;\n}\n"],"mappings":"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"}