@conduction/nextcloud-vue 2.111.0 → 2.113.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 (153) hide show
  1. package/dist/esm/components/CnActionButtons/CnActionButtons.vue.js +1 -1
  2. package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js +4 -6
  3. package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
  5. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  6. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +3 -2
  7. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  8. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +4 -2
  9. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  10. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js +1 -1
  11. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +3 -2
  12. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -1
  13. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js +2 -1
  14. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -1
  15. package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
  16. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +54 -5
  17. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  18. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +16 -2
  19. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  20. package/dist/esm/components/CnCardGrid/CnCardGrid.vue.js +1 -1
  21. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +10 -0
  22. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  23. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +2 -1
  24. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  25. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
  26. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +22 -2
  27. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  28. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +17 -3
  29. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  30. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  31. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  32. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  33. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
  34. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
  35. package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
  36. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  37. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +18 -3
  38. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  39. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -2
  40. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  41. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +52 -13
  42. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  43. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +31 -3
  44. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  45. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  46. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +48 -11
  47. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  48. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +160 -97
  49. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  50. package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
  51. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  52. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +2 -2
  53. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  54. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +222 -15
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  56. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +24 -7
  57. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  58. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js +1 -1
  59. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js +21 -1
  60. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js.map +1 -1
  61. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js.map +1 -1
  62. package/dist/esm/components/CnObjectCard/CnObjectCard.vue.js +1 -1
  63. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +36 -2
  64. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  65. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +44 -5
  66. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  67. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js +1 -0
  68. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js.map +1 -1
  69. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js +22 -2
  70. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js.map +1 -1
  71. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js +36 -16
  72. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js.map +1 -1
  73. package/dist/esm/components/CnReportsPage/CnReportsPage.vue.js +1 -1
  74. package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js +29 -12
  75. package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js.map +1 -1
  76. package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js +3 -1
  77. package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js.map +1 -1
  78. package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue.js +1 -1
  79. package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue2.js.map +1 -1
  80. package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue3.js.map +1 -1
  81. package/dist/esm/composables/useRefLabels.js +26 -6
  82. package/dist/esm/composables/useRefLabels.js.map +1 -1
  83. package/dist/esm/integrations/registry.js +4 -0
  84. package/dist/esm/integrations/registry.js.map +1 -1
  85. package/dist/esm/l10n/en.json.js +3 -1
  86. package/dist/esm/l10n/en.json.js.map +1 -1
  87. package/dist/esm/l10n/nl.json.js +3 -1
  88. package/dist/esm/l10n/nl.json.js.map +1 -1
  89. package/dist/esm/library-style.js +1 -1
  90. package/dist/esm/nextcloud-vue.css +680 -449
  91. package/dist/esm/store/plugins/liveUpdates.js +3 -1
  92. package/dist/esm/store/plugins/liveUpdates.js.map +1 -1
  93. package/dist/esm/store/plugins/logs.js +2 -1
  94. package/dist/esm/store/plugins/logs.js.map +1 -1
  95. package/dist/esm/utils/actionLink.js +104 -1
  96. package/dist/esm/utils/actionLink.js.map +1 -1
  97. package/dist/esm/utils/detailActionModel.js +28 -1
  98. package/dist/esm/utils/detailActionModel.js.map +1 -1
  99. package/dist/esm/utils/fillTemplate.js +13 -5
  100. package/dist/esm/utils/fillTemplate.js.map +1 -1
  101. package/dist/esm/utils/headerFieldChips.js +21 -2
  102. package/dist/esm/utils/headerFieldChips.js.map +1 -1
  103. package/dist/esm/utils/mapList.js +26 -11
  104. package/dist/esm/utils/mapList.js.map +1 -1
  105. package/dist/esm/utils/savedViewMenu.js +12 -5
  106. package/dist/esm/utils/savedViewMenu.js.map +1 -1
  107. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  108. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  109. package/dist/nextcloud-vue.cjs.js +2800 -1933
  110. package/dist/nextcloud-vue.cjs.js.map +2 -2
  111. package/dist/nextcloud-vue.css +680 -449
  112. package/l10n/en.json +3 -1
  113. package/l10n/nl.json +3 -1
  114. package/package.json +1 -1
  115. package/src/components/CnActionButtons/CnActionButtons.vue +4 -6
  116. package/src/components/CnActionDialog/CnActionDialog.vue +32 -9
  117. package/src/components/CnAssignDialog/CnAssignDialog.vue +4 -2
  118. package/src/components/CnBoardView/CnBoardView.vue +58 -5
  119. package/src/components/CnCardGrid/CnCardGrid.md +1 -0
  120. package/src/components/CnCardGrid/CnCardGrid.vue +11 -0
  121. package/src/components/CnCellRenderer/CnCellRenderer.vue +27 -3
  122. package/src/components/CnChartWidget/CnChartWidget.vue +6 -0
  123. package/src/components/CnChoiceCards/CnChoiceCards.vue +0 -3
  124. package/src/components/CnDashboardPage/CnDashboardPage.vue +20 -5
  125. package/src/components/CnDetailPage/CnDetailPage.vue +62 -14
  126. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +100 -5
  127. package/src/components/CnFormDialog/CnFormDialog.vue +1 -1
  128. package/src/components/CnIndexPage/CnIndexPage.vue +233 -18
  129. package/src/components/CnNcDashboardList/CnNcDashboardList.vue +21 -1
  130. package/src/components/CnObjectCard/CnObjectCard.md +1 -0
  131. package/src/components/CnObjectCard/CnObjectCard.vue +51 -5
  132. package/src/components/CnPartyPicker/CnPartyPicker.vue +62 -16
  133. package/src/components/CnReportsPage/CnReportsPage.vue +31 -12
  134. package/src/components/CnSearchSelect/CnSearchSelect.vue +8 -0
  135. package/src/composables/useRefLabels.js +25 -5
  136. package/src/css/badge.css +15 -13
  137. package/src/css/board.css +0 -1
  138. package/src/css/dialog.css +53 -3
  139. package/src/css/index-page.css +26 -1
  140. package/src/css/look-board-detail.css +36 -15
  141. package/src/css/look-board-index.css +67 -15
  142. package/src/css/look-board.css +32 -18
  143. package/src/integrations/registry.js +4 -0
  144. package/src/schemas/app-manifest-v2.schema.json +61 -13
  145. package/src/store/plugins/liveUpdates.js +3 -1
  146. package/src/store/plugins/logs.js +2 -1
  147. package/src/utils/actionLink.js +103 -0
  148. package/src/utils/detailActionModel.js +27 -0
  149. package/src/utils/fillTemplate.js +13 -5
  150. package/src/utils/headerFieldChips.js +21 -2
  151. package/src/utils/mapList.js +26 -11
  152. package/src/utils/savedViewMenu.js +12 -5
  153. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"CnActionButtons.vue3.js","sources":["../../../../src/components/CnActionButtons/CnActionButtons.vue?vue&type=template&id=49607313&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-action-buttons\"\n\t\t:class=\"{ 'cn-action-buttons--menu': display === 'menu' }\"\n\t\tdata-testid=\"cn-action-buttons\">\n\t\t<!-- `display: \"menu\"` keeps every dialog below but renders no buttons of\n\t\t its own: the host draws the entries as NcActionButtons inside an\n\t\t overflow menu instead, driven by the `entries` event. NcActions\n\t\t keeps only NcAction* vnodes out of its default slot, so this\n\t\t component cannot be placed in the menu itself.\n\t\t That mode is enforced in `barActions` / `overflowActions`, which\n\t\t both answer empty for it — so `inline` only means anything while\n\t\t this component is the one drawing. -->\n\t\t<template v-for=\"entry in barActions\">\n\t\t\t<!-- Toggle: a stateful two-way state button (GET on mount, write on\n\t\t\t click, optimistic + revert). Rendered inline, never dispatched. -->\n\t\t\t<NcButton\n\t\t\t\tv-if=\"entry.type === 'toggle'\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\t:variant=\"toggleState[entry.id] ? 'primary' : 'secondary'\"\n\t\t\t\t:disabled=\"Boolean(togglePending[entry.id])\"\n\t\t\t\t:data-testid=\"`cn-action-toggle-${entry.id}`\"\n\t\t\t\t:aria-pressed=\"String(Boolean(toggleState[entry.id]))\"\n\t\t\t\t@click=\"onToggleClick(entry)\">\n\t\t\t\t<template v-if=\"entry.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(entry.icon)\" :name=\"entry.icon\" :size=\"20\" />\n\t\t\t\t\t<span v-else :class=\"entry.icon\" />\n\t\t\t\t</template>\n\t\t\t\t{{ tr(toggleState[entry.id] ? (entry.labelOn || entry.label) : (entry.labelOff || entry.label)) }}\n\t\t\t</NcButton>\n\t\t\t<!-- An entry that goes to a URL is a LINK, not a button that calls\n\t\t\t the browser. NcButton renders an anchor when given `href`, so it\n\t\t\t keeps the cluster's styling while the browser supplies the\n\t\t\t pointer, the focus ring, middle-click and \"open in new tab\" —\n\t\t\t none of which a click handler can offer. -->\n\t\t\t<NcButton\n\t\t\t\tv-else-if=\"entry.href\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\t:variant=\"entry.variant || 'secondary'\"\n\t\t\t\t:href=\"entry.href\"\n\t\t\t\t:target=\"linkTargetOf(entry)\"\n\t\t\t\t:data-testid=\"`cn-action-${entry.id}`\"\n\t\t\t\t@click=\"onLinkClick(entry, $event)\">\n\t\t\t\t<template v-if=\"entry.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(entry.icon)\" :name=\"entry.icon\" :size=\"20\" />\n\t\t\t\t\t<span v-else :class=\"entry.icon\" />\n\t\t\t\t</template>\n\t\t\t\t{{ tr(entry.label) }}\n\t\t\t</NcButton>\n\t\t\t<!-- Everything else: a plain action button routed through the shared\n\t\t\t dispatcher (api-call / open-form / navigate / open-modal / refresh),\n\t\t\t confirm-gated first when the action asks for it. -->\n\t\t\t<!-- `variant`, NOT `type`. NcButton carries both: `type` is the\n\t\t\t native button type (default \"button\") and `variant` is the\n\t\t\t styling. Binding the descriptor's variant to `type` set an\n\t\t\t invalid native type and left `variant` at its \"secondary\"\n\t\t\t default, so `variant: \"primary\"` / `\"error\"` had never once\n\t\t\t rendered — silently, since an unknown native type is ignored. -->\n\t\t\t<NcButton\n\t\t\t\tv-else\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\t:variant=\"entry.variant || 'secondary'\"\n\t\t\t\t:disabled=\"Boolean(actionPending[entry.id])\"\n\t\t\t\t:data-testid=\"`cn-action-${entry.id}`\"\n\t\t\t\t@click=\"onActionClick(entry)\">\n\t\t\t\t<template v-if=\"entry.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(entry.icon)\" :name=\"entry.icon\" :size=\"20\" />\n\t\t\t\t\t<span v-else :class=\"entry.icon\" />\n\t\t\t\t</template>\n\t\t\t\t{{ tr(entry.label) }}\n\t\t\t</NcButton>\n\t\t\t<!-- Sub-actions. Its OWN chevron rather than entries folded into the\n\t\t\t overflow below, because NcActions cannot nest — and a grouped\n\t\t\t dropdown is the point of declaring children (a version list\n\t\t\t flattened into the page's one menu grows without bound). The\n\t\t\t parent's own button above still renders alongside this chevron —\n\t\t\t a parent meant only to group children needs a real `handler` (or\n\t\t\t `href`/other type) of its own, or its button dispatches nothing. -->\n\t\t\t<NcActions\n\t\t\t\tv-if=\"hasChildren(entry)\"\n\t\t\t\t:key=\"`${entry.id}-children`\"\n\t\t\t\t:menuName=\"childMenuName(entry)\"\n\t\t\t\t:data-testid=\"`cn-action-children-${entry.id}`\">\n\t\t\t\t<template v-for=\"child in visibleChildren(entry)\">\n\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\tv-if=\"child.href\"\n\t\t\t\t\t\t:key=\"`${child.id}-link`\"\n\t\t\t\t\t\t:href=\"child.href\"\n\t\t\t\t\t\t:target=\"linkTargetOf(child)\"\n\t\t\t\t\t\t:data-testid=\"`cn-action-${child.id}`\"\n\t\t\t\t\t\t@click=\"onLinkClick(child, $event)\">\n\t\t\t\t\t\t<template v-if=\"child.icon\" #icon>\n\t\t\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(child.icon)\" :name=\"child.icon\" :size=\"20\" />\n\t\t\t\t\t\t\t<span v-else :class=\"child.icon\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ tr(child.label) }}\n\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\t:key=\"child.id\"\n\t\t\t\t\t\t:disabled=\"Boolean(actionPending[child.id])\"\n\t\t\t\t\t\t:data-testid=\"`cn-action-${child.id}`\"\n\t\t\t\t\t\t@click=\"onActionClick(child)\">\n\t\t\t\t\t\t<template v-if=\"child.icon\" #icon>\n\t\t\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(child.icon)\" :name=\"child.icon\" :size=\"20\" />\n\t\t\t\t\t\t\t<span v-else :class=\"child.icon\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ tr(child.label) }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcActions>\n\t\t</template>\n\n\t\t<!-- Overflow. Only ever rendered when `inline` is set, so a consumer\n\t\t that never asked for a limit keeps every action as a button. -->\n\t\t<NcActions\n\t\t\tv-if=\"overflowActions.length > 0\"\n\t\t\t:menuName=\"overflowMenuName\"\n\t\t\t:forceMenu=\"true\"\n\t\t\tdata-testid=\"cn-action-buttons-overflow\">\n\t\t\t<template v-for=\"entry in overflowActions\">\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-if=\"entry.href\"\n\t\t\t\t\t:key=\"`${entry.id}-link`\"\n\t\t\t\t\t:href=\"entry.href\"\n\t\t\t\t\t:target=\"linkTargetOf(entry)\"\n\t\t\t\t\t:data-testid=\"`cn-action-${entry.id}`\"\n\t\t\t\t\t@click=\"onLinkClick(entry, $event)\">\n\t\t\t\t\t<template v-if=\"entry.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(entry.icon)\" :name=\"entry.icon\" :size=\"20\" />\n\t\t\t\t\t\t<span v-else :class=\"entry.icon\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ tr(entry.label) }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-else\n\t\t\t\t\t:key=\"entry.id\"\n\t\t\t\t\t:disabled=\"Boolean(actionPending[entry.id])\"\n\t\t\t\t\t:data-testid=\"`cn-action-${entry.id}`\"\n\t\t\t\t\t@click=\"onActionClick(entry)\">\n\t\t\t\t\t<template v-if=\"entry.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(entry.icon)\" :name=\"entry.icon\" :size=\"20\" />\n\t\t\t\t\t\t<span v-else :class=\"entry.icon\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ tr(entry.label) }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\t\t</NcActions>\n\n\t\t<!-- Confirm gate for a confirm:true action (reuses CnConfirmDialog). -->\n\t\t<CnConfirmDialog\n\t\t\tv-if=\"confirmEntry\"\n\t\t\tref=\"confirmDialog\"\n\t\t\t:dialogTitle=\"tr(confirmEntry.confirmTitle || confirmEntry.label)\"\n\t\t\t:message=\"tr(confirmEntry.confirmMessage) || defaultConfirmMessage\"\n\t\t\t:variant=\"confirmEntry.variant === 'error' ? 'error' : 'primary'\"\n\t\t\t@confirm=\"onConfirmProceed\"\n\t\t\t@close=\"confirmEntry = null\" />\n\n\t\t<!-- Schema-driven create dialog for an open-form action. The plain\n\t\t form is the default: the properties/JSON table is a power-user\n\t\t surface, and a header button that says \"New case\" is aimed at\n\t\t someone filing one, not at someone inspecting the schema. Set\n\t\t `advanced: true` on the action to get the table back. -->\n\t\t<CnFormDialog\n\t\t\tv-if=\"formEntry && formSchema && !formEntry.advanced\"\n\t\t\tref=\"formDialog\"\n\t\t\t:schema=\"formSchema\"\n\t\t\t:item=\"null\"\n\t\t\t:register=\"formRegister\"\n\t\t\t:initialData=\"formInitialValues || {}\"\n\t\t\t:dialogTitle=\"tr(formEntry.formTitle) || ''\"\n\t\t\t:eyebrow=\"tr(formEntry.formEyebrow) || ''\"\n\t\t\t:subtitle=\"tr(formEntry.formSubtitle) || ''\"\n\t\t\t:includeFields=\"formEntry.includeFields || null\"\n\t\t\t:excludeFields=\"formEntry.excludeFields || []\"\n\t\t\t:fieldOverrides=\"formEntry.fieldOverrides || {}\"\n\t\t\t:size=\"formEntry.size || 'normal'\"\n\t\t\t:columns=\"formEntry.columns || 1\"\n\t\t\t@confirm=\"onFormConfirm\"\n\t\t\t@close=\"closeForm\" />\n\t\t<CnAdvancedFormDialog\n\t\t\tv-if=\"formEntry && formSchema && formEntry.advanced\"\n\t\t\tref=\"formDialog\"\n\t\t\t:schema=\"formSchema\"\n\t\t\t:item=\"null\"\n\t\t\t:initialValues=\"formInitialValues\"\n\t\t\t@confirm=\"onFormConfirm\"\n\t\t\t@close=\"closeForm\" />\n\n\t\t<!-- run-node's config dialog (manifest-run-node-action): only mounted\n\t\t when the target node declared at least one field AND that\n\t\t describe call succeeded — an empty/no form runs immediately with\n\t\t no dialog at all (openRunNode below), and a describe FAILURE also\n\t\t skips the dialog (toasts instead) rather than showing an empty,\n\t\t confusing one. -->\n\t\t<CnRunNodeDialog\n\t\t\tv-if=\"runNodeEntry && runNodeFields.length\"\n\t\t\t:title=\"tr(runNodeEntry.formTitle) || tr(runNodeEntry.label)\"\n\t\t\t:fields=\"runNodeFields\"\n\t\t\t:loading=\"Boolean(actionPending[runNodeEntry.id])\"\n\t\t\t:translate=\"effectiveTranslate\"\n\t\t\t@confirm=\"onRunNodeConfirm\"\n\t\t\t@close=\"closeRunNode\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionLink, NcActions, NcButton } from '@nextcloud/vue'\nimport { inject } from 'vue'\nimport CnConfirmDialog from '../../dialogs/CnConfirmDialog.vue'\nimport CnRunNodeDialog from '../../dialogs/CnRunNodeDialog.vue'\nimport { fetchEndpointSource } from '../../composables/useEndpointSource.js'\nimport { useObjectStore } from '../../store/useObjectStore.js'\nimport { actionLink, buildOnSuccessRoute, dispatchAction, interpolateActionTarget, isExternalActionTarget, postRunNode, resolveCreateOverrideHandler, resolveObjectOpType } from '../../utils/actionsDispatcher.js'\nimport { resolveObjectTokenContext } from '../../utils/detailObjectContext.js'\nimport { usesArrayValues, valueArrayFor, valueRecordsFor } from '../../utils/dynamicProperties.js'\nimport { followLinkClick } from '../../utils/linkNavigation.js'\nimport { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { evaluateVisibleWhen } from '../../utils/visibleWhen.js'\nimport { CnAdvancedFormDialog } from '../CnAdvancedFormDialog/index.js'\nimport { CnFormDialog } from '../CnFormDialog/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnActionButtons — declarative header-actions surface (#91 Wave 3).\n *\n * Renders a page's `headerActions[]` as buttons and owns the two action\n * behaviours that need STATE or a DIALOG the one-shot `dispatchAction`\n * can't provide on its own:\n *\n * - **`open-form`** — mounts the shared `CnAdvancedFormDialog` for the\n * action's `schema` (fetched through the object store), saves on\n * confirm, toasts, refreshes the page, and optionally navigates to\n * `onSuccessRoute`. Two optional keys cover the schemas a bare create\n * cannot satisfy: `props` seeds fixed field values into the create form\n * (so one schema can back several buttons, each fixing its own\n * discriminator), and `createOverride` names a registry handler that owns\n * the persist instead of `objectStore.saveObject` (for a schema requiring\n * a server-minted field the form cannot supply). `includeFields` /\n * `excludeFields` / `fieldOverrides` narrow what the button asks for, which\n * is how one schema serves both a full editor and a quick-create button.\n * `advanced: true` swaps in the properties/JSON table for a power user.\n * - **`toggle`** — a two-way state button: `GET`s `stateSource` on mount,\n * renders `labelOn` / `labelOff`, and on click `writes` the flipped\n * value OPTIMISTICALLY, reverting on failure.\n * - **`run-node`** (manifest-run-node-action) — GETs the target flow node's\n * describe endpoint (OpenRegister's `or-flow-run-node`); a node with\n * declared fields mounts `CnRunNodeDialog` (its own small field renderer —\n * see that component's docblock for why it is not `CnFlowNodeEditModal`'s),\n * a node with none runs immediately. Either way `postRunNode()`\n * (`actionsDispatcher.js`) makes the actual call, subject-token-resolved\n * exactly like `agent`.\n *\n * Every other type (`api-call`, `navigate`, `open-modal`, `open-page`,\n * `refresh`, `handler`) routes through the shared `dispatchAction` — with\n * a `CnConfirmDialog` gate first when the action sets `confirm: true`\n * (the object-op precedent: the dispatcher runs AFTER confirmation).\n *\n * Each action may carry a `visibleWhen` predicate (the shared banner\n * shape — `{ endpoint | source | field, op, value }`) evaluated against\n * the page / object context; a hidden action simply doesn't render.\n *\n * Placement: CnDashboardPage / CnDetailPage / CnPageRenderer mount this in\n * their header-actions area, passing the page's `headerActions[]`. The\n * object context (`{ objectId, object, register, schema }`) and the page\n * workspace / app-config bags are injected so tokens + local `visibleWhen`\n * resolve against the current record and page state.\n *\n * ```json\n * \"headerActions\": [\n * { \"id\": \"new-lead\", \"label\": \"New lead\", \"type\": \"open-form\",\n * \"register\": \"crm\", \"schema\": \"lead\", \"onSuccessRoute\": \"Leads\", \"variant\": \"primary\" },\n * { \"id\": \"approve\", \"label\": \"Approve\", \"type\": \"api-call\",\n * \"url\": \"/apps/shillinq/api/payment-runs/@objectId/approve\", \"confirm\": true,\n * \"visibleWhen\": { \"field\": \"state\", \"op\": \"eq\", \"value\": \"pending\" } },\n * { \"id\": \"toggle-open\", \"type\": \"toggle\", \"labelOn\": \"Open\", \"labelOff\": \"Closed\",\n * \"stateSource\": { \"url\": \"/apps/pipelinq/api/werkplek/@objectId/state\", \"responsePath\": \"open\" },\n * \"field\": \"open\", \"writeUrl\": \"/apps/pipelinq/api/werkplek/@objectId/state\", \"method\": \"PUT\" }\n * ]\n * ```\n */\nexport default {\n\tname: 'CnActionButtons',\n\n\tcomponents: { NcActions, NcActionButton, NcActionLink, NcButton, CnIcon, CnConfirmDialog, CnFormDialog, CnAdvancedFormDialog, CnRunNodeDialog },\n\n\tinject: {\n\t\t/** Detail-page object context (`{ objectId, object, register, schema }`). */\n\t\tcnObjectContext: { default: null },\n\t\t/** v2 slot-grid detail context holder (CnPageRenderer). */\n\t\tcnDetailObjectContext: { default: null },\n\t\t/** Page-level workspace context bag (reactive). */\n\t\tcnWorkspaceContext: { default: () => ({}) },\n\t\t/** Page-level app config bag (reactive). */\n\t\tcnAppConfig: { default: () => ({}) },\n\t\t/** Pre-bound dispatchAction from CnPageRenderer (router/registry/handlers/openModal wired). */\n\t\tcnDispatchAction: { default: null },\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). The\n\t\t * manifest-authored action labels, confirm copy and api-call toasts\n\t\t * are run through it. Defaults to an identity function so an\n\t\t * untranslated key renders as itself.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** v2 component registry, for resolving an `open-form` createOverride. */\n\t\tcnRegistry: { default: () => ({}) },\n\t\t/** Legacy customComponents map, same resolution fallback. */\n\t\tcnCustomComponents: { default: () => ({}) },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The page's declarative header actions. Each entry is a typed action\n\t\t * (`open-form` | `toggle` | `api-call` | `navigate` | `open-modal` |\n\t\t * `refresh` | `handler`) plus `id` / `label` and an optional\n\t\t * `visibleWhen` predicate, `icon`, `variant`, and `confirm`.\n\t\t *\n\t\t * An entry carrying `href` (with optional `target`) renders as a real\n\t\t * link instead — an anchor in the bar, an NcActionLink in a menu — and\n\t\t * is never dispatched. Use it for anything that ends in a URL, so the\n\t\t * browser keeps middle-click, \"open in new tab\" and the link semantics\n\t\t * assistive tech announces.\n\t\t *\n\t\t * `href` is JS-composition-only, like `onSelect` — manifest JSON cannot\n\t\t * carry it (the schema's `target` field already names the `navigate`\n\t\t * destination URL, and `additionalProperties: false` rejects an\n\t\t * unlisted `href`). A manifest author reaching for a link should use\n\t\t * `type: \"navigate\"` with an external `target`, which auto-derives the\n\t\t * anchor/`window.open` behaviour instead.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * How many collapsible actions stay as buttons; the rest fall into a\n\t\t * single `···` overflow menu.\n\t\t *\n\t\t * `0` (the default) renders EVERY action as a button, which is what\n\t\t * every existing consumer already gets — the overflow only ever\n\t\t * appears once a host asks for a limit.\n\t\t *\n\t\t * A `variant: \"primary\"` action, an action carrying `children`, and a\n\t\t * `toggle` are never collapsed and never consume one of the N slots:\n\t\t * NcActions paints all of its inline actions with one shared variant\n\t\t * (so a lone primary among them is not expressible), it cannot nest\n\t\t * another NcActions, and a toggle's state lives in its button.\n\t\t *\n\t\t * @type {number}\n\t\t */\n\t\tinline: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0,\n\t\t},\n\n\t\t/**\n\t\t * Name for the overflow menu. Empty falls back to a translated\n\t\t * \"Actions\".\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\toverflowLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Explicit Vue Router instance for `navigate` / `open-page` /\n\t\t * `onSuccessRoute`. Falls back to `this.$router`. Only needed for\n\t\t * standalone mounts outside a router tree.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\trouter: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Where the actions are drawn. `buttons` (the default) puts one\n\t\t * NcButton per action in the host's header. `menu` draws none and\n\t\t * emits `entries` instead, so the host can render them inside an\n\t\t * overflow menu while this component keeps owning the dialogs.\n\t\t *\n\t\t * @type {'buttons'|'menu'}\n\t\t */\n\t\tdisplay: {\n\t\t\ttype: String,\n\t\t\tdefault: 'buttons',\n\t\t\tvalidator: (v) => ['buttons', 'menu'].includes(v),\n\t\t},\n\t},\n\n\temits: ['created', 'entries'],\n\n\tsetup() {\n\t\t// Read the two detail-surface injects once so the object token context\n\t\t// resolves identically to the endpoint-bound widgets (Wave 3).\n\t\tconst objectCtxRaw = inject('cnObjectContext', null)\n\t\tconst detailCtxRaw = inject('cnDetailObjectContext', null)\n\t\tconst workspaceRaw = inject('cnWorkspaceContext', null)\n\t\tconst appConfigRaw = inject('cnAppConfig', null)\n\t\treturn { objectCtxRaw, detailCtxRaw, workspaceRaw, appConfigRaw }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Evaluated visibleWhen outcome per action id (true when no predicate). */\n\t\t\tvisibility: {},\n\t\t\t/** Current boolean state per toggle action id. */\n\t\t\ttoggleState: {},\n\t\t\t/** In-flight flag per toggle action id (disables the button). */\n\t\t\ttogglePending: {},\n\t\t\t/** In-flight flag per non-toggle action id. */\n\t\t\tactionPending: {},\n\t\t\t/** The action awaiting confirmation (null = no dialog). */\n\t\t\tconfirmEntry: null,\n\t\t\t/** The open-form action currently showing its dialog (null = closed). */\n\t\t\tformEntry: null,\n\t\t\t/** The fetched schema object for the open-form dialog (null until loaded). */\n\t\t\tformSchema: null,\n\t\t\t/** The run-node action currently in flight / showing its dialog (null = closed). */\n\t\t\trunNodeEntry: null,\n\t\t\t/** The target node's declared config-form fields, from the describe GET. */\n\t\t\trunNodeFields: [],\n\t\t\t/** Default confirm-dialog message. */\n\t\t\tdefaultConfirmMessage: t('nextcloud-vue', 'Are you sure you want to continue?'),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Effective translate function: the injected `cnTranslate` (the host\n\t\t * app's bound `t()`), identity by default.\n\t\t *\n\t\t * @return {(key: string) => string}\n\t\t */\n\t\teffectiveTranslate() {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key\n\t\t},\n\n\t\t/**\n\t\t * Create-form seed values for the open `open-form` action. An action\n\t\t * declares them as `props`, which is how a single schema backs several\n\t\t * buttons — \"New request\" and \"New complaint\" both open the `ticket`\n\t\t * form, each fixing its own `ticketType`.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tformInitialValues() {\n\t\t\tconst props = this.formEntry && this.formEntry.props\n\t\t\tif (!props || typeof props !== 'object' || Array.isArray(props)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Seed values go through the SAME token grammar as filters, so an\n\t\t\t// action on a detail page can stamp the record it belongs to:\n\t\t\t// `{ \"domainObjectRef\": \"@objectId\" }`. Without this the literal\n\t\t\t// string \"@objectId\" is saved, and a foreign key pointing at nothing\n\t\t\t// is a defect that only shows up in whatever reads it later.\n\t\t\treturn resolveFilterTokens(props, this.tokenCtx)\n\t\t},\n\n\t\t/**\n\t\t * The register the open dialog reads and writes against. The plain\n\t\t * form needs it to resolve `$ref` pickers into real dropdowns; without\n\t\t * one a reference field renders as an empty select and the action is\n\t\t * unusable for exactly the schemas that need it most.\n\t\t *\n\t\t * @return {string} The register slug, '' when none is resolvable.\n\t\t */\n\t\tformRegister() {\n\t\t\treturn (this.formEntry && this.formEntry.register) || this.objectCtx.register || ''\n\t\t},\n\n\t\t/**\n\t\t * The actions whose visibleWhen evaluated true (or carry no predicate).\n\t\t *\n\t\t * A `navigate` action whose target leaves the app is turned into a link\n\t\t * entry here, so it takes the `href` branch above instead of being\n\t\t * dispatched. An absolute URL is not a route: pushing one at the router\n\t\t * matched nothing and landed on the fallback page, carrying the URL's\n\t\t * own query string with it. The action's `target` (the URL) is replaced\n\t\t * by the anchor's `target`, since the two fields share a name and the\n\t\t * URL now lives in `href`. An author's explicit `href` still wins.\n\t\t *\n\t\t * An in-app `navigate` or `open-page` becomes a link too (see\n\t\t * `toLinkEntry`), keeping its own `target` so a fallback dispatch works.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn (this.actions || [])\n\t\t\t\t.filter((a) => a && a.id && this.visibility[a.id] !== false)\n\t\t\t\t.map((a) => this.toLinkEntry(a))\n\t\t},\n\n\t\t/**\n\t\t * The actions rendered in the bar itself: every non-collapsible one,\n\t\t * plus the first `inline` collapsible ones. Declaration order is kept,\n\t\t * so promoting an action never reshuffles the row.\n\t\t *\n\t\t * Empty in `display: \"menu\"` — the host draws the entries itself there,\n\t\t * so this component renders no buttons and has nothing to cap.\n\t\t *\n\t\t * @return {Array<object>} Actions to render as buttons / chevrons.\n\t\t */\n\t\tbarActions() {\n\t\t\tif (this.display === 'menu') {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tif (!(this.inline > 0)) {\n\t\t\t\treturn this.visibleActions\n\t\t\t}\n\t\t\tconst promoted = new Set(this.visibleActions\n\t\t\t\t.filter((a) => this.isCollapsible(a))\n\t\t\t\t.slice(0, this.inline)\n\t\t\t\t.map((a) => a.id))\n\t\t\treturn this.visibleActions.filter((a) => !this.isCollapsible(a) || promoted.has(a.id))\n\t\t},\n\n\t\t/**\n\t\t * The actions that did not fit, for the `···` menu. Always empty while\n\t\t * `inline` is 0, and in `display: \"menu\"` where the host owns the menu.\n\t\t *\n\t\t * @return {Array<object>} Actions to render as menu items.\n\t\t */\n\t\toverflowActions() {\n\t\t\tif (this.display === 'menu' || !(this.inline > 0)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst shown = new Set(this.barActions.map((a) => a.id))\n\t\t\treturn this.visibleActions.filter((a) => !shown.has(a.id))\n\t\t},\n\n\t\t/**\n\t\t * Name for the overflow trigger.\n\t\t *\n\t\t * @return {string} The label.\n\t\t */\n\t\toverflowMenuName() {\n\t\t\treturn this.tr(this.overflowLabel) || t('nextcloud-vue', 'Actions')\n\t\t},\n\n\t\t/**\n\t\t * The visible actions flattened for a host that draws them as menu\n\t\t * items. Everything the item needs to render is resolved here — the\n\t\t * translated label a toggle currently shows, its pressed state, its\n\t\t * pending flag, and which of the two icon shapes applies — so the host\n\t\t * template stays a plain v-for and needs none of this component's\n\t\t * state. `run()` is pre-bound, the way CnIndexPage pre-binds a header\n\t\t * action's handler for the index bar, so the host dispatches without\n\t\t * reaching back through a ref.\n\t\t *\n\t\t * @return {Array<object>} One menu-ready descriptor per visible action.\n\t\t */\n\t\tmenuEntries() {\n\t\t\treturn this.visibleActions.map((entry) => {\n\t\t\t\tconst isToggle = entry.type === 'toggle'\n\t\t\t\tconst on = Boolean(this.toggleState[entry.id])\n\t\t\t\tconst label = isToggle\n\t\t\t\t\t? (on ? (entry.labelOn || entry.label) : (entry.labelOff || entry.label))\n\t\t\t\t\t: entry.label\n\t\t\t\treturn {\n\t\t\t\t\tid: entry.id,\n\t\t\t\t\tlabel: this.tr(label),\n\t\t\t\t\t// An mdi name goes to CnIcon; a legacy `icon-*` class goes\n\t\t\t\t\t// on a bare span. Deciding it here keeps isMdiIconName from\n\t\t\t\t\t// having to be duplicated into every host.\n\t\t\t\t\ticonName: this.isMdiIconName(entry.icon) ? entry.icon : '',\n\t\t\t\t\ticonClass: (entry.icon && !this.isMdiIconName(entry.icon)) ? entry.icon : '',\n\t\t\t\t\tdisabled: Boolean(isToggle ? this.togglePending[entry.id] : this.actionPending[entry.id]),\n\t\t\t\t\t// null, not false: a non-toggle carries no pressed state at\n\t\t\t\t\t// all, and `aria-pressed=\"false\"` would announce every\n\t\t\t\t\t// action as an un-pressed toggle button.\n\t\t\t\t\tpressed: isToggle ? on : null,\n\t\t\t\t\ttestid: isToggle ? `cn-action-toggle-${entry.id}` : `cn-action-${entry.id}`,\n\t\t\t\t\t// A link entry, for a host to render as an anchor rather\n\t\t\t\t\t// than a button with a click handler. Named `linkTarget`\n\t\t\t\t\t// because an action's own `target` is its destination. An in-app\n\t\t\t\t\t// link (`linkTo`) is left to `run()`: a host anchor has no router.\n\t\t\t\t\thref: (!entry.linkTo && entry.href) || '',\n\t\t\t\t\tlinkTarget: (!entry.linkTo && entry.href) ? (entry.target || '_blank') : '',\n\t\t\t\t\trun: () => (isToggle ? this.onToggleClick(entry) : this.onActionClick(entry)),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** Unwrapped page workspace bag. */\n\t\tworkspaceCtx() {\n\t\t\tconst c = this.workspaceRaw\n\t\t\tconst v = (c && typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t\treturn (v && typeof v === 'object') ? v : {}\n\t\t},\n\n\t\t/** Unwrapped app-config bag. */\n\t\tconfigCtx() {\n\t\t\tconst c = this.appConfigRaw\n\t\t\tconst v = (c && typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t\treturn (v && typeof v === 'object') ? v : {}\n\t\t},\n\n\t\t/** The merged object token context (both detail-surface injects). */\n\t\tobjectCtx() {\n\t\t\treturn resolveObjectTokenContext(this.objectCtxRaw, this.detailCtxRaw) || {}\n\t\t},\n\n\t\t/** The full token context for url/param interpolation + local visibleWhen. */\n\t\ttokenCtx() {\n\t\t\treturn {\n\t\t\t\t...this.objectCtx,\n\t\t\t\tworkspace: this.workspaceCtx,\n\t\t\t\tconfig: this.configCtx,\n\t\t\t}\n\t\t},\n\n\t\t/** The effective router (explicit prop wins, else this.$router). */\n\t\teffectiveRouter() {\n\t\t\treturn this.router || this.$router || null\n\t\t},\n\t},\n\n\twatch: {\n\t\tactions: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.evaluateVisibility()\n\t\t\t\tthis.initToggles()\n\t\t\t},\n\t\t},\n\n\t\t// Re-evaluate local (object-context) visibleWhen when the record loads.\n\t\tobjectCtx: {\n\t\t\tdeep: true,\n\t\t\thandler() { this.evaluateVisibility() },\n\t\t},\n\n\t\t// Push the menu-ready descriptors at a `display: \"menu\"` host. It fires\n\t\t// immediately so the host has the list on first paint, and again on\n\t\t// every change of visibility, toggle state or pending flag — the three\n\t\t// things that would otherwise leave a stale item in an open menu.\n\t\tmenuEntries: {\n\t\t\timmediate: true,\n\t\t\thandler(entries) {\n\t\t\t\tif (this.display === 'menu') {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event entries Emitted in `display: \"menu\"` only, whenever the visible actions, their toggle state or their pending flags change. Payload: one menu-ready descriptor per visible action, each carrying `id`, `label`, `iconName`, `iconClass`, `disabled`, `pressed`, `testid` and a pre-bound `run()`.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('entries', entries)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Heuristic MDI-name check (an `icon-`-prefixed string is a CSS class).\n\t\t *\n\t\t * @param {string} name The icon string.\n\t\t * @return {boolean} True when it should render via CnIcon.\n\t\t */\n\t\tisMdiIconName(name) {\n\t\t\treturn typeof name === 'string' && name !== '' && !name.startsWith('icon-')\n\t\t},\n\n\t\t/**\n\t\t * Evaluate every action's `visibleWhen` against the current context.\n\t\t * Fail-safe (a broken predicate hides its action); actions without a\n\t\t * predicate are always visible.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync evaluateVisibility() {\n\t\t\t// Children are walked too: a sub-action whose predicate was never\n\t\t\t// evaluated would read as visible and render regardless.\n\t\t\tconst flat = []\n\t\t\tfor (const action of this.actions || []) {\n\t\t\t\tif (!action || !action.id) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tflat.push(action)\n\t\t\t\tif (Array.isArray(action.children)) {\n\t\t\t\t\tflat.push(...action.children.filter((c) => c && c.id))\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const action of flat) {\n\t\t\t\tif (!action.visibleWhen) {\n\t\t\t\t\tthis.visibility[action.id] = true\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst ok = await evaluateVisibleWhen(action.visibleWhen, this.tokenCtx)\n\t\t\t\tthis.visibility[action.id] = ok\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Seed each toggle action's state from its `stateSource` endpoint\n\t\t * (fetched through the shared endpoint engine so `@objectId` etc.\n\t\t * resolve). No-op for non-toggle actions.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync initToggles() {\n\t\t\tfor (const action of this.actions || []) {\n\t\t\t\tif (!action || action.type !== 'toggle' || !action.id) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.toggleState[action.id] = false\n\t\t\t\tif (!action.stateSource || !action.stateSource.url) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst payload = await fetchEndpointSource(action.stateSource, this.tokenCtx)\n\t\t\t\t\tconst value = action.field ? this.readField(payload, action.field) : payload\n\t\t\t\t\tthis.toggleState[action.id] = Boolean(value)\n\t\t\t\t} catch {\n\t\t\t\t\t// Leave the default (off); a failed state read never breaks the bar.\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an action may be pushed into the overflow menu. See the\n\t\t * `inline` prop for why these three shapes never can be.\n\t\t *\n\t\t * @param {object} entry The action descriptor.\n\t\t * @return {boolean} True when it can collapse.\n\t\t */\n\t\tisCollapsible(entry) {\n\t\t\treturn !this.hasChildren(entry)\n\t\t\t\t&& entry.variant !== 'primary'\n\t\t\t\t&& entry.type !== 'toggle'\n\t\t},\n\n\t\t/**\n\t\t * Whether an action declares sub-actions worth a chevron.\n\t\t *\n\t\t * @param {object} entry The action descriptor.\n\t\t * @return {boolean} True when it has at least one visible child.\n\t\t */\n\t\thasChildren(entry) {\n\t\t\treturn this.visibleChildren(entry).length > 0\n\t\t},\n\n\t\t/**\n\t\t * An action's sub-actions, minus any hidden by `visibleWhen`.\n\t\t *\n\t\t * @param {object} entry The action descriptor.\n\t\t * @return {Array<object>} The renderable children.\n\t\t */\n\t\tvisibleChildren(entry) {\n\t\t\tif (!Array.isArray(entry?.children)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn entry.children\n\t\t\t\t.filter((c) => c && c.id && this.visibility[c.id] !== false)\n\t\t\t\t.map((c) => this.toLinkEntry(c))\n\t\t},\n\n\t\t/**\n\t\t * Give a navigating action the `href` it renders as. An external\n\t\t * `navigate` target becomes the href with a `_blank` anchor target. An\n\t\t * in-app `navigate` / `open-page` gets the router-resolved href plus\n\t\t * `linkTo`, the location a plain click routes to; one with `confirm`,\n\t\t * an `onSelect` function, or an unresolvable route stays a button.\n\t\t *\n\t\t * @param {object} a The action descriptor.\n\t\t * @return {object} The descriptor, possibly with `href` / `linkTo` added.\n\t\t */\n\t\ttoLinkEntry(a) {\n\t\t\tif (a.href || (a.type !== 'navigate' && a.type !== 'open-page')) {\n\t\t\t\treturn a\n\t\t\t}\n\t\t\tlet entry = a\n\t\t\tif (a.type === 'navigate') {\n\t\t\t\t// Interpolated BEFORE the external test and before it becomes an\n\t\t\t\t// href, so the anchor carries the record's id rather than the token.\n\t\t\t\tconst target = interpolateActionTarget(a.target, this.tokenCtx)\n\t\t\t\tif (isExternalActionTarget(target)) {\n\t\t\t\t\treturn { ...a, href: target, target: '_blank' }\n\t\t\t\t}\n\t\t\t\tentry = target === a.target ? a : { ...a, target }\n\t\t\t}\n\t\t\tif (typeof entry.onSelect === 'function') {\n\t\t\t\treturn entry\n\t\t\t}\n\t\t\tconst link = actionLink(entry, { router: this.effectiveRouter })\n\t\t\treturn link ? { ...entry, href: link.href, linkTo: link.to } : entry\n\t\t},\n\n\t\t/**\n\t\t * The anchor `target` of a link entry. An in-app link opens in place;\n\t\t * its own `target` is the destination, not an anchor target.\n\t\t *\n\t\t * @param {object} entry The link entry.\n\t\t * @return {string|undefined} The anchor target.\n\t\t */\n\t\tlinkTargetOf(entry) {\n\t\t\treturn entry.linkTo ? undefined : (entry.target || undefined)\n\t\t},\n\n\t\t/**\n\t\t * Click on a link entry: a plain click on an in-app link routes in\n\t\t * place; a URL or a modified click is left to the browser.\n\t\t *\n\t\t * @param {object} entry The link entry.\n\t\t * @param {MouseEvent} event The click event.\n\t\t * @return {void}\n\t\t */\n\t\tonLinkClick(entry, event) {\n\t\t\tif (entry.linkTo) {\n\t\t\t\tfollowLinkClick(event, entry.linkTo, this.effectiveRouter)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Name for a sub-action chevron. `childrenLabel` when set, else the\n\t\t * parent's own label, so the dropdown is never announced unlabelled.\n\t\t *\n\t\t * @param {object} entry The action descriptor.\n\t\t * @return {string} The menu name.\n\t\t */\n\t\tchildMenuName(entry) {\n\t\t\treturn this.tr(entry.childrenLabel) || this.tr(entry.label) || ''\n\t\t},\n\n\t\t/**\n\t\t * Read a dot-path off a payload (the toggle state field).\n\t\t *\n\t\t * @param {unknown} data The payload.\n\t\t * @param {string} field The dot-path.\n\t\t * @return {unknown} The value at the path.\n\t\t */\n\t\treadField(data, field) {\n\t\t\tif (!field) {\n\t\t\t\treturn data\n\t\t\t}\n\t\t\treturn String(field).split('.').reduce((o, k) => (o === null || o === undefined ? o : o[k]), data)\n\t\t},\n\n\t\t/**\n\t\t * Toggle click: flip the state OPTIMISTICALLY, write it, and revert on\n\t\t * failure. Success/error toast via the api-call dispatch path (which\n\t\t * also refreshes the page unless `refresh: false`).\n\t\t *\n\t\t * @param {object} entry The toggle action.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onToggleClick(entry) {\n\t\t\tif (this.togglePending[entry.id]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst previous = Boolean(this.toggleState[entry.id])\n\t\t\tconst next = !previous\n\t\t\tthis.toggleState[entry.id] = next\n\t\t\tthis.togglePending[entry.id] = true\n\t\t\tconst writeParams = { ...(entry.params || {}) }\n\t\t\tif (entry.field) {\n\t\t\t\twriteParams[entry.field] = next\n\t\t\t}\n\t\t\tconst result = await this.dispatch({\n\t\t\t\ttype: 'api-call',\n\t\t\t\turl: entry.writeUrl,\n\t\t\t\tmethod: entry.method || 'PUT',\n\t\t\t\tparams: writeParams,\n\t\t\t\tsuccessMessage: entry.successMessage,\n\t\t\t\terrorMessage: entry.errorMessage,\n\t\t\t\trefresh: entry.refresh,\n\t\t\t})\n\t\t\tif (!result || result.ok === false) {\n\t\t\t\t// Revert the optimistic flip.\n\t\t\t\tthis.toggleState[entry.id] = previous\n\t\t\t}\n\t\t\tthis.togglePending[entry.id] = false\n\t\t},\n\n\t\t/**\n\t\t * Non-toggle action click: gate on `confirm` first, else run.\n\t\t *\n\t\t * @param {object} entry The action.\n\t\t * @return {void}\n\t\t */\n\t\tonActionClick(entry) {\n\t\t\tif (entry.confirm === true) {\n\t\t\t\tthis.confirmEntry = entry\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.runAction(entry)\n\t\t},\n\n\t\t/**\n\t\t * The confirm dialog's proceed handler: run the pending action, then\n\t\t * report the outcome back to the dialog's result phase.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onConfirmProceed() {\n\t\t\tconst entry = this.confirmEntry\n\t\t\tif (!entry) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst result = await this.runAction(entry)\n\t\t\tconst dialog = this.$refs.confirmDialog\n\t\t\tif (dialog && typeof dialog.setResult === 'function') {\n\t\t\t\tconst ok = result === undefined || (result && result.ok !== false)\n\t\t\t\tdialog.setResult(ok ? { success: true } : { error: t('nextcloud-vue', 'Action failed.') })\n\t\t\t} else {\n\t\t\t\tthis.confirmEntry = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Run an action (post-confirmation). `open-form` opens the schema\n\t\t * dialog; everything else dispatches. Returns the dispatch result so\n\t\t * the confirm dialog can report success/failure.\n\t\t *\n\t\t * @param {object} entry The action.\n\t\t * @return {Promise<unknown>} The dispatch result (undefined for open-form).\n\t\t */\n\t\tasync runAction(entry) {\n\t\t\t// A function on the descriptor, for a PARENT COMPONENT composing\n\t\t\t// entries in JS (CnDetailPage folds its own Edit in this way).\n\t\t\t// Manifest JSON cannot carry one, so the declarative path is\n\t\t\t// untouched — and it runs after the confirm gate, like every\n\t\t\t// dispatched type.\n\t\t\tif (typeof entry.onSelect === 'function') {\n\t\t\t\tentry.onSelect(entry)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tif (entry.type === 'open-form') {\n\t\t\t\tawait this.openForm(entry)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tif (entry.type === 'run-node') {\n\t\t\t\tawait this.openRunNode(entry)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tthis.actionPending[entry.id] = true\n\t\t\tconst result = await this.dispatch(entry)\n\t\t\tthis.actionPending[entry.id] = false\n\t\t\treturn result\n\t\t},\n\n\t\t/**\n\t\t * Resolve a manifest-authored UI string through the host translate\n\t\t * function. A pure pass-through: an empty value, or a catalogue that\n\t\t * lacks the key, returns the input unchanged.\n\t\t *\n\t\t * @param {string} [value] The English source string.\n\t\t * @return {string|undefined} The translated (or source) string.\n\t\t */\n\t\ttr(value) {\n\t\t\treturn value ? this.effectiveTranslate(value) : value\n\t\t},\n\n\t\t/**\n\t\t * Route an action through the shared dispatcher — the pre-bound\n\t\t * `cnDispatchAction` (router/registry/handlers/openModal already\n\t\t * wired) when available, else a direct call with a minimal context.\n\t\t * The token context and the host translate function are always\n\t\t * merged in (the latter localises `api-call` success/error toasts).\n\t\t *\n\t\t * @param {object} action The action to dispatch.\n\t\t * @return {Promise<unknown>} The dispatch result.\n\t\t */\n\t\tdispatch(action) {\n\t\t\tconst extra = { tokenCtx: this.tokenCtx, translate: this.effectiveTranslate }\n\t\t\tif (typeof this.cnDispatchAction === 'function') {\n\t\t\t\treturn this.cnDispatchAction(action, extra)\n\t\t\t}\n\t\t\treturn dispatchAction(action, {\n\t\t\t\trouter: this.effectiveRouter,\n\t\t\t\topenForm: (a) => this.openForm(a),\n\t\t\t\topenRunNode: (a) => this.openRunNode(a),\n\t\t\t\t...extra,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Open the schema-driven create dialog for an `open-form` action:\n\t\t * fetch the schema object through the shared object store, then mount\n\t\t * CnAdvancedFormDialog. A failed schema load toasts and no-ops.\n\t\t *\n\t\t * @param {object} entry The open-form action (`register`, `schema`).\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync openForm(entry) {\n\t\t\tthis.formEntry = entry\n\t\t\tthis.formSchema = null\n\t\t\tconst register = entry.register || this.objectCtx.register || ''\n\t\t\tconst schema = entry.schema || ''\n\t\t\tif (!schema) {\n\t\t\t\tthis.formEntry = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst store = useObjectStore()\n\t\t\t\tconst type = resolveObjectOpType(store, { register, schema })\n\t\t\t\tthis.formSchema = await store.fetchSchema(type)\n\t\t\t} catch {\n\t\t\t\tthis.formSchema = null\n\t\t\t}\n\t\t\tif (!this.formSchema) {\n\t\t\t\tconst { showError } = await import('@nextcloud/dialogs')\n\t\t\t\tif (typeof showError === 'function') {\n\t\t\t\t\tshowError(t('nextcloud-vue', 'Could not open the form.'))\n\t\t\t\t}\n\t\t\t\tthis.formEntry = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open (or skip) the `run-node` config dialog: GET the target node's\n\t\t * describe endpoint (`or-flow-run-node`'s `GET .../{flowId}/{nodeId}/run`),\n\t\t * and either mount `CnRunNodeDialog` with its declared fields, or —\n\t\t * for a node with none — run immediately with an empty config. A\n\t\t * failed describe call toasts and does neither (RN-4: this resolves\n\t\t * as soon as the decision is made, never waiting on the person).\n\t\t *\n\t\t * @param {object} entry The run-node action (`flowId`, `nodeId`, ...).\n\t\t * @return {Promise<void>}\n\t\t *\n\t\t * @spec openspec/changes/manifest-run-node-action/specs/manifest-run-node-action/spec.md#requirement-the-nodes-own-config-form-drives-the-dialog-not-a-new-token\n\t\t */\n\t\tasync openRunNode(entry) {\n\t\t\tconst flowId = entry.flowId\n\t\t\tconst nodeId = entry.nodeId\n\t\t\tif (!flowId || !nodeId) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnActionButtons] run-node requires flowId and nodeId.', entry)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// The try assigns it and the catch returns, so an initialiser here\n\t\t\t// would never be read.\n\t\t\tlet fields\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl(`/apps/openregister/api/flows/${encodeURIComponent(flowId)}/nodes/${encodeURIComponent(nodeId)}/run`)\n\t\t\t\tconst response = await axios.get(url)\n\t\t\t\tfields = Array.isArray(response.data && response.data.configForm) ? response.data.configForm : []\n\t\t\t} catch (error) {\n\t\t\t\tconst { showError } = await import('@nextcloud/dialogs')\n\t\t\t\tconst serverMessage = error && error.response && error.response.data\n\t\t\t\t\t&& (error.response.data.error || error.response.data.message)\n\t\t\t\tif (typeof showError === 'function') {\n\t\t\t\t\tshowError(serverMessage || t('nextcloud-vue', 'Could not open this action.'))\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (!fields.length) {\n\t\t\t\t// Empty (or undeclared) form: the \"empty form is itself\n\t\t\t\t// information\" rule flow-node-config-forms already established\n\t\t\t\t// for the editor — run immediately, no dialog.\n\t\t\t\tthis.actionPending[entry.id] = true\n\t\t\t\tawait this.runNodePost(entry, {})\n\t\t\t\tthis.actionPending[entry.id] = false\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.runNodeFields = fields\n\t\t\tthis.runNodeEntry = entry\n\t\t},\n\n\t\t/**\n\t\t * The run-node dialog's confirm: POST the collected config, close the\n\t\t * dialog either way (success or failure both end the interaction —\n\t\t * a failure already toasted its own message).\n\t\t *\n\t\t * @param {object} config The dialog's collected field values.\n\t\t * @return {Promise<void>}\n\t\t *\n\t\t * @spec openspec/changes/manifest-run-node-action/specs/manifest-run-node-action/spec.md#requirement-a-run-node-action-invokes-one-flow-node-against-the-page-object\n\t\t */\n\t\tasync onRunNodeConfirm(config) {\n\t\t\tconst entry = this.runNodeEntry\n\t\t\tif (!entry) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.actionPending[entry.id] = true\n\t\t\tawait this.runNodePost(entry, config)\n\t\t\tthis.actionPending[entry.id] = false\n\t\t\tthis.closeRunNode()\n\t\t},\n\n\t\t/**\n\t\t * POST the run-node call through the shared dispatcher's `postRunNode`\n\t\t * (subject/register/schema resolution, fail-closed, toast + refresh —\n\t\t * see its own docblock in actionsDispatcher.js).\n\t\t *\n\t\t * @param {object} entry The run-node action.\n\t\t * @param {object} config The node's collected config.\n\t\t * @return {Promise<{ok: boolean}>}\n\t\t */\n\t\trunNodePost(entry, config) {\n\t\t\treturn postRunNode(entry, { tokenCtx: this.tokenCtx, translate: this.effectiveTranslate }, config)\n\t\t},\n\n\t\t/** Close the run-node dialog without running anything. */\n\t\tcloseRunNode() {\n\t\t\tthis.runNodeEntry = null\n\t\t\tthis.runNodeFields = []\n\t\t},\n\n\t\t/**\n\t\t * The create dialog's confirm: save the new object through the shared\n\t\t * object store, report the result phase, toast, refresh the page, and\n\t\t * navigate to `onSuccessRoute` when set — merging the saved object's id\n\t\t * into the route params (via `buildOnSuccessRoute`) so the navigation\n\t\t * can deep-link to the created object's detail page.\n\t\t *\n\t\t * @param {object} formData The dialog's form payload.\n\t\t * @param {object|null} [dynamic] The answers to the schema's data-driven questions (`{ answers, declarations }`), when it has any.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onFormConfirm(formData, dynamic = null) {\n\t\t\tconst entry = this.formEntry\n\t\t\tconst dialog = this.$refs.formDialog\n\t\t\tconst register = this.formRegister\n\t\t\tconst schema = (entry && entry.schema) || ''\n\t\t\ttry {\n\t\t\t\tconst store = useObjectStore()\n\t\t\t\tconst type = resolveObjectOpType(store, { register, schema })\n\t\t\t\t// A schema can require a field the form cannot supply — a\n\t\t\t\t// server-minted foreign key, say — in which case a straight\n\t\t\t\t// saveObject 400s. `createOverride` names a registry handler\n\t\t\t\t// that owns the persist instead (the CnIndexPage precedent).\n\t\t\t\tconst override = resolveCreateOverrideHandler(\n\t\t\t\t\tentry && entry.createOverride,\n\t\t\t\t\tthis.cnRegistry,\n\t\t\t\t\tthis.cnCustomComponents,\n\t\t\t\t)\n\t\t\t\tconst payload = { ...formData }\n\t\t\t\t// Array-mode declarations fold their answers into the parent's\n\t\t\t\t// own payload, so the whole record saves in ONE write. The\n\t\t\t\t// child-record mode below cannot do that (the rows need the\n\t\t\t\t// parent's id), which is why it writes afterwards and can leave\n\t\t\t\t// answers behind if that second write fails.\n\t\t\t\tthis.foldArrayAnswers(payload, dynamic)\n\t\t\t\tconst saved = override\n\t\t\t\t\t? await override(payload, { register, schema, type })\n\t\t\t\t\t: await store.saveObject(type, payload)\n\t\t\t\tif (!saved) {\n\t\t\t\t\tthrow new Error('save rejected')\n\t\t\t\t}\n\t\t\t\tawait this.saveDynamicAnswers(store, register, dynamic, saved)\n\t\t\t\tif (dialog && typeof dialog.setResult === 'function') {\n\t\t\t\t\tdialog.setResult({ success: true })\n\t\t\t\t}\n\t\t\t\tconst { showSuccess } = await import('@nextcloud/dialogs')\n\t\t\t\tif (typeof showSuccess === 'function') {\n\t\t\t\t\tshowSuccess((entry && entry.successMessage) || t('nextcloud-vue', 'Saved.'))\n\t\t\t\t}\n\t\t\t\tconst { emit } = await import('@nextcloud/event-bus')\n\t\t\t\temit('cn:page:refresh', {})\n\t\t\t\t/**\n\t\t\t\t * @event created Emitted after an open-form action saves. Payload: the created object.\n\t\t\t\t */\n\t\t\t\tthis.$emit('created', saved)\n\t\t\t\tif (entry && entry.onSuccessRoute && this.effectiveRouter) {\n\t\t\t\t\tconst location = buildOnSuccessRoute(entry.onSuccessRoute, saved)\n\t\t\t\t\tif (location) {\n\t\t\t\t\t\tthis.effectiveRouter.push(location).catch(() => {})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (dialog && typeof dialog.setResult === 'function') {\n\t\t\t\t\tdialog.setResult({ error: (e && e.message) || t('nextcloud-vue', 'Save failed.') })\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write the answers to a schema's data-driven questions, once the\n\t\t * object they belong to exists.\n\t\t *\n\t\t * This runs AFTER the parent save and deliberately does not roll it\n\t\t * back on failure: a case that exists without one of its custom values\n\t\t * is recoverable by editing it, whereas discarding a case the user\n\t\t * believes they filed is not. A failure here surfaces as the dialog's\n\t\t * error so it is never silent.\n\t\t *\n\t\t * @param {object} store The object store.\n\t\t * @param {string} register The register the object was saved in.\n\t\t * @param {object|null} dynamic The `{ answers, declarations }` payload.\n\t\t * @param {object} saved The saved parent object.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync saveDynamicAnswers(store, register, dynamic, saved) {\n\t\t\tif (!dynamic || !Array.isArray(dynamic.answers) || dynamic.answers.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst objectId = saved && (saved.id || saved.uuid)\n\t\t\tif (!objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst declarations = dynamic.declarations || []\n\t\t\tfor (const { key, config } of declarations) {\n\t\t\t\tconst values = config && config.values\n\t\t\t\tif (!values || !values.schema) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// Already written as part of the parent payload.\n\t\t\t\tif (usesArrayValues(config)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// An answer belongs to exactly one declaration. With a single\n\t\t\t\t// declaration every answer carries its key anyway; the filter\n\t\t\t\t// only matters once a schema has two, where writing an answer\n\t\t\t\t// to both value schemas would invent a row nobody entered.\n\t\t\t\tconst mine = declarations.length === 1\n\t\t\t\t\t? dynamic.answers\n\t\t\t\t\t: dynamic.answers.filter((a) => a.declarationKey === key)\n\t\t\t\tconst rows = valueRecordsFor(mine, config, objectId)\n\t\t\t\tif (rows.length === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst valueRegister = values.register || register\n\t\t\t\tconst type = resolveObjectOpType(store, { register: valueRegister, schema: values.schema })\n\t\t\t\tfor (const row of rows) {\n\t\t\t\t\tawait store.saveObject(type, row)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fold array-mode dynamic answers into the parent payload.\n\t\t *\n\t\t * @param {object} payload The parent object payload, mutated in place.\n\t\t * @param {object|null} dynamic The `{ answers, declarations }` payload.\n\t\t * @return {void}\n\t\t */\n\t\tfoldArrayAnswers(payload, dynamic) {\n\t\t\tif (!dynamic || !Array.isArray(dynamic.answers) || dynamic.answers.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst declarations = dynamic.declarations || []\n\t\t\tfor (const { key, config } of declarations) {\n\t\t\t\tif (!usesArrayValues(config)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst mine = declarations.length === 1\n\t\t\t\t\t? dynamic.answers\n\t\t\t\t\t: dynamic.answers.filter((a) => a.declarationKey === key)\n\t\t\t\tconst entries = valueArrayFor(mine, config, dynamic.definitions || [])\n\t\t\t\tif (entries.length) {\n\t\t\t\t\tpayload[config.values.arrayKey] = entries\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Close the create dialog.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcloseForm() {\n\t\t\tthis.formEntry = null\n\t\t\tthis.formSchema = null\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-action-buttons {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tflex-wrap: wrap;\n}\n\n/* In menu mode the only children are dialogs, so the wrapper must not be a\n box: an empty inline-flex child still eats one of the header's flex gaps\n and pushes the Actions menu away from the Edit button beside it. */\n.cn-action-buttons--menu {\n\tdisplay: contents;\n}\n</style>\n"],"names":["_normalizeClass","_createElementBlock","_Fragment","_renderList","_createBlock","_createTextVNode","_toDisplayString"],"mappings":"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"}
1
+ {"version":3,"file":"CnActionButtons.vue3.js","sources":["../../../../src/components/CnActionButtons/CnActionButtons.vue?vue&type=template&id=0f9733ca&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-action-buttons\"\n\t\t:class=\"{ 'cn-action-buttons--menu': display === 'menu' }\"\n\t\tdata-testid=\"cn-action-buttons\">\n\t\t<!-- `display: \"menu\"` keeps every dialog below but renders no buttons of\n\t\t its own: the host draws the entries as NcActionButtons inside an\n\t\t overflow menu instead, driven by the `entries` event. NcActions\n\t\t keeps only NcAction* vnodes out of its default slot, so this\n\t\t component cannot be placed in the menu itself.\n\t\t That mode is enforced in `barActions` / `overflowActions`, which\n\t\t both answer empty for it — so `inline` only means anything while\n\t\t this component is the one drawing. -->\n\t\t<template v-for=\"entry in barActions\">\n\t\t\t<!-- Toggle: a stateful two-way state button (GET on mount, write on\n\t\t\t click, optimistic + revert). Rendered inline, never dispatched. -->\n\t\t\t<NcButton\n\t\t\t\tv-if=\"entry.type === 'toggle'\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\t:variant=\"toggleState[entry.id] ? 'primary' : 'secondary'\"\n\t\t\t\t:disabled=\"Boolean(togglePending[entry.id])\"\n\t\t\t\t:data-testid=\"`cn-action-toggle-${entry.id}`\"\n\t\t\t\t:aria-pressed=\"String(Boolean(toggleState[entry.id]))\"\n\t\t\t\t@click=\"onToggleClick(entry)\">\n\t\t\t\t<template v-if=\"entry.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(entry.icon)\" :name=\"entry.icon\" :size=\"20\" />\n\t\t\t\t\t<span v-else :class=\"entry.icon\" />\n\t\t\t\t</template>\n\t\t\t\t{{ tr(toggleState[entry.id] ? (entry.labelOn || entry.label) : (entry.labelOff || entry.label)) }}\n\t\t\t</NcButton>\n\t\t\t<!-- An entry that goes to a URL is a LINK, not a button that calls\n\t\t\t the browser. NcButton renders an anchor when given `href`, so it\n\t\t\t keeps the cluster's styling while the browser supplies the\n\t\t\t pointer, the focus ring, middle-click and \"open in new tab\" —\n\t\t\t none of which a click handler can offer. -->\n\t\t\t<NcButton\n\t\t\t\tv-else-if=\"entry.href\"\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\t:variant=\"entry.variant || 'secondary'\"\n\t\t\t\t:href=\"entry.href\"\n\t\t\t\t:target=\"linkTargetOf(entry)\"\n\t\t\t\t:data-testid=\"`cn-action-${entry.id}`\"\n\t\t\t\t@click=\"onLinkClick(entry, $event)\">\n\t\t\t\t<template v-if=\"entry.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(entry.icon)\" :name=\"entry.icon\" :size=\"20\" />\n\t\t\t\t\t<span v-else :class=\"entry.icon\" />\n\t\t\t\t</template>\n\t\t\t\t{{ tr(entry.label) }}\n\t\t\t</NcButton>\n\t\t\t<!-- Everything else: a plain action button routed through the shared\n\t\t\t dispatcher (api-call / open-form / navigate / open-modal / refresh),\n\t\t\t confirm-gated first when the action asks for it. -->\n\t\t\t<!-- `variant`, NOT `type`. NcButton carries both: `type` is the\n\t\t\t native button type (default \"button\") and `variant` is the\n\t\t\t styling. Binding the descriptor's variant to `type` set an\n\t\t\t invalid native type and left `variant` at its \"secondary\"\n\t\t\t default, so `variant: \"primary\"` / `\"error\"` had never once\n\t\t\t rendered — silently, since an unknown native type is ignored. -->\n\t\t\t<NcButton\n\t\t\t\tv-else\n\t\t\t\t:key=\"entry.id\"\n\t\t\t\t:variant=\"entry.variant || 'secondary'\"\n\t\t\t\t:disabled=\"Boolean(actionPending[entry.id])\"\n\t\t\t\t:data-testid=\"`cn-action-${entry.id}`\"\n\t\t\t\t@click=\"onActionClick(entry)\">\n\t\t\t\t<template v-if=\"entry.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(entry.icon)\" :name=\"entry.icon\" :size=\"20\" />\n\t\t\t\t\t<span v-else :class=\"entry.icon\" />\n\t\t\t\t</template>\n\t\t\t\t{{ tr(entry.label) }}\n\t\t\t</NcButton>\n\t\t\t<!-- Sub-actions. Its OWN chevron rather than entries folded into the\n\t\t\t overflow below, because NcActions cannot nest — and a grouped\n\t\t\t dropdown is the point of declaring children (a version list\n\t\t\t flattened into the page's one menu grows without bound). The\n\t\t\t parent's own button above still renders alongside this chevron —\n\t\t\t a parent meant only to group children needs a real `handler` (or\n\t\t\t `href`/other type) of its own, or its button dispatches nothing. -->\n\t\t\t<NcActions\n\t\t\t\tv-if=\"hasChildren(entry)\"\n\t\t\t\t:key=\"`${entry.id}-children`\"\n\t\t\t\t:menuName=\"childMenuName(entry)\"\n\t\t\t\t:data-testid=\"`cn-action-children-${entry.id}`\">\n\t\t\t\t<template v-for=\"child in visibleChildren(entry)\">\n\t\t\t\t\t<NcActionLink\n\t\t\t\t\t\tv-if=\"child.href\"\n\t\t\t\t\t\t:key=\"`${child.id}-link`\"\n\t\t\t\t\t\t:href=\"child.href\"\n\t\t\t\t\t\t:target=\"linkTargetOf(child)\"\n\t\t\t\t\t\t:data-testid=\"`cn-action-${child.id}`\"\n\t\t\t\t\t\t@click=\"onLinkClick(child, $event)\">\n\t\t\t\t\t\t<template v-if=\"child.icon\" #icon>\n\t\t\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(child.icon)\" :name=\"child.icon\" :size=\"20\" />\n\t\t\t\t\t\t\t<span v-else :class=\"child.icon\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ tr(child.label) }}\n\t\t\t\t\t</NcActionLink>\n\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\t:key=\"child.id\"\n\t\t\t\t\t\t:disabled=\"Boolean(actionPending[child.id])\"\n\t\t\t\t\t\t:data-testid=\"`cn-action-${child.id}`\"\n\t\t\t\t\t\t@click=\"onActionClick(child)\">\n\t\t\t\t\t\t<template v-if=\"child.icon\" #icon>\n\t\t\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(child.icon)\" :name=\"child.icon\" :size=\"20\" />\n\t\t\t\t\t\t\t<span v-else :class=\"child.icon\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ tr(child.label) }}\n\t\t\t\t\t</NcActionButton>\n\t\t\t\t</template>\n\t\t\t</NcActions>\n\t\t</template>\n\n\t\t<!-- Overflow. Only ever rendered when `inline` is set, so a consumer\n\t\t that never asked for a limit keeps every action as a button. -->\n\t\t<NcActions\n\t\t\tv-if=\"overflowActions.length > 0\"\n\t\t\t:menuName=\"overflowMenuName\"\n\t\t\t:forceMenu=\"true\"\n\t\t\tdata-testid=\"cn-action-buttons-overflow\">\n\t\t\t<template v-for=\"entry in overflowActions\">\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-if=\"entry.href\"\n\t\t\t\t\t:key=\"`${entry.id}-link`\"\n\t\t\t\t\t:href=\"entry.href\"\n\t\t\t\t\t:target=\"linkTargetOf(entry)\"\n\t\t\t\t\t:data-testid=\"`cn-action-${entry.id}`\"\n\t\t\t\t\t@click=\"onLinkClick(entry, $event)\">\n\t\t\t\t\t<template v-if=\"entry.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(entry.icon)\" :name=\"entry.icon\" :size=\"20\" />\n\t\t\t\t\t\t<span v-else :class=\"entry.icon\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ tr(entry.label) }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-else\n\t\t\t\t\t:key=\"entry.id\"\n\t\t\t\t\t:disabled=\"Boolean(actionPending[entry.id])\"\n\t\t\t\t\t:data-testid=\"`cn-action-${entry.id}`\"\n\t\t\t\t\t@click=\"onActionClick(entry)\">\n\t\t\t\t\t<template v-if=\"entry.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"isMdiIconName(entry.icon)\" :name=\"entry.icon\" :size=\"20\" />\n\t\t\t\t\t\t<span v-else :class=\"entry.icon\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ tr(entry.label) }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\t\t</NcActions>\n\n\t\t<!-- Confirm gate for a confirm:true action (reuses CnConfirmDialog). -->\n\t\t<CnConfirmDialog\n\t\t\tv-if=\"confirmEntry\"\n\t\t\tref=\"confirmDialog\"\n\t\t\t:dialogTitle=\"tr(confirmEntry.confirmTitle || confirmEntry.label)\"\n\t\t\t:message=\"tr(confirmEntry.confirmMessage) || defaultConfirmMessage\"\n\t\t\t:variant=\"confirmEntry.variant === 'error' ? 'error' : 'primary'\"\n\t\t\t@confirm=\"onConfirmProceed\"\n\t\t\t@close=\"confirmEntry = null\" />\n\n\t\t<!-- Schema-driven create dialog for an open-form action. The plain\n\t\t form is the default: the properties/JSON table is a power-user\n\t\t surface, and a header button that says \"New case\" is aimed at\n\t\t someone filing one, not at someone inspecting the schema. Set\n\t\t `advanced: true` on the action to get the table back. -->\n\t\t<CnFormDialog\n\t\t\tv-if=\"formEntry && formSchema && !formEntry.advanced\"\n\t\t\tref=\"formDialog\"\n\t\t\t:schema=\"formSchema\"\n\t\t\t:item=\"null\"\n\t\t\t:register=\"formRegister\"\n\t\t\t:initialData=\"formInitialValues || {}\"\n\t\t\t:dialogTitle=\"tr(formEntry.formTitle) || ''\"\n\t\t\t:eyebrow=\"tr(formEntry.formEyebrow) || ''\"\n\t\t\t:subtitle=\"tr(formEntry.formSubtitle) || ''\"\n\t\t\t:includeFields=\"formEntry.includeFields || null\"\n\t\t\t:excludeFields=\"formEntry.excludeFields || []\"\n\t\t\t:fieldOverrides=\"formEntry.fieldOverrides || {}\"\n\t\t\t:size=\"formEntry.size || 'normal'\"\n\t\t\t:columns=\"formEntry.columns || 1\"\n\t\t\t@confirm=\"onFormConfirm\"\n\t\t\t@close=\"closeForm\" />\n\t\t<CnAdvancedFormDialog\n\t\t\tv-if=\"formEntry && formSchema && formEntry.advanced\"\n\t\t\tref=\"formDialog\"\n\t\t\t:schema=\"formSchema\"\n\t\t\t:item=\"null\"\n\t\t\t:initialValues=\"formInitialValues\"\n\t\t\t@confirm=\"onFormConfirm\"\n\t\t\t@close=\"closeForm\" />\n\n\t\t<!-- run-node's config dialog (manifest-run-node-action): only mounted\n\t\t when the target node declared at least one field AND that\n\t\t describe call succeeded — an empty/no form runs immediately with\n\t\t no dialog at all (openRunNode below), and a describe FAILURE also\n\t\t skips the dialog (toasts instead) rather than showing an empty,\n\t\t confusing one. -->\n\t\t<CnRunNodeDialog\n\t\t\tv-if=\"runNodeEntry && runNodeFields.length\"\n\t\t\t:title=\"tr(runNodeEntry.formTitle) || tr(runNodeEntry.label)\"\n\t\t\t:fields=\"runNodeFields\"\n\t\t\t:loading=\"Boolean(actionPending[runNodeEntry.id])\"\n\t\t\t:translate=\"effectiveTranslate\"\n\t\t\t@confirm=\"onRunNodeConfirm\"\n\t\t\t@close=\"closeRunNode\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcActionLink, NcActions, NcButton } from '@nextcloud/vue'\nimport { inject } from 'vue'\nimport CnConfirmDialog from '../../dialogs/CnConfirmDialog.vue'\nimport CnRunNodeDialog from '../../dialogs/CnRunNodeDialog.vue'\nimport { fetchEndpointSource } from '../../composables/useEndpointSource.js'\nimport { useObjectStore } from '../../store/useObjectStore.js'\nimport { actionLink, buildOnSuccessRoute, dispatchAction, interpolateActionTarget, isExternalActionTarget, postRunNode, resolveCreateOverrideHandler, resolveObjectOpType } from '../../utils/actionsDispatcher.js'\nimport { resolveObjectTokenContext } from '../../utils/detailObjectContext.js'\nimport { usesArrayValues, valueArrayFor, valueRecordsFor } from '../../utils/dynamicProperties.js'\nimport { followLinkClick } from '../../utils/linkNavigation.js'\nimport { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { evaluateVisibleWhen } from '../../utils/visibleWhen.js'\nimport { CnAdvancedFormDialog } from '../CnAdvancedFormDialog/index.js'\nimport { CnFormDialog } from '../CnFormDialog/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnActionButtons — declarative header-actions surface (#91 Wave 3).\n *\n * Renders a page's `headerActions[]` as buttons and owns the two action\n * behaviours that need STATE or a DIALOG the one-shot `dispatchAction`\n * can't provide on its own:\n *\n * - **`open-form`** — mounts the shared `CnAdvancedFormDialog` for the\n * action's `schema` (fetched through the object store), saves on\n * confirm, toasts, refreshes the page, and optionally navigates to\n * `onSuccessRoute`. Two optional keys cover the schemas a bare create\n * cannot satisfy: `props` seeds fixed field values into the create form\n * (so one schema can back several buttons, each fixing its own\n * discriminator), and `createOverride` names a registry handler that owns\n * the persist instead of `objectStore.saveObject` (for a schema requiring\n * a server-minted field the form cannot supply). `includeFields` /\n * `excludeFields` / `fieldOverrides` narrow what the button asks for, which\n * is how one schema serves both a full editor and a quick-create button.\n * `advanced: true` swaps in the properties/JSON table for a power user.\n * - **`toggle`** — a two-way state button: `GET`s `stateSource` on mount,\n * renders `labelOn` / `labelOff`, and on click `writes` the flipped\n * value OPTIMISTICALLY, reverting on failure.\n * - **`run-node`** (manifest-run-node-action) — GETs the target flow node's\n * describe endpoint (OpenRegister's `or-flow-run-node`); a node with\n * declared fields mounts `CnRunNodeDialog` (its own small field renderer —\n * see that component's docblock for why it is not `CnFlowNodeEditModal`'s),\n * a node with none runs immediately. Either way `postRunNode()`\n * (`actionsDispatcher.js`) makes the actual call, subject-token-resolved\n * exactly like `agent`.\n *\n * Every other type (`api-call`, `navigate`, `open-modal`, `open-page`,\n * `refresh`, `handler`) routes through the shared `dispatchAction` — with\n * a `CnConfirmDialog` gate first when the action sets `confirm: true`\n * (the object-op precedent: the dispatcher runs AFTER confirmation).\n *\n * Each action may carry a `visibleWhen` predicate (the shared banner\n * shape — `{ endpoint | source | field, op, value }`) evaluated against\n * the page / object context; a hidden action simply doesn't render.\n *\n * Placement: CnDashboardPage / CnDetailPage / CnPageRenderer mount this in\n * their header-actions area, passing the page's `headerActions[]`. The\n * object context (`{ objectId, object, register, schema }`) and the page\n * workspace / app-config bags are injected so tokens + local `visibleWhen`\n * resolve against the current record and page state.\n *\n * ```json\n * \"headerActions\": [\n * { \"id\": \"new-lead\", \"label\": \"New lead\", \"type\": \"open-form\",\n * \"register\": \"crm\", \"schema\": \"lead\", \"onSuccessRoute\": \"Leads\", \"variant\": \"primary\" },\n * { \"id\": \"approve\", \"label\": \"Approve\", \"type\": \"api-call\",\n * \"url\": \"/apps/shillinq/api/payment-runs/@objectId/approve\", \"confirm\": true,\n * \"visibleWhen\": { \"field\": \"state\", \"op\": \"eq\", \"value\": \"pending\" } },\n * { \"id\": \"toggle-open\", \"type\": \"toggle\", \"labelOn\": \"Open\", \"labelOff\": \"Closed\",\n * \"stateSource\": { \"url\": \"/apps/pipelinq/api/werkplek/@objectId/state\", \"responsePath\": \"open\" },\n * \"field\": \"open\", \"writeUrl\": \"/apps/pipelinq/api/werkplek/@objectId/state\", \"method\": \"PUT\" }\n * ]\n * ```\n */\nexport default {\n\tname: 'CnActionButtons',\n\n\tcomponents: { NcActions, NcActionButton, NcActionLink, NcButton, CnIcon, CnConfirmDialog, CnFormDialog, CnAdvancedFormDialog, CnRunNodeDialog },\n\n\tinject: {\n\t\t/** Detail-page object context (`{ objectId, object, register, schema }`). */\n\t\tcnObjectContext: { default: null },\n\t\t/** v2 slot-grid detail context holder (CnPageRenderer). */\n\t\tcnDetailObjectContext: { default: null },\n\t\t/** Page-level workspace context bag (reactive). */\n\t\tcnWorkspaceContext: { default: () => ({}) },\n\t\t/** Page-level app config bag (reactive). */\n\t\tcnAppConfig: { default: () => ({}) },\n\t\t/** Pre-bound dispatchAction from CnPageRenderer (router/registry/handlers/openModal wired). */\n\t\tcnDispatchAction: { default: null },\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). The\n\t\t * manifest-authored action labels, confirm copy and api-call toasts\n\t\t * are run through it. Defaults to an identity function so an\n\t\t * untranslated key renders as itself.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** v2 component registry, for resolving an `open-form` createOverride. */\n\t\tcnRegistry: { default: () => ({}) },\n\t\t/** Legacy customComponents map, same resolution fallback. */\n\t\tcnCustomComponents: { default: () => ({}) },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The page's declarative header actions. Each entry is a typed action\n\t\t * (`open-form` | `toggle` | `api-call` | `navigate` | `open-modal` |\n\t\t * `refresh` | `handler`) plus `id` / `label` and an optional\n\t\t * `visibleWhen` predicate, `icon`, `variant`, and `confirm`.\n\t\t *\n\t\t * An entry carrying `href` (with optional `target`) renders as a real\n\t\t * link instead — an anchor in the bar, an NcActionLink in a menu — and\n\t\t * is never dispatched. Use it for anything that ends in a URL, so the\n\t\t * browser keeps middle-click, \"open in new tab\" and the link semantics\n\t\t * assistive tech announces.\n\t\t *\n\t\t * `href` is used as given, so it must be a finished URL. A manifest\n\t\t * `headerActions[]` entry may carry `href` too: the page (CnDetailPage,\n\t\t * CnDashboardPage) resolves an instance path through `generateUrl()`\n\t\t * and sets `target` before the entry reaches this component.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * How many collapsible actions stay as buttons; the rest fall into a\n\t\t * single `···` overflow menu.\n\t\t *\n\t\t * `0` (the default) renders EVERY action as a button, which is what\n\t\t * every existing consumer already gets — the overflow only ever\n\t\t * appears once a host asks for a limit.\n\t\t *\n\t\t * A `variant: \"primary\"` action, an action carrying `children`, and a\n\t\t * `toggle` are never collapsed and never consume one of the N slots:\n\t\t * NcActions paints all of its inline actions with one shared variant\n\t\t * (so a lone primary among them is not expressible), it cannot nest\n\t\t * another NcActions, and a toggle's state lives in its button.\n\t\t *\n\t\t * @type {number}\n\t\t */\n\t\tinline: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0,\n\t\t},\n\n\t\t/**\n\t\t * Name for the overflow menu. Empty falls back to a translated\n\t\t * \"Actions\".\n\t\t *\n\t\t * @type {string}\n\t\t */\n\t\toverflowLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Explicit Vue Router instance for `navigate` / `open-page` /\n\t\t * `onSuccessRoute`. Falls back to `this.$router`. Only needed for\n\t\t * standalone mounts outside a router tree.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\trouter: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Where the actions are drawn. `buttons` (the default) puts one\n\t\t * NcButton per action in the host's header. `menu` draws none and\n\t\t * emits `entries` instead, so the host can render them inside an\n\t\t * overflow menu while this component keeps owning the dialogs.\n\t\t *\n\t\t * @type {'buttons'|'menu'}\n\t\t */\n\t\tdisplay: {\n\t\t\ttype: String,\n\t\t\tdefault: 'buttons',\n\t\t\tvalidator: (v) => ['buttons', 'menu'].includes(v),\n\t\t},\n\t},\n\n\temits: ['created', 'entries'],\n\n\tsetup() {\n\t\t// Read the two detail-surface injects once so the object token context\n\t\t// resolves identically to the endpoint-bound widgets (Wave 3).\n\t\tconst objectCtxRaw = inject('cnObjectContext', null)\n\t\tconst detailCtxRaw = inject('cnDetailObjectContext', null)\n\t\tconst workspaceRaw = inject('cnWorkspaceContext', null)\n\t\tconst appConfigRaw = inject('cnAppConfig', null)\n\t\treturn { objectCtxRaw, detailCtxRaw, workspaceRaw, appConfigRaw }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Evaluated visibleWhen outcome per action id (true when no predicate). */\n\t\t\tvisibility: {},\n\t\t\t/** Current boolean state per toggle action id. */\n\t\t\ttoggleState: {},\n\t\t\t/** In-flight flag per toggle action id (disables the button). */\n\t\t\ttogglePending: {},\n\t\t\t/** In-flight flag per non-toggle action id. */\n\t\t\tactionPending: {},\n\t\t\t/** The action awaiting confirmation (null = no dialog). */\n\t\t\tconfirmEntry: null,\n\t\t\t/** The open-form action currently showing its dialog (null = closed). */\n\t\t\tformEntry: null,\n\t\t\t/** The fetched schema object for the open-form dialog (null until loaded). */\n\t\t\tformSchema: null,\n\t\t\t/** The run-node action currently in flight / showing its dialog (null = closed). */\n\t\t\trunNodeEntry: null,\n\t\t\t/** The target node's declared config-form fields, from the describe GET. */\n\t\t\trunNodeFields: [],\n\t\t\t/** Default confirm-dialog message. */\n\t\t\tdefaultConfirmMessage: t('nextcloud-vue', 'Are you sure you want to continue?'),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Effective translate function: the injected `cnTranslate` (the host\n\t\t * app's bound `t()`), identity by default.\n\t\t *\n\t\t * @return {(key: string) => string}\n\t\t */\n\t\teffectiveTranslate() {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key\n\t\t},\n\n\t\t/**\n\t\t * Create-form seed values for the open `open-form` action. An action\n\t\t * declares them as `props`, which is how a single schema backs several\n\t\t * buttons — \"New request\" and \"New complaint\" both open the `ticket`\n\t\t * form, each fixing its own `ticketType`.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tformInitialValues() {\n\t\t\tconst props = this.formEntry && this.formEntry.props\n\t\t\tif (!props || typeof props !== 'object' || Array.isArray(props)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\t// Seed values go through the SAME token grammar as filters, so an\n\t\t\t// action on a detail page can stamp the record it belongs to:\n\t\t\t// `{ \"domainObjectRef\": \"@objectId\" }`. Without this the literal\n\t\t\t// string \"@objectId\" is saved, and a foreign key pointing at nothing\n\t\t\t// is a defect that only shows up in whatever reads it later.\n\t\t\treturn resolveFilterTokens(props, this.tokenCtx)\n\t\t},\n\n\t\t/**\n\t\t * The register the open dialog reads and writes against. The plain\n\t\t * form needs it to resolve `$ref` pickers into real dropdowns; without\n\t\t * one a reference field renders as an empty select and the action is\n\t\t * unusable for exactly the schemas that need it most.\n\t\t *\n\t\t * @return {string} The register slug, '' when none is resolvable.\n\t\t */\n\t\tformRegister() {\n\t\t\treturn (this.formEntry && this.formEntry.register) || this.objectCtx.register || ''\n\t\t},\n\n\t\t/**\n\t\t * The actions whose visibleWhen evaluated true (or carry no predicate).\n\t\t *\n\t\t * A `navigate` action whose target leaves the app is turned into a link\n\t\t * entry here, so it takes the `href` branch above instead of being\n\t\t * dispatched. An absolute URL is not a route: pushing one at the router\n\t\t * matched nothing and landed on the fallback page, carrying the URL's\n\t\t * own query string with it. The action's `target` (the URL) is replaced\n\t\t * by the anchor's `target`, since the two fields share a name and the\n\t\t * URL now lives in `href`. An author's explicit `href` still wins.\n\t\t *\n\t\t * An in-app `navigate` or `open-page` becomes a link too (see\n\t\t * `toLinkEntry`), keeping its own `target` so a fallback dispatch works.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn (this.actions || [])\n\t\t\t\t.filter((a) => a && a.id && this.visibility[a.id] !== false)\n\t\t\t\t.map((a) => this.toLinkEntry(a))\n\t\t},\n\n\t\t/**\n\t\t * The actions rendered in the bar itself: every non-collapsible one,\n\t\t * plus the first `inline` collapsible ones. Declaration order is kept,\n\t\t * so promoting an action never reshuffles the row.\n\t\t *\n\t\t * Empty in `display: \"menu\"` — the host draws the entries itself there,\n\t\t * so this component renders no buttons and has nothing to cap.\n\t\t *\n\t\t * @return {Array<object>} Actions to render as buttons / chevrons.\n\t\t */\n\t\tbarActions() {\n\t\t\tif (this.display === 'menu') {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tif (!(this.inline > 0)) {\n\t\t\t\treturn this.visibleActions\n\t\t\t}\n\t\t\tconst promoted = new Set(this.visibleActions\n\t\t\t\t.filter((a) => this.isCollapsible(a))\n\t\t\t\t.slice(0, this.inline)\n\t\t\t\t.map((a) => a.id))\n\t\t\treturn this.visibleActions.filter((a) => !this.isCollapsible(a) || promoted.has(a.id))\n\t\t},\n\n\t\t/**\n\t\t * The actions that did not fit, for the `···` menu. Always empty while\n\t\t * `inline` is 0, and in `display: \"menu\"` where the host owns the menu.\n\t\t *\n\t\t * @return {Array<object>} Actions to render as menu items.\n\t\t */\n\t\toverflowActions() {\n\t\t\tif (this.display === 'menu' || !(this.inline > 0)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst shown = new Set(this.barActions.map((a) => a.id))\n\t\t\treturn this.visibleActions.filter((a) => !shown.has(a.id))\n\t\t},\n\n\t\t/**\n\t\t * Name for the overflow trigger.\n\t\t *\n\t\t * @return {string} The label.\n\t\t */\n\t\toverflowMenuName() {\n\t\t\treturn this.tr(this.overflowLabel) || t('nextcloud-vue', 'Actions')\n\t\t},\n\n\t\t/**\n\t\t * The visible actions flattened for a host that draws them as menu\n\t\t * items. Everything the item needs to render is resolved here — the\n\t\t * translated label a toggle currently shows, its pressed state, its\n\t\t * pending flag, and which of the two icon shapes applies — so the host\n\t\t * template stays a plain v-for and needs none of this component's\n\t\t * state. `run()` is pre-bound, the way CnIndexPage pre-binds a header\n\t\t * action's handler for the index bar, so the host dispatches without\n\t\t * reaching back through a ref.\n\t\t *\n\t\t * @return {Array<object>} One menu-ready descriptor per visible action.\n\t\t */\n\t\tmenuEntries() {\n\t\t\treturn this.visibleActions.map((entry) => {\n\t\t\t\tconst isToggle = entry.type === 'toggle'\n\t\t\t\tconst on = Boolean(this.toggleState[entry.id])\n\t\t\t\tconst label = isToggle\n\t\t\t\t\t? (on ? (entry.labelOn || entry.label) : (entry.labelOff || entry.label))\n\t\t\t\t\t: entry.label\n\t\t\t\treturn {\n\t\t\t\t\tid: entry.id,\n\t\t\t\t\tlabel: this.tr(label),\n\t\t\t\t\t// An mdi name goes to CnIcon; a legacy `icon-*` class goes\n\t\t\t\t\t// on a bare span. Deciding it here keeps isMdiIconName from\n\t\t\t\t\t// having to be duplicated into every host.\n\t\t\t\t\ticonName: this.isMdiIconName(entry.icon) ? entry.icon : '',\n\t\t\t\t\ticonClass: (entry.icon && !this.isMdiIconName(entry.icon)) ? entry.icon : '',\n\t\t\t\t\tdisabled: Boolean(isToggle ? this.togglePending[entry.id] : this.actionPending[entry.id]),\n\t\t\t\t\t// null, not false: a non-toggle carries no pressed state at\n\t\t\t\t\t// all, and `aria-pressed=\"false\"` would announce every\n\t\t\t\t\t// action as an un-pressed toggle button.\n\t\t\t\t\tpressed: isToggle ? on : null,\n\t\t\t\t\ttestid: isToggle ? `cn-action-toggle-${entry.id}` : `cn-action-${entry.id}`,\n\t\t\t\t\t// A link entry, for a host to render as an anchor rather\n\t\t\t\t\t// than a button with a click handler. Named `linkTarget`\n\t\t\t\t\t// because an action's own `target` is its destination. An in-app\n\t\t\t\t\t// link (`linkTo`) is left to `run()`: a host anchor has no router.\n\t\t\t\t\thref: (!entry.linkTo && entry.href) || '',\n\t\t\t\t\tlinkTarget: (!entry.linkTo && entry.href) ? (entry.target || '_blank') : '',\n\t\t\t\t\trun: () => (isToggle ? this.onToggleClick(entry) : this.onActionClick(entry)),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** Unwrapped page workspace bag. */\n\t\tworkspaceCtx() {\n\t\t\tconst c = this.workspaceRaw\n\t\t\tconst v = (c && typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t\treturn (v && typeof v === 'object') ? v : {}\n\t\t},\n\n\t\t/** Unwrapped app-config bag. */\n\t\tconfigCtx() {\n\t\t\tconst c = this.appConfigRaw\n\t\t\tconst v = (c && typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t\treturn (v && typeof v === 'object') ? v : {}\n\t\t},\n\n\t\t/** The merged object token context (both detail-surface injects). */\n\t\tobjectCtx() {\n\t\t\treturn resolveObjectTokenContext(this.objectCtxRaw, this.detailCtxRaw) || {}\n\t\t},\n\n\t\t/** The full token context for url/param interpolation + local visibleWhen. */\n\t\ttokenCtx() {\n\t\t\treturn {\n\t\t\t\t...this.objectCtx,\n\t\t\t\tworkspace: this.workspaceCtx,\n\t\t\t\tconfig: this.configCtx,\n\t\t\t}\n\t\t},\n\n\t\t/** The effective router (explicit prop wins, else this.$router). */\n\t\teffectiveRouter() {\n\t\t\treturn this.router || this.$router || null\n\t\t},\n\t},\n\n\twatch: {\n\t\tactions: {\n\t\t\timmediate: true,\n\t\t\thandler() {\n\t\t\t\tthis.evaluateVisibility()\n\t\t\t\tthis.initToggles()\n\t\t\t},\n\t\t},\n\n\t\t// Re-evaluate local (object-context) visibleWhen when the record loads.\n\t\tobjectCtx: {\n\t\t\tdeep: true,\n\t\t\thandler() { this.evaluateVisibility() },\n\t\t},\n\n\t\t// Push the menu-ready descriptors at a `display: \"menu\"` host. It fires\n\t\t// immediately so the host has the list on first paint, and again on\n\t\t// every change of visibility, toggle state or pending flag — the three\n\t\t// things that would otherwise leave a stale item in an open menu.\n\t\tmenuEntries: {\n\t\t\timmediate: true,\n\t\t\thandler(entries) {\n\t\t\t\tif (this.display === 'menu') {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event entries Emitted in `display: \"menu\"` only, whenever the visible actions, their toggle state or their pending flags change. Payload: one menu-ready descriptor per visible action, each carrying `id`, `label`, `iconName`, `iconClass`, `disabled`, `pressed`, `testid` and a pre-bound `run()`.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('entries', entries)\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Heuristic MDI-name check (an `icon-`-prefixed string is a CSS class).\n\t\t *\n\t\t * @param {string} name The icon string.\n\t\t * @return {boolean} True when it should render via CnIcon.\n\t\t */\n\t\tisMdiIconName(name) {\n\t\t\treturn typeof name === 'string' && name !== '' && !name.startsWith('icon-')\n\t\t},\n\n\t\t/**\n\t\t * Evaluate every action's `visibleWhen` against the current context.\n\t\t * Fail-safe (a broken predicate hides its action); actions without a\n\t\t * predicate are always visible.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync evaluateVisibility() {\n\t\t\t// Children are walked too: a sub-action whose predicate was never\n\t\t\t// evaluated would read as visible and render regardless.\n\t\t\tconst flat = []\n\t\t\tfor (const action of this.actions || []) {\n\t\t\t\tif (!action || !action.id) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tflat.push(action)\n\t\t\t\tif (Array.isArray(action.children)) {\n\t\t\t\t\tflat.push(...action.children.filter((c) => c && c.id))\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const action of flat) {\n\t\t\t\tif (!action.visibleWhen) {\n\t\t\t\t\tthis.visibility[action.id] = true\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst ok = await evaluateVisibleWhen(action.visibleWhen, this.tokenCtx)\n\t\t\t\tthis.visibility[action.id] = ok\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Seed each toggle action's state from its `stateSource` endpoint\n\t\t * (fetched through the shared endpoint engine so `@objectId` etc.\n\t\t * resolve). No-op for non-toggle actions.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync initToggles() {\n\t\t\tfor (const action of this.actions || []) {\n\t\t\t\tif (!action || action.type !== 'toggle' || !action.id) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.toggleState[action.id] = false\n\t\t\t\tif (!action.stateSource || !action.stateSource.url) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst payload = await fetchEndpointSource(action.stateSource, this.tokenCtx)\n\t\t\t\t\tconst value = action.field ? this.readField(payload, action.field) : payload\n\t\t\t\t\tthis.toggleState[action.id] = Boolean(value)\n\t\t\t\t} catch {\n\t\t\t\t\t// Leave the default (off); a failed state read never breaks the bar.\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Whether an action may be pushed into the overflow menu. See the\n\t\t * `inline` prop for why these three shapes never can be.\n\t\t *\n\t\t * @param {object} entry The action descriptor.\n\t\t * @return {boolean} True when it can collapse.\n\t\t */\n\t\tisCollapsible(entry) {\n\t\t\treturn !this.hasChildren(entry)\n\t\t\t\t&& entry.variant !== 'primary'\n\t\t\t\t&& entry.type !== 'toggle'\n\t\t},\n\n\t\t/**\n\t\t * Whether an action declares sub-actions worth a chevron.\n\t\t *\n\t\t * @param {object} entry The action descriptor.\n\t\t * @return {boolean} True when it has at least one visible child.\n\t\t */\n\t\thasChildren(entry) {\n\t\t\treturn this.visibleChildren(entry).length > 0\n\t\t},\n\n\t\t/**\n\t\t * An action's sub-actions, minus any hidden by `visibleWhen`.\n\t\t *\n\t\t * @param {object} entry The action descriptor.\n\t\t * @return {Array<object>} The renderable children.\n\t\t */\n\t\tvisibleChildren(entry) {\n\t\t\tif (!Array.isArray(entry?.children)) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn entry.children\n\t\t\t\t.filter((c) => c && c.id && this.visibility[c.id] !== false)\n\t\t\t\t.map((c) => this.toLinkEntry(c))\n\t\t},\n\n\t\t/**\n\t\t * Give a navigating action the `href` it renders as. An external\n\t\t * `navigate` target becomes the href with a `_blank` anchor target. An\n\t\t * in-app `navigate` / `open-page` gets the router-resolved href plus\n\t\t * `linkTo`, the location a plain click routes to; one with `confirm`,\n\t\t * an `onSelect` function, or an unresolvable route stays a button.\n\t\t *\n\t\t * @param {object} a The action descriptor.\n\t\t * @return {object} The descriptor, possibly with `href` / `linkTo` added.\n\t\t */\n\t\ttoLinkEntry(a) {\n\t\t\tif (a.href || (a.type !== 'navigate' && a.type !== 'open-page')) {\n\t\t\t\treturn a\n\t\t\t}\n\t\t\tlet entry = a\n\t\t\tif (a.type === 'navigate') {\n\t\t\t\t// Interpolated BEFORE the external test and before it becomes an\n\t\t\t\t// href, so the anchor carries the record's id rather than the token.\n\t\t\t\tconst target = interpolateActionTarget(a.target, this.tokenCtx)\n\t\t\t\tif (isExternalActionTarget(target)) {\n\t\t\t\t\treturn { ...a, href: target, target: '_blank' }\n\t\t\t\t}\n\t\t\t\tentry = target === a.target ? a : { ...a, target }\n\t\t\t}\n\t\t\tif (typeof entry.onSelect === 'function') {\n\t\t\t\treturn entry\n\t\t\t}\n\t\t\tconst link = actionLink(entry, { router: this.effectiveRouter })\n\t\t\treturn link ? { ...entry, href: link.href, linkTo: link.to } : entry\n\t\t},\n\n\t\t/**\n\t\t * The anchor `target` of a link entry. An in-app link opens in place;\n\t\t * its own `target` is the destination, not an anchor target.\n\t\t *\n\t\t * @param {object} entry The link entry.\n\t\t * @return {string|undefined} The anchor target.\n\t\t */\n\t\tlinkTargetOf(entry) {\n\t\t\treturn entry.linkTo ? undefined : (entry.target || undefined)\n\t\t},\n\n\t\t/**\n\t\t * Click on a link entry: a plain click on an in-app link routes in\n\t\t * place; a URL or a modified click is left to the browser.\n\t\t *\n\t\t * @param {object} entry The link entry.\n\t\t * @param {MouseEvent} event The click event.\n\t\t * @return {void}\n\t\t */\n\t\tonLinkClick(entry, event) {\n\t\t\tif (entry.linkTo) {\n\t\t\t\tfollowLinkClick(event, entry.linkTo, this.effectiveRouter)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Name for a sub-action chevron. `childrenLabel` when set, else the\n\t\t * parent's own label, so the dropdown is never announced unlabelled.\n\t\t *\n\t\t * @param {object} entry The action descriptor.\n\t\t * @return {string} The menu name.\n\t\t */\n\t\tchildMenuName(entry) {\n\t\t\treturn this.tr(entry.childrenLabel) || this.tr(entry.label) || ''\n\t\t},\n\n\t\t/**\n\t\t * Read a dot-path off a payload (the toggle state field).\n\t\t *\n\t\t * @param {unknown} data The payload.\n\t\t * @param {string} field The dot-path.\n\t\t * @return {unknown} The value at the path.\n\t\t */\n\t\treadField(data, field) {\n\t\t\tif (!field) {\n\t\t\t\treturn data\n\t\t\t}\n\t\t\treturn String(field).split('.').reduce((o, k) => (o === null || o === undefined ? o : o[k]), data)\n\t\t},\n\n\t\t/**\n\t\t * Toggle click: flip the state OPTIMISTICALLY, write it, and revert on\n\t\t * failure. Success/error toast via the api-call dispatch path (which\n\t\t * also refreshes the page unless `refresh: false`).\n\t\t *\n\t\t * @param {object} entry The toggle action.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onToggleClick(entry) {\n\t\t\tif (this.togglePending[entry.id]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst previous = Boolean(this.toggleState[entry.id])\n\t\t\tconst next = !previous\n\t\t\tthis.toggleState[entry.id] = next\n\t\t\tthis.togglePending[entry.id] = true\n\t\t\tconst writeParams = { ...(entry.params || {}) }\n\t\t\tif (entry.field) {\n\t\t\t\twriteParams[entry.field] = next\n\t\t\t}\n\t\t\tconst result = await this.dispatch({\n\t\t\t\ttype: 'api-call',\n\t\t\t\turl: entry.writeUrl,\n\t\t\t\tmethod: entry.method || 'PUT',\n\t\t\t\tparams: writeParams,\n\t\t\t\tsuccessMessage: entry.successMessage,\n\t\t\t\terrorMessage: entry.errorMessage,\n\t\t\t\trefresh: entry.refresh,\n\t\t\t})\n\t\t\tif (!result || result.ok === false) {\n\t\t\t\t// Revert the optimistic flip.\n\t\t\t\tthis.toggleState[entry.id] = previous\n\t\t\t}\n\t\t\tthis.togglePending[entry.id] = false\n\t\t},\n\n\t\t/**\n\t\t * Non-toggle action click: gate on `confirm` first, else run.\n\t\t *\n\t\t * @param {object} entry The action.\n\t\t * @return {void}\n\t\t */\n\t\tonActionClick(entry) {\n\t\t\tif (entry.confirm === true) {\n\t\t\t\tthis.confirmEntry = entry\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.runAction(entry)\n\t\t},\n\n\t\t/**\n\t\t * The confirm dialog's proceed handler: run the pending action, then\n\t\t * report the outcome back to the dialog's result phase.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onConfirmProceed() {\n\t\t\tconst entry = this.confirmEntry\n\t\t\tif (!entry) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst result = await this.runAction(entry)\n\t\t\tconst dialog = this.$refs.confirmDialog\n\t\t\tif (dialog && typeof dialog.setResult === 'function') {\n\t\t\t\tconst ok = result === undefined || (result && result.ok !== false)\n\t\t\t\tdialog.setResult(ok ? { success: true } : { error: t('nextcloud-vue', 'Action failed.') })\n\t\t\t} else {\n\t\t\t\tthis.confirmEntry = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Run an action (post-confirmation). `open-form` opens the schema\n\t\t * dialog; everything else dispatches. Returns the dispatch result so\n\t\t * the confirm dialog can report success/failure.\n\t\t *\n\t\t * @param {object} entry The action.\n\t\t * @return {Promise<unknown>} The dispatch result (undefined for open-form).\n\t\t */\n\t\tasync runAction(entry) {\n\t\t\t// A function on the descriptor, for a PARENT COMPONENT composing\n\t\t\t// entries in JS (CnDetailPage folds its own Edit in this way).\n\t\t\t// Manifest JSON cannot carry one, so the declarative path is\n\t\t\t// untouched — and it runs after the confirm gate, like every\n\t\t\t// dispatched type.\n\t\t\tif (typeof entry.onSelect === 'function') {\n\t\t\t\tentry.onSelect(entry)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tif (entry.type === 'open-form') {\n\t\t\t\tawait this.openForm(entry)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tif (entry.type === 'run-node') {\n\t\t\t\tawait this.openRunNode(entry)\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tthis.actionPending[entry.id] = true\n\t\t\tconst result = await this.dispatch(entry)\n\t\t\tthis.actionPending[entry.id] = false\n\t\t\treturn result\n\t\t},\n\n\t\t/**\n\t\t * Resolve a manifest-authored UI string through the host translate\n\t\t * function. A pure pass-through: an empty value, or a catalogue that\n\t\t * lacks the key, returns the input unchanged.\n\t\t *\n\t\t * @param {string} [value] The English source string.\n\t\t * @return {string|undefined} The translated (or source) string.\n\t\t */\n\t\ttr(value) {\n\t\t\treturn value ? this.effectiveTranslate(value) : value\n\t\t},\n\n\t\t/**\n\t\t * Route an action through the shared dispatcher — the pre-bound\n\t\t * `cnDispatchAction` (router/registry/handlers/openModal already\n\t\t * wired) when available, else a direct call with a minimal context.\n\t\t * The token context and the host translate function are always\n\t\t * merged in (the latter localises `api-call` success/error toasts).\n\t\t *\n\t\t * @param {object} action The action to dispatch.\n\t\t * @return {Promise<unknown>} The dispatch result.\n\t\t */\n\t\tdispatch(action) {\n\t\t\tconst extra = { tokenCtx: this.tokenCtx, translate: this.effectiveTranslate }\n\t\t\tif (typeof this.cnDispatchAction === 'function') {\n\t\t\t\treturn this.cnDispatchAction(action, extra)\n\t\t\t}\n\t\t\treturn dispatchAction(action, {\n\t\t\t\trouter: this.effectiveRouter,\n\t\t\t\topenForm: (a) => this.openForm(a),\n\t\t\t\topenRunNode: (a) => this.openRunNode(a),\n\t\t\t\t...extra,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Open the schema-driven create dialog for an `open-form` action:\n\t\t * fetch the schema object through the shared object store, then mount\n\t\t * CnAdvancedFormDialog. A failed schema load toasts and no-ops.\n\t\t *\n\t\t * @param {object} entry The open-form action (`register`, `schema`).\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync openForm(entry) {\n\t\t\tthis.formEntry = entry\n\t\t\tthis.formSchema = null\n\t\t\tconst register = entry.register || this.objectCtx.register || ''\n\t\t\tconst schema = entry.schema || ''\n\t\t\tif (!schema) {\n\t\t\t\tthis.formEntry = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst store = useObjectStore()\n\t\t\t\tconst type = resolveObjectOpType(store, { register, schema })\n\t\t\t\tthis.formSchema = await store.fetchSchema(type)\n\t\t\t} catch {\n\t\t\t\tthis.formSchema = null\n\t\t\t}\n\t\t\tif (!this.formSchema) {\n\t\t\t\tconst { showError } = await import('@nextcloud/dialogs')\n\t\t\t\tif (typeof showError === 'function') {\n\t\t\t\t\tshowError(t('nextcloud-vue', 'Could not open the form.'))\n\t\t\t\t}\n\t\t\t\tthis.formEntry = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open (or skip) the `run-node` config dialog: GET the target node's\n\t\t * describe endpoint (`or-flow-run-node`'s `GET .../{flowId}/{nodeId}/run`),\n\t\t * and either mount `CnRunNodeDialog` with its declared fields, or —\n\t\t * for a node with none — run immediately with an empty config. A\n\t\t * failed describe call toasts and does neither (RN-4: this resolves\n\t\t * as soon as the decision is made, never waiting on the person).\n\t\t *\n\t\t * @param {object} entry The run-node action (`flowId`, `nodeId`, ...).\n\t\t * @return {Promise<void>}\n\t\t *\n\t\t * @spec openspec/changes/manifest-run-node-action/specs/manifest-run-node-action/spec.md#requirement-the-nodes-own-config-form-drives-the-dialog-not-a-new-token\n\t\t */\n\t\tasync openRunNode(entry) {\n\t\t\tconst flowId = entry.flowId\n\t\t\tconst nodeId = entry.nodeId\n\t\t\tif (!flowId || !nodeId) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnActionButtons] run-node requires flowId and nodeId.', entry)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// The try assigns it and the catch returns, so an initialiser here\n\t\t\t// would never be read.\n\t\t\tlet fields\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl(`/apps/openregister/api/flows/${encodeURIComponent(flowId)}/nodes/${encodeURIComponent(nodeId)}/run`)\n\t\t\t\tconst response = await axios.get(url)\n\t\t\t\tfields = Array.isArray(response.data && response.data.configForm) ? response.data.configForm : []\n\t\t\t} catch (error) {\n\t\t\t\tconst { showError } = await import('@nextcloud/dialogs')\n\t\t\t\tconst serverMessage = error && error.response && error.response.data\n\t\t\t\t\t&& (error.response.data.error || error.response.data.message)\n\t\t\t\tif (typeof showError === 'function') {\n\t\t\t\t\tshowError(serverMessage || t('nextcloud-vue', 'Could not open this action.'))\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (!fields.length) {\n\t\t\t\t// Empty (or undeclared) form: the \"empty form is itself\n\t\t\t\t// information\" rule flow-node-config-forms already established\n\t\t\t\t// for the editor — run immediately, no dialog.\n\t\t\t\tthis.actionPending[entry.id] = true\n\t\t\t\tawait this.runNodePost(entry, {})\n\t\t\t\tthis.actionPending[entry.id] = false\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.runNodeFields = fields\n\t\t\tthis.runNodeEntry = entry\n\t\t},\n\n\t\t/**\n\t\t * The run-node dialog's confirm: POST the collected config, close the\n\t\t * dialog either way (success or failure both end the interaction —\n\t\t * a failure already toasted its own message).\n\t\t *\n\t\t * @param {object} config The dialog's collected field values.\n\t\t * @return {Promise<void>}\n\t\t *\n\t\t * @spec openspec/changes/manifest-run-node-action/specs/manifest-run-node-action/spec.md#requirement-a-run-node-action-invokes-one-flow-node-against-the-page-object\n\t\t */\n\t\tasync onRunNodeConfirm(config) {\n\t\t\tconst entry = this.runNodeEntry\n\t\t\tif (!entry) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.actionPending[entry.id] = true\n\t\t\tawait this.runNodePost(entry, config)\n\t\t\tthis.actionPending[entry.id] = false\n\t\t\tthis.closeRunNode()\n\t\t},\n\n\t\t/**\n\t\t * POST the run-node call through the shared dispatcher's `postRunNode`\n\t\t * (subject/register/schema resolution, fail-closed, toast + refresh —\n\t\t * see its own docblock in actionsDispatcher.js).\n\t\t *\n\t\t * @param {object} entry The run-node action.\n\t\t * @param {object} config The node's collected config.\n\t\t * @return {Promise<{ok: boolean}>}\n\t\t */\n\t\trunNodePost(entry, config) {\n\t\t\treturn postRunNode(entry, { tokenCtx: this.tokenCtx, translate: this.effectiveTranslate }, config)\n\t\t},\n\n\t\t/** Close the run-node dialog without running anything. */\n\t\tcloseRunNode() {\n\t\t\tthis.runNodeEntry = null\n\t\t\tthis.runNodeFields = []\n\t\t},\n\n\t\t/**\n\t\t * The create dialog's confirm: save the new object through the shared\n\t\t * object store, report the result phase, toast, refresh the page, and\n\t\t * navigate to `onSuccessRoute` when set — merging the saved object's id\n\t\t * into the route params (via `buildOnSuccessRoute`) so the navigation\n\t\t * can deep-link to the created object's detail page.\n\t\t *\n\t\t * @param {object} formData The dialog's form payload.\n\t\t * @param {object|null} [dynamic] The answers to the schema's data-driven questions (`{ answers, declarations }`), when it has any.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onFormConfirm(formData, dynamic = null) {\n\t\t\tconst entry = this.formEntry\n\t\t\tconst dialog = this.$refs.formDialog\n\t\t\tconst register = this.formRegister\n\t\t\tconst schema = (entry && entry.schema) || ''\n\t\t\ttry {\n\t\t\t\tconst store = useObjectStore()\n\t\t\t\tconst type = resolveObjectOpType(store, { register, schema })\n\t\t\t\t// A schema can require a field the form cannot supply — a\n\t\t\t\t// server-minted foreign key, say — in which case a straight\n\t\t\t\t// saveObject 400s. `createOverride` names a registry handler\n\t\t\t\t// that owns the persist instead (the CnIndexPage precedent).\n\t\t\t\tconst override = resolveCreateOverrideHandler(\n\t\t\t\t\tentry && entry.createOverride,\n\t\t\t\t\tthis.cnRegistry,\n\t\t\t\t\tthis.cnCustomComponents,\n\t\t\t\t)\n\t\t\t\tconst payload = { ...formData }\n\t\t\t\t// Array-mode declarations fold their answers into the parent's\n\t\t\t\t// own payload, so the whole record saves in ONE write. The\n\t\t\t\t// child-record mode below cannot do that (the rows need the\n\t\t\t\t// parent's id), which is why it writes afterwards and can leave\n\t\t\t\t// answers behind if that second write fails.\n\t\t\t\tthis.foldArrayAnswers(payload, dynamic)\n\t\t\t\tconst saved = override\n\t\t\t\t\t? await override(payload, { register, schema, type })\n\t\t\t\t\t: await store.saveObject(type, payload)\n\t\t\t\tif (!saved) {\n\t\t\t\t\tthrow new Error('save rejected')\n\t\t\t\t}\n\t\t\t\tawait this.saveDynamicAnswers(store, register, dynamic, saved)\n\t\t\t\tif (dialog && typeof dialog.setResult === 'function') {\n\t\t\t\t\tdialog.setResult({ success: true })\n\t\t\t\t}\n\t\t\t\tconst { showSuccess } = await import('@nextcloud/dialogs')\n\t\t\t\tif (typeof showSuccess === 'function') {\n\t\t\t\t\tshowSuccess((entry && entry.successMessage) || t('nextcloud-vue', 'Saved.'))\n\t\t\t\t}\n\t\t\t\tconst { emit } = await import('@nextcloud/event-bus')\n\t\t\t\temit('cn:page:refresh', {})\n\t\t\t\t/**\n\t\t\t\t * @event created Emitted after an open-form action saves. Payload: the created object.\n\t\t\t\t */\n\t\t\t\tthis.$emit('created', saved)\n\t\t\t\tif (entry && entry.onSuccessRoute && this.effectiveRouter) {\n\t\t\t\t\tconst location = buildOnSuccessRoute(entry.onSuccessRoute, saved)\n\t\t\t\t\tif (location) {\n\t\t\t\t\t\tthis.effectiveRouter.push(location).catch(() => {})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (dialog && typeof dialog.setResult === 'function') {\n\t\t\t\t\tdialog.setResult({ error: (e && e.message) || t('nextcloud-vue', 'Save failed.') })\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write the answers to a schema's data-driven questions, once the\n\t\t * object they belong to exists.\n\t\t *\n\t\t * This runs AFTER the parent save and deliberately does not roll it\n\t\t * back on failure: a case that exists without one of its custom values\n\t\t * is recoverable by editing it, whereas discarding a case the user\n\t\t * believes they filed is not. A failure here surfaces as the dialog's\n\t\t * error so it is never silent.\n\t\t *\n\t\t * @param {object} store The object store.\n\t\t * @param {string} register The register the object was saved in.\n\t\t * @param {object|null} dynamic The `{ answers, declarations }` payload.\n\t\t * @param {object} saved The saved parent object.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync saveDynamicAnswers(store, register, dynamic, saved) {\n\t\t\tif (!dynamic || !Array.isArray(dynamic.answers) || dynamic.answers.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst objectId = saved && (saved.id || saved.uuid)\n\t\t\tif (!objectId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst declarations = dynamic.declarations || []\n\t\t\tfor (const { key, config } of declarations) {\n\t\t\t\tconst values = config && config.values\n\t\t\t\tif (!values || !values.schema) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// Already written as part of the parent payload.\n\t\t\t\tif (usesArrayValues(config)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t// An answer belongs to exactly one declaration. With a single\n\t\t\t\t// declaration every answer carries its key anyway; the filter\n\t\t\t\t// only matters once a schema has two, where writing an answer\n\t\t\t\t// to both value schemas would invent a row nobody entered.\n\t\t\t\tconst mine = declarations.length === 1\n\t\t\t\t\t? dynamic.answers\n\t\t\t\t\t: dynamic.answers.filter((a) => a.declarationKey === key)\n\t\t\t\tconst rows = valueRecordsFor(mine, config, objectId)\n\t\t\t\tif (rows.length === 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst valueRegister = values.register || register\n\t\t\t\tconst type = resolveObjectOpType(store, { register: valueRegister, schema: values.schema })\n\t\t\t\tfor (const row of rows) {\n\t\t\t\t\tawait store.saveObject(type, row)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fold array-mode dynamic answers into the parent payload.\n\t\t *\n\t\t * @param {object} payload The parent object payload, mutated in place.\n\t\t * @param {object|null} dynamic The `{ answers, declarations }` payload.\n\t\t * @return {void}\n\t\t */\n\t\tfoldArrayAnswers(payload, dynamic) {\n\t\t\tif (!dynamic || !Array.isArray(dynamic.answers) || dynamic.answers.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst declarations = dynamic.declarations || []\n\t\t\tfor (const { key, config } of declarations) {\n\t\t\t\tif (!usesArrayValues(config)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst mine = declarations.length === 1\n\t\t\t\t\t? dynamic.answers\n\t\t\t\t\t: dynamic.answers.filter((a) => a.declarationKey === key)\n\t\t\t\tconst entries = valueArrayFor(mine, config, dynamic.definitions || [])\n\t\t\t\tif (entries.length) {\n\t\t\t\t\tpayload[config.values.arrayKey] = entries\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Close the create dialog.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcloseForm() {\n\t\t\tthis.formEntry = null\n\t\t\tthis.formSchema = null\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-action-buttons {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tflex-wrap: wrap;\n}\n\n/* In menu mode the only children are dialogs, so the wrapper must not be a\n box: an empty inline-flex child still eats one of the header's flex gaps\n and pushes the Actions menu away from the Edit button beside it. */\n.cn-action-buttons--menu {\n\tdisplay: contents;\n}\n</style>\n"],"names":["_normalizeClass","_createElementBlock","_Fragment","_renderList","_createBlock","_createTextVNode","_toDisplayString"],"mappings":";;;;;;;;;;;;;;SAYO,EAAAA,cAAA,CAAA,CAAC,mBAAmB,EAAA,EAAA,yBAAA,EACY,MAAO,CAAA,OAAA,KAAA,MAAA,EAAA,CAAA,CAAA;AACvC,IAAA,aAAM,EAAC,mBAAoB;;sBAS9BC,kBAkGS,CAAAC,QAAA,EAAA,IAAA,EAAAC,UAAA,CAlGK,QAAA,CAAA,UAAA,EAAA,CAAA,KAAA,KAAA;;AAIR,QAAA,CAAA,KAAK,CAAC,IAAI,KAAA,QAAA;0BADhBC,WAaU,CAAA,mBAAA,EAAA;cAXT,GAAG,EAAE,KAAK,CAAC,EAAE;AACb,cAAA,OAAO,EAAE,KAAA,CAAA,WAAW,CAAC,KAAM,CAAA,EAAE,CAAA,GAAA,SAAA,GAAA,WAAA;AAC7B,cAAA,QAAQ,EAAE,OAAO,CAAC,mBAAa,CAAC,KAAK,CAAC,EAAG,CAAA,CAAA;cACzC,aAAW,EAAA,CAAA,iBAAA,EAAsB,KAAC,CAAA,EAAA,CAAA,CAAA;cAClC,cAAS,EAAA,MAAA,CAAA,OAAA,CAAA,KAAA,CAAA,WAAA,CAAA,KAAA,CAAA,EAAA,CAAA,CAAA,CAAA;cACT,OAAK,EAAA,MAAA,KAAE,QAAa,CAAA,aAAA,CAAC,KAAI,CAAA,CAAA;;+BAIf;gCAAA,iCACN,CAAC,KAAA,CAAA,WAAW,CAAC,KAAK,CAAC,EAAE,CAAA,IAAK,KAAK,CAAC,OAAO,IAAI,KAAK,CAAE,KAAI,KAAK,KAAA,CAAA,QAAA,IAAA,KAAA,CAAA,KAAA,CAAA,CAAA,CAAA,EAAA,CAAA,YAAA;;;;AAJvD,cAAA,CAAA,KAAA,CAAA,IAAA;;0BAAA,MAAA;gCACR,MAAgE;uBAAlD,QAAa,CAAA,aAAA,CAAC,KAAM,CAAA,IAAI,CAAA;wCAAtCA,WAAgE,CAAA,iBAAA,EAAA;;4BAAtB,IAAI,EAAE,KAAK,CAAA,IAAK;AAAG,4BAAA,IAAG,EAAA,EAAA;;0DAC5B,CAAA,MAAA,EAAA;;4BAAtB,KAAK,EAAAJ,cAAA,CAAE,KAAK,CAAC,IAAI,CAAA;;;;;;;AAUrB,YAAA,CAAA,KAAK,CAAC,IAAI;;gBACpB,GAAG,EAAE,KAAK,CAAC,EAAE;gBACb,OAAO,EAAE,KAAK,CAAC,OAAO,IAAA,WAAA;gBACtB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,MAAM,EAAE,QAAY,CAAA,YAAA,CAAC,KAAK,CAAA;gBAC1B,aAAS,EAAA,CAAA,UAAA,EAAA,KAAA,CAAA,EAAA,CAAA,CAAA;AACT,gBAAA,OAAK,EAAE,MAAA,KAAA,QAAA,CAAA,WAAW,CAAC,KAAA,EAAA,MAAA,CAAA,CAAA;;iCAIT,MACX;AADW,kBAAAK,eAAA,CAAA,GACX,GAAEC,eAAA,CAAA,QAAA,CAAA,EAAG,CAAC,KAAK,CAAC,KAAK,CAAA,CAAA,EAAA,CAAA,YAAA;;;;AAJR,gBAAA,CAAA,KAAA,CAAA,IAAA;;4BAAA,MAAA;kCACR,MAAkE;yBAApD,QAAY,CAAA,aAAA,CAAE,KAAK,CAAE,IAAG,CAAA;0CAAtCF,WAAkE,CAAA,iBAAA,EAAA;;8BAAxB,IAAI,EAAG,KAAI,CAAC,IAAI;AAAG,8BAAA,IAAI,EAAC,EAAA;;4DAChC,CAAA,MAAA,EAAA;;8BAApB,KAAK,EAAAJ,cAAA,CAAE,KAAK,CAAC,IAAI,CAAA;;;;;;;4BAahCI,WAYU,CAAA,mBAAA,EAAA;gBAVT,GAAG,EAAE,KAAK,CAAC,EAAE;gBACb,OAAM,EAAG,KAAK,CAAC,OAAO,IAAA,WAAA;AACtB,gBAAA,QAAQ,EAAE,OAAO,CAAC,mBAAa,MAAG,CAAA,EAAA,CAAA,CAAA;gBAClC,aAAS,EAAA,CAAA,UAAA,EAAA,KAAA,CAAA,EAAA,CAAA,CAAA;gBACT,OAAK,EAAA,MAAA,KAAE,QAAY,CAAA,aAAA,CAAA,KAAA,CAAA,CAAA;;iCAIT;AAAA,kBAAAC,eAAA,CAAA,iCACN,CAAC,KAAK,CAAC,KAAK,CAAA,CAAA,EAAA,CAAA,YAAA;;;;AAJR,gBAAA,CAAA,KAAA,CAAA,IAAA;;4BAAA,MAAA;kCACR,MAAkE;yBAApD,QAAa,CAAA,aAAA,CAAC,KAAK,CAAC,IAAK,CAAA;0CAAvCD,WAAkE,CAAA,iBAAA,EAAA;;8BAAxB,IAAI,EAAE,KAAK,CAAC,IAAK;AAAE,8BAAA,IAAI,EAAC,EAAA;;4DAC/B,CAAA,MAAA,EAAA;;8BAArB,KAAK,EAAAJ,cAAA,CAAE,KAAI,CAAE,IAAI,CAAA;;;;;;;AAY1B,QAAA,CAAA,QAAA,CAAA,WAAO,CAAA,KAAA,CAAA;qCA+BN,CAAA,oBAAA,EAAA;cA9BL,GAAE,EAAA,CAAA,EAAK,KAAK,CAAC,EAAE,CAAA,SAAA,CAAA;sBACR,EAAE,QAAa,CAAA,aAAA,CAAC,KAAC,CAAA;cACxB,aAAU,EAAA,CAAA,mBAAA,EAAO,KAAA,CAAA,EAAA,CAAA,CAAA;;+BACT,MAAoB;qDA0BnBE,QA1Be,EAAA,IAAA,EAAAC,UAAA,CAAA,QAAA,CAAA,eAAI,CAAA,KAAA,IAAb,KAAK,KAAA;;AAEd,oBAAA,CAAA,KAAK,CAAC,IAAI;sCADhBC,WAYc,CAAA,uBAAA,EAAA;0BAVb,GAAG,EAAA,CAAA,EAAI,KAAM,CAAC,EAAE,CAAA,KAAA,CAAA;0BAChB,IAAI,EAAE,KAAK,CAAC,IAAI;0BAChB,MAAM,EAAE,QAAY,CAAA,YAAA,CAAC,KAAK,CAAA;0BAC1B,aAAS,EAAA,CAAA,UAAA,EAAA,KAAA,CAAA,EAAA,CAAA,CAAA;AACT,0BAAA,OAAK,EAAE,MAAA,KAAA,QAAA,CAAA,WAAW,CAAC,KAAA,EAAA,MAAA,CAAA,CAAA;;2CAIT,MACX;AADW,4BAAAC,eAAA,CAAA,GACX,GAAGC,eAAA,CAAA,QAAA,CAAA,EAAE,CAAC,KAAK,CAAC,KAAK,CAAA,CAAA,EAAA,CAAA,YAAA;;;;AAJJ,0BAAA,CAAA,KAAA,CAAA,IAAA;;sCAAA,MAAA;4CACZ,MAAY;mCAAA,QAAA,CAAA,aAAA,CAAA,KAAA,CAAA,IAAA,CAAA;oDAAZF,WAAY,CAAA,iBAAA,EAAA;;wCAAA,IAAA,EAAA,KAAA,CAAA,IAAA;AAAA,wCAAA,IAAA,EAAA,EAAA;;oDACZH,kBAAI,CAAA,MAAA,EAAA;;wCAAA,KAAA,EAAAD,cAAA,CAAA,KAAA,CAAA,IAAA,CAAA;;;;;;;iDAeW,CAAA,yBAAA,EAAA;0BATf,GAAG,EAAE,KAAK,CAAC,EAAE;AACb,0BAAA,QAAQ,EAAE,OAAO,CAAC,mBAAa,MAAG,CAAA,EAAA,CAAA,CAAA;0BAClC,aAAS,EAAA,CAAA,UAAA,EAAA,KAAA,CAAA,EAAA,CAAA,CAAA;0BACT,OAAK,EAAA,MAAA,KAAE,QAAY,CAAA,aAAA,CAAA,KAAA,CAAA,CAAA;;2CAId;AAAA,4BAAAK,eAAA,CAAA;;;;AAHS,0BAAA,CAAA,KAAA,CAAA,IAAA;;sCAAA,MAAA;4CACd,MAAO;mCAAA,QAAA,CAAA,aAAA,CAAA,KAAA,CAAA,IAAA,CAAA;oDAAPD,WAAO,CAAA,iBAAA,EAAA;;wCAAA,IAAA,EAAA,KAAA,CAAA,IAAA;AAAA,wCAAA,IAAA,EAAA,EAAA;;oDACPH,kBAAO,CAAA,MAAA,EAAA;;wCAAA,KAAA,EAAAD,cAAA,CAAA,KAAA,CAAA,IAAA,CAAA;;;;;;;;;;;;;;;AAWL,IAAA,CAAA,QAAA,CAAA,eAAO,CAAA,MAAA,GAAA,CAAA;sBADbI,WAgCW,CAAA,oBAAA,EAAA;;AA9BT,UAAA,QAAO,EAAE,QAAO,CAAA,gBAAA;mBACR,EAAE,IAAI;AACd,UAAA,aAAS,EAAC,4BAAO;;2BACT,MAAoB;gDA0BnB,CAAAF,QAAA,EAAA,IAAA,EAAAC,UAAA,CA1Be,QAAI,CAAA,eAAA,EAAA,CAAb,KAAK,KAAA;;AAEd,gBAAA,CAAA,KAAK,CAAC,IAAI;kCADhBC,WAYc,CAAA,uBAAA,EAAA;sBAVb,GAAG,EAAA,CAAA,EAAI,KAAM,CAAC,EAAE,CAAA,KAAA,CAAA;sBAChB,IAAI,EAAE,KAAK,CAAC,IAAI;sBAChB,MAAM,EAAE,QAAY,CAAA,YAAA,CAAC,KAAK,CAAA;sBAC1B,aAAS,EAAA,CAAA,UAAA,EAAA,KAAA,CAAA,EAAA,CAAA,CAAA;AACT,sBAAA,OAAK,EAAE,MAAA,KAAA,QAAA,CAAA,WAAW,CAAC,KAAA,EAAA,MAAA,CAAA,CAAA;;uCAIT,MACX;AADW,wBAAAC,eAAA,CAAA,GACX,GAAGC,eAAA,CAAA,QAAA,CAAA,EAAE,CAAC,KAAK,CAAC,KAAK,CAAA,CAAA,EAAA,CAAA,YAAA;;;;AAJJ,sBAAA,CAAA,KAAA,CAAA,IAAA;;kCAAA,MAAA;wCACZ,MAAY;+BAAA,QAAA,CAAA,aAAA,CAAA,KAAA,CAAA,IAAA,CAAA;gDAAZF,WAAY,CAAA,iBAAA,EAAA;;oCAAA,IAAA,EAAA,KAAA,CAAA,IAAA;AAAA,oCAAA,IAAA,EAAA,EAAA;;gDACZH,kBAAI,CAAA,MAAA,EAAA;;oCAAA,KAAA,EAAAD,cAAA,CAAA,KAAA,CAAA,IAAA,CAAA;;;;;;;6CAeO,CAAA,yBAAA,EAAA;sBATX,GAAG,EAAE,KAAK,CAAC,EAAE;AACb,sBAAA,QAAQ,EAAE,OAAO,CAAC,mBAAa,MAAG,CAAA,EAAA,CAAA,CAAA;sBAClC,aAAS,EAAA,CAAA,UAAA,EAAA,KAAA,CAAA,EAAA,CAAA,CAAA;sBACT,OAAK,EAAA,MAAA,KAAE,QAAY,CAAA,aAAA,CAAA,KAAA,CAAA,CAAA;;6CAKpB;2CAAA,GAAGM,eAAA,CAAA,QAAA,CAAA,EAAE,CAAC,KAAK,CAAC,KAAK,CAAA,CAAA,EAAA,CAAA,YAAA;;;;AAJF,sBAAA,CAAA,KAAA,CAAA,IAAA;;kCAAA,MAAA;wCACd,MAAO;+BAAA,QAAA,CAAA,aAAA,CAAA,KAAA,CAAA,IAAA,CAAA;gDAAPF,WAAO,CAAA,iBAAA,EAAA;;oCAAA,IAAA,EAAA,KAAA,CAAA,IAAA;AAAA,oCAAA,IAAA,EAAA,EAAA;;gDACPH,kBAAO,CAAA,MAAA,EAAA;;oCAAA,KAAA,EAAAD,cAAA,CAAA,KAAA,CAAA,IAAA,CAAA;;;;;;;;;;;;;KASJ,KAAY,CAAA,YAAA;iCAMa,CAAA,0BAAA,EAAA;;AAL/B,UAAA,GAAG,EAAC,eAAe;;AAElB,UAAA,OAAO,EAAE,QAAE,CAAA,EAAA,CAAC,kBAAY,CAAC,cAAc,KAAK,KAAoB,CAAA,qBAAA;iBACxD,EAAC,KAAY,CAAA,YAAA,CAAC,OAAO,KAAA,OAAA,GAAA,OAAA,GAAA,SAAA;mBACtB,EAAE,QAAgB,CAAA,gBAAA;iBACpB,uCAAE,KAAY,CAAA,YAAA,GAAA,IAAA,CAAA,CAAA;;;AAQd,IAAA,CAAA,KAAA,CAAA,SAAS,IAAI,KAAA,CAAA,UAAU,IAAI,CAAA,KAAA,CAAA,SAAI,CAAA,QAAA;iCAe1B,CAAA,uBAAA,EAAA;;AAdX,UAAA,GAAG,EAAC,YAAY;AACf,UAAA,MAAM,EAAE,KAAU,CAAA,UAAA;AAClB,UAAA,IAAI,EAAE,IAAI;AACV,UAAA,QAAQ,EAAE,QAAY,CAAA,YAAA;AACtB,UAAA,WAAW,EAAE,QAAiB,CAAA,iBAAA,IAAA,EAAA;AAC9B,UAAA,WAAW,EAAE,QAAA,CAAA,EAAE,CAAC,KAAA,CAAA,SAAS,CAAC,SAAS,CAAA,IAAA,EAAA;AACnC,UAAA,OAAO,EAAE,QAAA,CAAA,EAAE,CAAC,KAAA,CAAA,SAAS,CAAC,WAAU,CAAA,IAAA,EAAA;AAChC,UAAA,QAAQ,EAAE,QAAA,CAAA,EAAE,CAAC,KAAA,CAAA,SAAS,CAAC,YAAQ,CAAA,IAAA,EAAA;UAC/B,aAAa,EAAE,KAAO,CAAA,SAAA,CAAA,aAAA,IAAA,IAAA;UACtB,aAAa,EAAE,KAAI,CAAA,SAAA,CAAA,aAAA,IAAA,EAAA;UACnB,cAAc,EAAE,KAAE,CAAA,SAAA,CAAA,cAAA,IAAA,EAAA;UAClB,IAAI,EAAE,KAAS,CAAA,SAAA,CAAC,IAAI,IAAA,QAAA;UACpB,OAAO,EAAE,KAAK,CAAA,SAAA,CAAA,OAAA,IAAA,CAAA;AACd,UAAA,SAAO,EAAE,QAAS,CAAA,aAAA;AAClB,UAAA,OAAK,EAAE,QAAG,CAAA,SAAA;;;AAEL,IAAA,CAAA,KAAA,CAAA,SAAS,IAAI,KAAA,CAAA,UAAI,IAAA,KAAA,CAAA,SAAA,CAAA,QAAA;sBADvBI,WAOqB,CAAA,+BAAA,EAAA;;AALrB,UAAA,GAAG,EAAC,YAAY;;AAEf,UAAA,IAAI,EAAE,IAAI;uBACG,EAAE,QAAkB,CAAA,iBAAA;mBAC1B,EAAE,QAAa,CAAA,aAAA;iBAClB,EAAG,QAAS,CAAA,SAAA;;;KASX,KAAY,CAAA,YAAA,IAAI,KAAI,CAAA,aAAA,CAAA,MAAA;sBAD1BA;;UAEC,KAAK,EAAE,QAAE,CAAA,EAAA,CAAC,KAAY,CAAA,YAAA,CAAA,SAAA,CAAA,IAAA,QAAA,CAAA,EAAA,CAAA,KAAA,CAAA,YAAA,CAAA,KAAA,CAAA;AACtB,UAAA,MAAE,EAAA,KAAA,CAAA,aAAA;;;;;;;;;;;;"}
@@ -2,7 +2,7 @@ import script from './CnActionDialog.vue2.js';
2
2
  import { render } from './CnActionDialog.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-0bb28fd1";
5
+ script.__scopeId = "data-v-5293ec77";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -44,8 +44,9 @@ let reasonSeq = 0;
44
44
  * required?, options?: [{ value, label, fills? }] }]`: inputs sent beside
45
45
  * the reason, required by default; a select option's `fills` sets other
46
46
  * fields that are still empty.
47
- * - `reason`: `{ label?, hint?, required?, field? }`: a text area; required by
48
- * default; sent as `field` (default `reason`).
47
+ * - `reason`: `{ label?, hint?, required?, field?, rows? }`: a text area; required by
48
+ * default; sent as `field` (default `reason`). `rows` is its height in lines
49
+ * (default 2; board DqBesluit draws 4).
49
50
  * - `note`: `{ variant?, title?, text }`: a read-only note.
50
51
  * - `confirm`: `{ label, icon?, request?: { method?, url, body? }, action? }`.
51
52
  * `request` posts itself (`body` templates filled with the record and
@@ -1 +1 @@
1
- {"version":3,"file":"CnActionDialog.vue2.js","sources":["../../../../src/components/CnActionDialog/CnActionDialog.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnDialog\n\t\t:name=\"text(config.title) || t('nextcloud-vue', 'Confirm')\"\n\t\t:eyebrow=\"eyebrowText\"\n\t\t:subtitle=\"text(config.subtitle)\"\n\t\twidth=\"form\"\n\t\t:noClose=\"busy\"\n\t\tdata-testid=\"cn-action-dialog\"\n\t\t@closing=\"close\">\n\t\t<div v-if=\"!done\" class=\"cn-action-dialog\">\n\t\t\t<!-- The record the action is about: its title, a meta line, a badge. -->\n\t\t\t<div v-if=\"summary\" class=\"cn-action-dialog__summary\" data-testid=\"cn-action-dialog-summary\">\n\t\t\t\t<CnIcon :name=\"config.summary.icon || 'FolderOutline'\" :size=\"20\" class=\"cn-action-dialog__summary-icon\" />\n\t\t\t\t<span class=\"cn-action-dialog__summary-text\">\n\t\t\t\t\t<strong>{{ summary.title }}</strong>\n\t\t\t\t\t<span v-if=\"summary.meta\" class=\"cn-action-dialog__muted\">{{ summary.meta }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span v-if=\"summary.badge\" class=\"cn-action-dialog__badge\">{{ summary.badge }}</span>\n\t\t\t</div>\n\n\t\t\t<!-- Facts about the record, in titled groups (\"Retention under the case type\"). -->\n\t\t\t<section v-for=\"(section, index) in factSections\"\n\t\t\t\t:key=\"`facts-${index}`\"\n\t\t\t\tclass=\"cn-action-dialog__section\"\n\t\t\t\tdata-testid=\"cn-action-dialog-facts\">\n\t\t\t\t<h3 v-if=\"section.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ section.title }}\n\t\t\t\t</h3>\n\t\t\t\t<dl class=\"cn-action-dialog__facts\">\n\t\t\t\t\t<div v-for=\"fact in section.facts\" :key=\"fact.label\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ fact.value }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t</section>\n\n\t\t\t<!-- The checklist, read from an endpoint: each item met, not met or\n\t\t\t unknown. Items named in `required` decide whether the action may\n\t\t\t run; the others inform. -->\n\t\t\t<section v-if=\"config.checklist\" class=\"cn-action-dialog__section\" data-testid=\"cn-action-dialog-checklist\">\n\t\t\t\t<h3 v-if=\"config.checklist.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ text(config.checklist.title) }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"checklistError\" class=\"cn-action-dialog__muted\" role=\"status\">\n\t\t\t\t\t{{ checklistError }}\n\t\t\t\t</p>\n\t\t\t\t<ul class=\"cn-action-dialog__checks\">\n\t\t\t\t\t<li v-for=\"item in checklist\"\n\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\tclass=\"cn-action-dialog__check\"\n\t\t\t\t\t\t:data-testid=\"`cn-action-dialog-check-${item.id}`\"\n\t\t\t\t\t\t:data-met=\"String(item.met)\">\n\t\t\t\t\t\t<CheckCircle v-if=\"item.met === true\" :size=\"20\" class=\"cn-action-dialog__check-icon cn-action-dialog__check-icon--met\" />\n\t\t\t\t\t\t<HelpCircleOutline v-else-if=\"item.met === null\" :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<CircleOutline v-else :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<strong class=\"cn-action-dialog__check-label\">{{ item.label }}</strong>\n\t\t\t\t\t\t<span v-if=\"item.detail\" class=\"cn-action-dialog__muted\">{{ item.detail }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\n\t\t\t<!-- Fields the action asks for, sent beside the reason. -->\n\t\t\t<div v-for=\"field in fields\"\n\t\t\t\t:key=\"field.field\"\n\t\t\t\tclass=\"cn-action-dialog__field\"\n\t\t\t\t:data-testid=\"`cn-action-dialog-field-${field.field}`\">\n\t\t\t\t<NcSelect v-if=\"field.type === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"optionsOf(field)\"\n\t\t\t\t\t:inputLabel=\"text(field.label) || field.field\"\n\t\t\t\t\t:clearable=\"field.required === false\"\n\t\t\t\t\t@update:modelValue=\"(option) => pick(field, option)\" />\n\t\t\t\t<NcTextField v-else\n\t\t\t\t\t:modelValue=\"values[field.field] || ''\"\n\t\t\t\t\t:type=\"field.type === 'datetime' ? 'datetime-local' : (field.type === 'date' ? 'date' : 'text')\"\n\t\t\t\t\t:label=\"text(field.label) || field.field\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value)\" />\n\t\t\t</div>\n\n\t\t\t<div v-if=\"config.reason\" class=\"cn-action-dialog__field\">\n\t\t\t\t<NcTextArea\n\t\t\t\t\t:id=\"reasonId\"\n\t\t\t\t\tv-model=\"reason\"\n\t\t\t\t\t:label=\"reasonLabel\"\n\t\t\t\t\t:labelOutside=\"false\"\n\t\t\t\t\tresize=\"vertical\"\n\t\t\t\t\tdata-testid=\"cn-action-dialog-reason\" />\n\t\t\t\t<span v-if=\"config.reason.hint\" class=\"cn-action-dialog__muted\">{{ text(config.reason.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<NcNoteCard v-if=\"config.note\"\n\t\t\t\t:type=\"noteType\"\n\t\t\t\t:heading=\"text(config.note.title)\"\n\t\t\t\tdata-testid=\"cn-action-dialog-note\">\n\t\t\t\t{{ text(config.note.text) }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"refusal\"\n\t\t\t\tclass=\"cn-action-dialog__error\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-action-dialog-refusal\">\n\t\t\t\t{{ refusal }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p v-else role=\"status\" data-testid=\"cn-action-dialog-done\">\n\t\t\t{{ text(config.doneText) || t('nextcloud-vue', 'Done') }}\n\t\t</p>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"busy\" data-testid=\"cn-action-dialog-cancel\" @click=\"close\">\n\t\t\t\t{{ done ? t('nextcloud-vue', 'Close') : t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton v-if=\"!done\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"!canConfirm\"\n\t\t\t\tdata-testid=\"cn-action-dialog-confirm\"\n\t\t\t\t@click=\"confirm\">\n\t\t\t\t<template v-if=\"confirmConfig.icon\" #icon>\n\t\t\t\t\t<CnIcon :name=\"confirmConfig.icon\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ text(confirmConfig.label) || t('nextcloud-vue', 'Confirm') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnDialog>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport CheckCircle from 'vue-material-design-icons/CheckCircle.vue'\nimport CircleOutline from 'vue-material-design-icons/CircleOutline.vue'\nimport HelpCircleOutline from 'vue-material-design-icons/HelpCircleOutline.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { CnDialog } from '../CnDialog/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/** The page refresh channel the built-in widgets listen on. */\nconst PAGE_REFRESH = 'cn:page:refresh'\n\n/** Gives each dialog's reason field its own id. */\nlet reasonSeq = 0\n\n/**\n * CnActionDialog — the generic \"summary, checklist, reason, confirm\" dialog\n * for an act on a record, configured from the manifest (an `open-modal`\n * action's props), so an app does not hand-build each one. dossiq's archive\n * dialog (board DqArchiveren) is the first: the case, what its case type\n * keeps, a readiness checklist read from an endpoint, a reason, a read-only\n * note and Archive it.\n *\n * `config`:\n * - `title`, `subtitle`, `doneText`: texts, through the host translate;\n * `eyebrow` a template filled from the record (\"Close off · {identifier}\").\n * - `summary`: `{ source?, title, meta?, badge?, icon? }`: templates filled from\n * the record (`source` an endpointSource to read it from, else `object`).\n * - `facts`: `[{ title?, items: [{ label, value }] }]`, `value` a template.\n * - `checklist`: `{ title?, source, itemsPath?, labels?, required? }`: items\n * `{ id, met, label?, detail? }` read from `source`; `labels[id]` gives an\n * item's `{ label, detail?, unmetDetail? }`; the ids in `required` must be\n * met before the action can run. `met: null` reads as unknown.\n * - `permission`: `{ source, listPath?, match?, allowedPath, reasonPath? }`:\n * the action runs only while `allowedPath` reads true; `reasonPath` says\n * why not. With `listPath` the paths read the entry of that list whose\n * fields equal `match` (`{ act: \"archive\" }`).\n * - `fields`: `[{ field, type?: \"text\"|\"date\"|\"datetime\"|\"select\", label?,\n * required?, options?: [{ value, label, fills? }] }]`: inputs sent beside\n * the reason, required by default; a select option's `fills` sets other\n * fields that are still empty.\n * - `reason`: `{ label?, hint?, required?, field? }`: a text area; required by\n * default; sent as `field` (default `reason`).\n * - `note`: `{ variant?, title?, text }`: a read-only note.\n * - `confirm`: `{ label, icon?, request?: { method?, url, body? }, action? }`.\n * `request` posts itself (`body` templates filled with the record and\n * `{reason}`); `action` dispatches a manifest action through the page.\n *\n * Tokens (`@objectId`, `@object.<field>`) resolve in every url.\n */\nexport default {\n\tname: 'CnActionDialog',\n\n\tcomponents: { CheckCircle, CircleOutline, CnDialog, CnIcon, HelpCircleOutline, NcButton, NcNoteCard, NcSelect, NcTextArea, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's pre-bound action dispatcher (CnPageRenderer), for `confirm.action`. */\n\t\tcnDispatchAction: { default: null },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The dialog's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tconfig: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record, when the host has it: the summary and facts read it.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['close', 'done'],\n\n\tdata() {\n\t\treturn {\n\t\t\trecord: this.object,\n\t\t\titems: [],\n\t\t\tchecklistError: '',\n\t\t\tallowed: true,\n\t\t\treason: '',\n\t\t\tvalues: {},\n\t\t\tbusy: false,\n\t\t\trefusal: '',\n\t\t\tdone: false,\n\t\t\treasonId: `cn-action-dialog-reason-${++reasonSeq}`,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The token context. */\n\t\ttokenCtx() {\n\t\t\treturn { objectId: this.recordId, object: this.record || {} }\n\t\t},\n\n\t\t/**\n\t\t * The record's id: `objectId`, or the route's `id` when the prop is\n\t\t * empty or a token the opener could not resolve.\n\t\t *\n\t\t * @return {string|number}\n\t\t */\n\t\trecordId() {\n\t\t\tconst given = this.objectId\n\t\t\tif (given !== '' && given !== null && given !== undefined && !String(given).startsWith('@')) {\n\t\t\t\treturn given\n\t\t\t}\n\t\t\treturn (this.$route && this.$route.params && this.$route.params.id) || ''\n\t\t},\n\n\t\t/** @return {object} The confirm config. */\n\t\tconfirmConfig() {\n\t\t\treturn this.config.confirm && typeof this.config.confirm === 'object' ? this.config.confirm : {}\n\t\t},\n\n\t\t/**\n\t\t * The summary row, or null without one or before the record is known.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {{title: string, meta: string, badge: string}|null}\n\t\t */\n\t\tsummary() {\n\t\t\tconst def = this.config.summary\n\t\t\tif (!def || !this.record) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst title = this.fill(def.title)\n\t\t\treturn title ? { title, meta: this.fill(def.meta), badge: this.fill(def.badge) } : null\n\t\t},\n\n\t\t/** @return {Array<{title: string, facts: Array<{label: string, value: string}>}>} The fact groups. */\n\t\tfactSections() {\n\t\t\tif (!Array.isArray(this.config.facts) || !this.record) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst empty = t('nextcloud-vue', 'Not set')\n\t\t\treturn this.config.facts.map((section) => ({\n\t\t\t\ttitle: this.text(section.title),\n\t\t\t\tfacts: (Array.isArray(section.items) ? section.items : []).map((fact) => ({ label: this.text(fact.label), value: this.fill(fact.value) || empty })),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * The checklist rows: label and detail from `labels` by id, else the item's own.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Array<{id: string, met: (boolean|null), label: string, detail: string}>}\n\t\t */\n\t\tchecklist() {\n\t\t\tconst labels = (this.config.checklist && this.config.checklist.labels) || {}\n\t\t\treturn this.items.map((item) => {\n\t\t\t\tconst met = item.met === null || item.met === undefined ? null : item.met === true\n\t\t\t\tconst own = labels[item.id] || {}\n\t\t\t\tconst detail = met === false && own.unmetDetail ? own.unmetDetail : (own.detail || item.detail || '')\n\t\t\t\treturn {\n\t\t\t\t\tid: String(item.id),\n\t\t\t\t\tmet,\n\t\t\t\t\tlabel: this.fill(own.label || item.label || String(item.id), item),\n\t\t\t\t\tdetail: met === null && !item.detail ? t('nextcloud-vue', 'Could not be read') : this.fill(detail, item),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether every required item is met. */\n\t\trequiredMet() {\n\t\t\tconst required = (this.config.checklist && Array.isArray(this.config.checklist.required)) ? this.config.checklist.required.map(String) : []\n\t\t\treturn required.every((id) => this.items.some((item) => String(item.id) === id && item.met === true))\n\t\t},\n\n\t\t/** @return {boolean} Whether a reason is asked and needed. */\n\t\treasonNeeded() {\n\t\t\treturn Boolean(this.config.reason) && this.config.reason.required !== false\n\t\t},\n\n\t\t/** @return {string} The reason field's label. */\n\t\treasonLabel() {\n\t\t\treturn this.text(this.config.reason && this.config.reason.label) || t('nextcloud-vue', 'Reason')\n\t\t},\n\n\t\t/** @return {string} The note's NcNoteCard type. */\n\t\tnoteType() {\n\t\t\tconst variant = this.config.note && this.config.note.variant\n\t\t\treturn ['info', 'warning', 'error', 'success'].includes(variant) ? variant : 'info'\n\t\t},\n\n\t\t/**\n\t\t * Whether the action may run: allowed, required items met, a reason\n\t\t * when one is needed, and nothing in flight.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {boolean}\n\t\t */\n\t\tcanConfirm() {\n\t\t\treturn !this.busy && this.allowed && this.requiredMet && this.fieldsFilled && (!this.reasonNeeded || this.reason.trim() !== '')\n\t\t},\n\n\t\t/** @return {string} The eyebrow, filled from the record once it is known. */\n\t\teyebrowText() {\n\t\t\treturn this.record ? this.fill(this.config.eyebrow) || this.text(this.config.eyebrow) : this.text(this.config.eyebrow)\n\t\t},\n\n\t\t/** @return {Array<object>} The fields with a name. */\n\t\tfields() {\n\t\t\treturn (Array.isArray(this.config.fields) ? this.config.fields : []).filter((f) => f && f.field)\n\t\t},\n\n\t\t/**\n\t\t * Whether every required field has a value.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-fields-and-acts/specs/action-dialog/spec.md#requirement-an-action-dialog-asks-for-fields-and-reads-a-permission-from-a-list\n\t\t * @return {boolean}\n\t\t */\n\t\tfieldsFilled() {\n\t\t\treturn this.fields.every((f) => f.required === false || String(this.values[f.field] ?? '').trim() !== '')\n\t\t},\n\t},\n\n\tasync mounted() {\n\t\tawait Promise.all([this.loadRecord(), this.loadChecklist(), this.loadPermission()])\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate, '' for anything else.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * A template filled from a row (the record by default), translated first.\n\t\t *\n\t\t * @param {unknown} template The template.\n\t\t * @param {object} [row] The row.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template, row) {\n\t\t\tif (typeof template !== 'string' || template === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn fillTemplate(this.cnTranslate(template), row || this.record || {}).trim()\n\t\t},\n\n\t\t/**\n\t\t * Read an endpoint, fresh.\n\t\t *\n\t\t * @param {object} source An endpointSource block.\n\t\t * @return {Promise<unknown>}\n\t\t */\n\t\tread(source) {\n\t\t\treturn fetchEndpointSource(source, this.tokenCtx, { fresh: true })\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadRecord() {\n\t\t\tconst source = this.config.summary && this.config.summary.source\n\t\t\tif (!source || !source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(source)\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : this.record\n\t\t\t} catch {\n\t\t\t\t// The summary stays empty; the checklist and the action still work.\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadChecklist() {\n\t\t\tconst def = this.config.checklist\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst list = def.itemsPath ? readPath(data, def.itemsPath) : data\n\t\t\t\tthis.items = Array.isArray(list) ? list.filter((item) => item && item.id !== undefined) : []\n\t\t\t} catch {\n\t\t\t\tthis.items = []\n\t\t\t\tthis.checklistError = t('nextcloud-vue', 'Whether this is ready could not be read.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The entry of a list whose fields equal `match`, or null.\n\t\t *\n\t\t * @param {unknown} list The list.\n\t\t * @param {object} [match] The fields to match.\n\t\t * @return {object|null}\n\t\t */\n\t\tmatchIn(list, match) {\n\t\t\tconst wanted = Object.entries(match || {})\n\t\t\treturn (Array.isArray(list) ? list : []).find((entry) => entry && wanted.every(([key, value]) => String(entry[key]) === String(value))) || null\n\t\t},\n\n\t\t/**\n\t\t * A select field's options, labels translated.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {Array<{id: string, label: string, fills?: object}>}\n\t\t */\n\t\toptionsOf(field) {\n\t\t\treturn (Array.isArray(field.options) ? field.options : []).map((o) => ({ id: String(o.value), label: this.text(o.label) || String(o.value), fills: o.fills }))\n\t\t},\n\n\t\t/**\n\t\t * A select field's chosen option.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\treturn this.optionsOf(field).find((o) => o.id === String(this.values[field.field] ?? '')) || null\n\t\t},\n\n\t\t/**\n\t\t * Set a field's value.\n\t\t *\n\t\t * @param {string} name The field.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\tsetValue(name, value) {\n\t\t\tthis.values = { ...this.values, [name]: value === null || value === undefined ? '' : String(value) }\n\t\t},\n\n\t\t/**\n\t\t * Choose a select option; its `fills` set the fields still empty.\n\t\t *\n\t\t * @param {object} field The select field.\n\t\t * @param {{id: string, fills?: object}|null} option The option.\n\t\t */\n\t\tpick(field, option) {\n\t\t\tthis.setValue(field.field, option ? option.id : '')\n\t\t\tfor (const [name, value] of Object.entries((option && option.fills) || {})) {\n\t\t\t\tif (String(this.values[name] ?? '').trim() === '') {\n\t\t\t\t\tthis.setValue(name, this.text(value) || value)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadPermission() {\n\t\t\tconst def = this.config.permission\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst entry = def.listPath ? this.matchIn(readPath(data, def.listPath), def.match) : data\n\t\t\t\tthis.allowed = readPath(entry, def.allowedPath || 'allowed') === true\n\t\t\t\tthis.refusal = this.allowed || !def.reasonPath ? '' : String(readPath(entry, def.reasonPath) || '')\n\t\t\t} catch {\n\t\t\t\tthis.allowed = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Run the action once: post `confirm.request`, or dispatch\n\t\t * `confirm.action`, then show the done text and refresh the page.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync confirm() {\n\t\t\tif (!this.canConfirm) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tthis.refusal = ''\n\t\t\tconst reason = this.reason.trim()\n\t\t\ttry {\n\t\t\t\tconst request = this.confirmConfig.request\n\t\t\t\tif (request && request.url) {\n\t\t\t\t\tconst url = interpolateUrlTokens(request.url, this.tokenCtx)\n\t\t\t\t\tconst body = {}\n\t\t\t\t\tfor (const [key, value] of Object.entries(request.body || {})) {\n\t\t\t\t\t\tbody[key] = typeof value === 'string' ? fillTemplate(value, { ...(this.record || {}), reason }) : value\n\t\t\t\t\t}\n\t\t\t\t\tif (this.config.reason) {\n\t\t\t\t\t\tbody[this.config.reason.field || 'reason'] = reason\n\t\t\t\t\t}\n\t\t\t\t\tfor (const field of this.fields) {\n\t\t\t\t\t\tbody[field.field] = this.values[field.field] ?? ''\n\t\t\t\t\t}\n\t\t\t\t\tconst method = String(request.method || 'POST').toLowerCase()\n\t\t\t\t\tawait axios[['post', 'put', 'patch', 'delete'].includes(method) ? method : 'post'](/^https?:\\/\\//i.test(url) ? url : generateUrl(url), body)\n\t\t\t\t} else if (this.confirmConfig.action && typeof this.cnDispatchAction === 'function') {\n\t\t\t\t\tawait this.cnDispatchAction({ ...this.confirmConfig.action, args: [...(this.confirmConfig.action.args || []), { ...this.values, reason }] })\n\t\t\t\t}\n\t\t\t\tthis.done = true\n\t\t\t\temit(PAGE_REFRESH, {})\n\t\t\t\t/** @event done The action ran. Payload: the field values and `reason`. */\n\t\t\t\tthis.$emit('done', { ...this.values, reason })\n\t\t\t} catch (failure) {\n\t\t\t\tconst data = failure && failure.response && failure.response.data\n\t\t\t\tthis.refusal = String((data && (data.message || data.error)) || t('nextcloud-vue', 'The action could not be completed.'))\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\n\t\t/** Close the dialog, unless the action is running. */\n\t\tclose() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/** @event close The dialog was closed. */\n\t\t\tthis.$emit('close')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-action-dialog {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-action-dialog__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-action-dialog__summary-text {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tmin-width: 0;\n}\n\n.cn-action-dialog__badge {\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-dark);\n\tfont-size: 13px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n}\n\n.cn-action-dialog__title {\n\tmargin: 0 0 8px;\n\tfont-size: 15px;\n\tfont-weight: 700;\n}\n\n.cn-action-dialog__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n\tgap: 8px 16px;\n\tmargin: 0;\n}\n\n.cn-action-dialog__facts dt {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__facts dd {\n\tmargin: 0;\n}\n\n.cn-action-dialog__checks {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-action-dialog__check {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-action-dialog__check-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-action-dialog__check-icon--met {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-action-dialog__muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n}\n\n.cn-action-dialog__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-action-dialog__error {\n\tmargin: 0;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["CnDialog","CnIcon","t","axios"],"mappings":"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"}
1
+ {"version":3,"file":"CnActionDialog.vue2.js","sources":["../../../../src/components/CnActionDialog/CnActionDialog.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnDialog\n\t\t:name=\"text(config.title) || t('nextcloud-vue', 'Confirm')\"\n\t\t:eyebrow=\"eyebrowText\"\n\t\t:subtitle=\"text(config.subtitle)\"\n\t\twidth=\"form\"\n\t\t:noClose=\"busy\"\n\t\tdata-testid=\"cn-action-dialog\"\n\t\t@closing=\"close\">\n\t\t<div v-if=\"!done\" class=\"cn-action-dialog\">\n\t\t\t<!-- The record the action is about: its title, a meta line, a badge. -->\n\t\t\t<div v-if=\"summary\" class=\"cn-action-dialog__summary\" data-testid=\"cn-action-dialog-summary\">\n\t\t\t\t<CnIcon :name=\"config.summary.icon || 'FolderOutline'\" :size=\"20\" class=\"cn-action-dialog__summary-icon\" />\n\t\t\t\t<span class=\"cn-action-dialog__summary-text\">\n\t\t\t\t\t<strong>{{ summary.title }}</strong>\n\t\t\t\t\t<span v-if=\"summary.meta\" class=\"cn-action-dialog__muted\">{{ summary.meta }}</span>\n\t\t\t\t</span>\n\t\t\t\t<span v-if=\"summary.badge\" class=\"cn-action-dialog__badge\">{{ summary.badge }}</span>\n\t\t\t</div>\n\n\t\t\t<!-- Facts about the record, in titled groups (\"Retention under the case type\"). -->\n\t\t\t<section v-for=\"(section, index) in factSections\"\n\t\t\t\t:key=\"`facts-${index}`\"\n\t\t\t\tclass=\"cn-action-dialog__section\"\n\t\t\t\tdata-testid=\"cn-action-dialog-facts\">\n\t\t\t\t<h3 v-if=\"section.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ section.title }}\n\t\t\t\t</h3>\n\t\t\t\t<dl class=\"cn-action-dialog__facts\">\n\t\t\t\t\t<div v-for=\"fact in section.facts\" :key=\"fact.label\" class=\"cn-action-dialog__fact\">\n\t\t\t\t\t\t<dt>{{ fact.label }}</dt>\n\t\t\t\t\t\t<dd>{{ fact.value }}</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t</section>\n\n\t\t\t<!-- The checklist, read from an endpoint: each item met, not met or\n\t\t\t unknown. Items named in `required` decide whether the action may\n\t\t\t run; the others inform. -->\n\t\t\t<section v-if=\"config.checklist\" class=\"cn-action-dialog__section\" data-testid=\"cn-action-dialog-checklist\">\n\t\t\t\t<h3 v-if=\"config.checklist.title\" class=\"cn-action-dialog__title\">\n\t\t\t\t\t{{ text(config.checklist.title) }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"checklistError\" class=\"cn-action-dialog__muted\" role=\"status\">\n\t\t\t\t\t{{ checklistError }}\n\t\t\t\t</p>\n\t\t\t\t<ul class=\"cn-action-dialog__checks\">\n\t\t\t\t\t<li v-for=\"item in checklist\"\n\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\tclass=\"cn-action-dialog__check\"\n\t\t\t\t\t\t:data-testid=\"`cn-action-dialog-check-${item.id}`\"\n\t\t\t\t\t\t:data-met=\"String(item.met)\">\n\t\t\t\t\t\t<CheckCircle v-if=\"item.met === true\" :size=\"20\" class=\"cn-action-dialog__check-icon cn-action-dialog__check-icon--met\" />\n\t\t\t\t\t\t<HelpCircleOutline v-else-if=\"item.met === null\" :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<CircleOutline v-else :size=\"20\" class=\"cn-action-dialog__check-icon\" />\n\t\t\t\t\t\t<strong class=\"cn-action-dialog__check-label\">{{ item.label }}</strong>\n\t\t\t\t\t\t<span v-if=\"item.detail\" class=\"cn-action-dialog__muted\">{{ item.detail }}</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\n\t\t\t<!-- Fields the action asks for, sent beside the reason. -->\n\t\t\t<div v-for=\"field in fields\"\n\t\t\t\t:key=\"field.field\"\n\t\t\t\tclass=\"cn-action-dialog__field\"\n\t\t\t\t:data-testid=\"`cn-action-dialog-field-${field.field}`\">\n\t\t\t\t<NcSelect v-if=\"field.type === 'select'\"\n\t\t\t\t\t:modelValue=\"selectedOption(field)\"\n\t\t\t\t\t:options=\"optionsOf(field)\"\n\t\t\t\t\t:inputLabel=\"text(field.label) || field.field\"\n\t\t\t\t\t:clearable=\"field.required === false\"\n\t\t\t\t\t@update:modelValue=\"(option) => pick(field, option)\" />\n\t\t\t\t<NcTextField v-else\n\t\t\t\t\t:modelValue=\"values[field.field] || ''\"\n\t\t\t\t\t:type=\"field.type === 'datetime' ? 'datetime-local' : (field.type === 'date' ? 'date' : 'text')\"\n\t\t\t\t\t:label=\"text(field.label) || field.field\"\n\t\t\t\t\t@update:modelValue=\"(value) => setValue(field.field, value)\" />\n\t\t\t</div>\n\n\t\t\t<div v-if=\"config.reason\" class=\"cn-action-dialog__field\">\n\t\t\t\t<NcTextArea\n\t\t\t\t\t:id=\"reasonId\"\n\t\t\t\t\tv-model=\"reason\"\n\t\t\t\t\t:label=\"reasonLabel\"\n\t\t\t\t\t:labelOutside=\"false\"\n\t\t\t\t\t:rows=\"config.reason.rows || 2\"\n\t\t\t\t\tresize=\"vertical\"\n\t\t\t\t\tdata-testid=\"cn-action-dialog-reason\" />\n\t\t\t\t<span v-if=\"config.reason.hint\" class=\"cn-action-dialog__muted\">{{ text(config.reason.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<NcNoteCard v-if=\"config.note\"\n\t\t\t\t:type=\"noteType\"\n\t\t\t\t:heading=\"text(config.note.title)\"\n\t\t\t\tdata-testid=\"cn-action-dialog-note\">\n\t\t\t\t{{ text(config.note.text) }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"refusal\"\n\t\t\t\tclass=\"cn-action-dialog__error\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-action-dialog-refusal\">\n\t\t\t\t{{ refusal }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p v-else role=\"status\" data-testid=\"cn-action-dialog-done\">\n\t\t\t{{ text(config.doneText) || t('nextcloud-vue', 'Done') }}\n\t\t</p>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"busy\" data-testid=\"cn-action-dialog-cancel\" @click=\"close\">\n\t\t\t\t{{ done ? t('nextcloud-vue', 'Close') : t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton v-if=\"!done\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"!canConfirm\"\n\t\t\t\tdata-testid=\"cn-action-dialog-confirm\"\n\t\t\t\t@click=\"confirm\">\n\t\t\t\t<template v-if=\"confirmConfig.icon\" #icon>\n\t\t\t\t\t<CnIcon :name=\"confirmConfig.icon\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ text(confirmConfig.label) || t('nextcloud-vue', 'Confirm') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnDialog>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'\nimport CheckCircle from 'vue-material-design-icons/CheckCircle.vue'\nimport CircleOutline from 'vue-material-design-icons/CircleOutline.vue'\nimport HelpCircleOutline from 'vue-material-design-icons/HelpCircleOutline.vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { CnDialog } from '../CnDialog/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/** The page refresh channel the built-in widgets listen on. */\nconst PAGE_REFRESH = 'cn:page:refresh'\n\n/** Gives each dialog's reason field its own id. */\nlet reasonSeq = 0\n\n/**\n * CnActionDialog — the generic \"summary, checklist, reason, confirm\" dialog\n * for an act on a record, configured from the manifest (an `open-modal`\n * action's props), so an app does not hand-build each one. dossiq's archive\n * dialog (board DqArchiveren) is the first: the case, what its case type\n * keeps, a readiness checklist read from an endpoint, a reason, a read-only\n * note and Archive it.\n *\n * `config`:\n * - `title`, `subtitle`, `doneText`: texts, through the host translate;\n * `eyebrow` a template filled from the record (\"Close off · {identifier}\").\n * - `summary`: `{ source?, title, meta?, badge?, icon? }`: templates filled from\n * the record (`source` an endpointSource to read it from, else `object`).\n * - `facts`: `[{ title?, items: [{ label, value }] }]`, `value` a template.\n * - `checklist`: `{ title?, source, itemsPath?, labels?, required? }`: items\n * `{ id, met, label?, detail? }` read from `source`; `labels[id]` gives an\n * item's `{ label, detail?, unmetDetail? }`; the ids in `required` must be\n * met before the action can run. `met: null` reads as unknown.\n * - `permission`: `{ source, listPath?, match?, allowedPath, reasonPath? }`:\n * the action runs only while `allowedPath` reads true; `reasonPath` says\n * why not. With `listPath` the paths read the entry of that list whose\n * fields equal `match` (`{ act: \"archive\" }`).\n * - `fields`: `[{ field, type?: \"text\"|\"date\"|\"datetime\"|\"select\", label?,\n * required?, options?: [{ value, label, fills? }] }]`: inputs sent beside\n * the reason, required by default; a select option's `fills` sets other\n * fields that are still empty.\n * - `reason`: `{ label?, hint?, required?, field?, rows? }`: a text area; required by\n * default; sent as `field` (default `reason`). `rows` is its height in lines\n * (default 2; board DqBesluit draws 4).\n * - `note`: `{ variant?, title?, text }`: a read-only note.\n * - `confirm`: `{ label, icon?, request?: { method?, url, body? }, action? }`.\n * `request` posts itself (`body` templates filled with the record and\n * `{reason}`); `action` dispatches a manifest action through the page.\n *\n * Tokens (`@objectId`, `@object.<field>`) resolve in every url.\n */\nexport default {\n\tname: 'CnActionDialog',\n\n\tcomponents: { CheckCircle, CircleOutline, CnDialog, CnIcon, HelpCircleOutline, NcButton, NcNoteCard, NcSelect, NcTextArea, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's pre-bound action dispatcher (CnPageRenderer), for `confirm.action`. */\n\t\tcnDispatchAction: { default: null },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The dialog's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tconfig: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record, when the host has it: the summary and facts read it.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['close', 'done'],\n\n\tdata() {\n\t\treturn {\n\t\t\trecord: this.object,\n\t\t\titems: [],\n\t\t\tchecklistError: '',\n\t\t\tallowed: true,\n\t\t\treason: '',\n\t\t\tvalues: {},\n\t\t\tbusy: false,\n\t\t\trefusal: '',\n\t\t\tdone: false,\n\t\t\treasonId: `cn-action-dialog-reason-${++reasonSeq}`,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The token context. */\n\t\ttokenCtx() {\n\t\t\treturn { objectId: this.recordId, object: this.record || {} }\n\t\t},\n\n\t\t/**\n\t\t * The record's id: `objectId`, or the route's `id` when the prop is\n\t\t * empty or a token the opener could not resolve.\n\t\t *\n\t\t * @return {string|number}\n\t\t */\n\t\trecordId() {\n\t\t\tconst given = this.objectId\n\t\t\tif (given !== '' && given !== null && given !== undefined && !String(given).startsWith('@')) {\n\t\t\t\treturn given\n\t\t\t}\n\t\t\treturn (this.$route && this.$route.params && this.$route.params.id) || ''\n\t\t},\n\n\t\t/** @return {object} The confirm config. */\n\t\tconfirmConfig() {\n\t\t\treturn this.config.confirm && typeof this.config.confirm === 'object' ? this.config.confirm : {}\n\t\t},\n\n\t\t/**\n\t\t * The summary row, or null without one or before the record is known.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {{title: string, meta: string, badge: string}|null}\n\t\t */\n\t\tsummary() {\n\t\t\tconst def = this.config.summary\n\t\t\tif (!def || !this.record) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst title = this.fill(def.title)\n\t\t\treturn title ? { title, meta: this.fill(def.meta), badge: this.fill(def.badge) } : null\n\t\t},\n\n\t\t/** @return {Array<{title: string, facts: Array<{label: string, value: string}>}>} The fact groups. */\n\t\tfactSections() {\n\t\t\tif (!Array.isArray(this.config.facts) || !this.record) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst empty = t('nextcloud-vue', 'Not set')\n\t\t\treturn this.config.facts.map((section) => ({\n\t\t\t\ttitle: this.text(section.title),\n\t\t\t\tfacts: (Array.isArray(section.items) ? section.items : []).map((fact) => ({ label: this.text(fact.label), value: this.fill(fact.value) || empty })),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * The checklist rows: label and detail from `labels` by id, else the item's own.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Array<{id: string, met: (boolean|null), label: string, detail: string}>}\n\t\t */\n\t\tchecklist() {\n\t\t\tconst labels = (this.config.checklist && this.config.checklist.labels) || {}\n\t\t\treturn this.items.map((item) => {\n\t\t\t\tconst met = item.met === null || item.met === undefined ? null : item.met === true\n\t\t\t\tconst own = labels[item.id] || {}\n\t\t\t\tconst detail = met === false && own.unmetDetail ? own.unmetDetail : (own.detail || item.detail || '')\n\t\t\t\treturn {\n\t\t\t\t\tid: String(item.id),\n\t\t\t\t\tmet,\n\t\t\t\t\tlabel: this.fill(own.label || item.label || String(item.id), item),\n\t\t\t\t\tdetail: met === null && !item.detail ? t('nextcloud-vue', 'Could not be read') : this.fill(detail, item),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether every required item is met. */\n\t\trequiredMet() {\n\t\t\tconst required = (this.config.checklist && Array.isArray(this.config.checklist.required)) ? this.config.checklist.required.map(String) : []\n\t\t\treturn required.every((id) => this.items.some((item) => String(item.id) === id && item.met === true))\n\t\t},\n\n\t\t/** @return {boolean} Whether a reason is asked and needed. */\n\t\treasonNeeded() {\n\t\t\treturn Boolean(this.config.reason) && this.config.reason.required !== false\n\t\t},\n\n\t\t/** @return {string} The reason field's label. */\n\t\treasonLabel() {\n\t\t\treturn this.text(this.config.reason && this.config.reason.label) || t('nextcloud-vue', 'Reason')\n\t\t},\n\n\t\t/** @return {string} The note's NcNoteCard type. */\n\t\tnoteType() {\n\t\t\tconst variant = this.config.note && this.config.note.variant\n\t\t\treturn ['info', 'warning', 'error', 'success'].includes(variant) ? variant : 'info'\n\t\t},\n\n\t\t/**\n\t\t * Whether the action may run: allowed, required items met, a reason\n\t\t * when one is needed, and nothing in flight.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {boolean}\n\t\t */\n\t\tcanConfirm() {\n\t\t\treturn !this.busy && this.allowed && this.requiredMet && this.fieldsFilled && (!this.reasonNeeded || this.reason.trim() !== '')\n\t\t},\n\n\t\t/** @return {string} The eyebrow, filled from the record once it is known. */\n\t\teyebrowText() {\n\t\t\treturn this.record ? this.fill(this.config.eyebrow) || this.text(this.config.eyebrow) : this.text(this.config.eyebrow)\n\t\t},\n\n\t\t/** @return {Array<object>} The fields with a name. */\n\t\tfields() {\n\t\t\treturn (Array.isArray(this.config.fields) ? this.config.fields : []).filter((f) => f && f.field)\n\t\t},\n\n\t\t/**\n\t\t * Whether every required field has a value.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog-fields-and-acts/specs/action-dialog/spec.md#requirement-an-action-dialog-asks-for-fields-and-reads-a-permission-from-a-list\n\t\t * @return {boolean}\n\t\t */\n\t\tfieldsFilled() {\n\t\t\treturn this.fields.every((f) => f.required === false || String(this.values[f.field] ?? '').trim() !== '')\n\t\t},\n\t},\n\n\tasync mounted() {\n\t\tawait Promise.all([this.loadRecord(), this.loadChecklist(), this.loadPermission()])\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate, '' for anything else.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * A template filled from a row (the record by default), translated first.\n\t\t *\n\t\t * @param {unknown} template The template.\n\t\t * @param {object} [row] The row.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template, row) {\n\t\t\tif (typeof template !== 'string' || template === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn fillTemplate(this.cnTranslate(template), row || this.record || {}).trim()\n\t\t},\n\n\t\t/**\n\t\t * Read an endpoint, fresh.\n\t\t *\n\t\t * @param {object} source An endpointSource block.\n\t\t * @return {Promise<unknown>}\n\t\t */\n\t\tread(source) {\n\t\t\treturn fetchEndpointSource(source, this.tokenCtx, { fresh: true })\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadRecord() {\n\t\t\tconst source = this.config.summary && this.config.summary.source\n\t\t\tif (!source || !source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(source)\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : this.record\n\t\t\t} catch {\n\t\t\t\t// The summary stays empty; the checklist and the action still work.\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadChecklist() {\n\t\t\tconst def = this.config.checklist\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst list = def.itemsPath ? readPath(data, def.itemsPath) : data\n\t\t\t\tthis.items = Array.isArray(list) ? list.filter((item) => item && item.id !== undefined) : []\n\t\t\t} catch {\n\t\t\t\tthis.items = []\n\t\t\t\tthis.checklistError = t('nextcloud-vue', 'Whether this is ready could not be read.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The entry of a list whose fields equal `match`, or null.\n\t\t *\n\t\t * @param {unknown} list The list.\n\t\t * @param {object} [match] The fields to match.\n\t\t * @return {object|null}\n\t\t */\n\t\tmatchIn(list, match) {\n\t\t\tconst wanted = Object.entries(match || {})\n\t\t\treturn (Array.isArray(list) ? list : []).find((entry) => entry && wanted.every(([key, value]) => String(entry[key]) === String(value))) || null\n\t\t},\n\n\t\t/**\n\t\t * A select field's options, labels translated.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {Array<{id: string, label: string, fills?: object}>}\n\t\t */\n\t\toptionsOf(field) {\n\t\t\treturn (Array.isArray(field.options) ? field.options : []).map((o) => ({ id: String(o.value), label: this.text(o.label) || String(o.value), fills: o.fills }))\n\t\t},\n\n\t\t/**\n\t\t * A select field's chosen option.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption(field) {\n\t\t\treturn this.optionsOf(field).find((o) => o.id === String(this.values[field.field] ?? '')) || null\n\t\t},\n\n\t\t/**\n\t\t * Set a field's value.\n\t\t *\n\t\t * @param {string} name The field.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\tsetValue(name, value) {\n\t\t\tthis.values = { ...this.values, [name]: value === null || value === undefined ? '' : String(value) }\n\t\t},\n\n\t\t/**\n\t\t * Choose a select option; its `fills` set the fields still empty.\n\t\t *\n\t\t * @param {object} field The select field.\n\t\t * @param {{id: string, fills?: object}|null} option The option.\n\t\t */\n\t\tpick(field, option) {\n\t\t\tthis.setValue(field.field, option ? option.id : '')\n\t\t\tfor (const [name, value] of Object.entries((option && option.fills) || {})) {\n\t\t\t\tif (String(this.values[name] ?? '').trim() === '') {\n\t\t\t\t\tthis.setValue(name, this.text(value) || value)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/** @return {Promise<void>} */\n\t\tasync loadPermission() {\n\t\t\tconst def = this.config.permission\n\t\t\tif (!def || !def.source || !def.source.url) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst data = await this.read(def.source)\n\t\t\t\tconst entry = def.listPath ? this.matchIn(readPath(data, def.listPath), def.match) : data\n\t\t\t\tthis.allowed = readPath(entry, def.allowedPath || 'allowed') === true\n\t\t\t\tthis.refusal = this.allowed || !def.reasonPath ? '' : String(readPath(entry, def.reasonPath) || '')\n\t\t\t} catch {\n\t\t\t\tthis.allowed = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Run the action once: post `confirm.request`, or dispatch\n\t\t * `confirm.action`, then show the done text and refresh the page.\n\t\t *\n\t\t * @spec openspec/changes/action-dialog/specs/action-dialog/spec.md#requirement-a-manifest-configures-an-action-dialog\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync confirm() {\n\t\t\tif (!this.canConfirm) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tthis.refusal = ''\n\t\t\tconst reason = this.reason.trim()\n\t\t\ttry {\n\t\t\t\tconst request = this.confirmConfig.request\n\t\t\t\tif (request && request.url) {\n\t\t\t\t\tconst url = interpolateUrlTokens(request.url, this.tokenCtx)\n\t\t\t\t\tconst body = {}\n\t\t\t\t\tfor (const [key, value] of Object.entries(request.body || {})) {\n\t\t\t\t\t\tbody[key] = typeof value === 'string' ? fillTemplate(value, { ...(this.record || {}), reason }) : value\n\t\t\t\t\t}\n\t\t\t\t\tif (this.config.reason) {\n\t\t\t\t\t\tbody[this.config.reason.field || 'reason'] = reason\n\t\t\t\t\t}\n\t\t\t\t\tfor (const field of this.fields) {\n\t\t\t\t\t\tbody[field.field] = this.values[field.field] ?? ''\n\t\t\t\t\t}\n\t\t\t\t\tconst method = String(request.method || 'POST').toLowerCase()\n\t\t\t\t\tawait axios[['post', 'put', 'patch', 'delete'].includes(method) ? method : 'post'](/^https?:\\/\\//i.test(url) ? url : generateUrl(url), body)\n\t\t\t\t} else if (this.confirmConfig.action && typeof this.cnDispatchAction === 'function') {\n\t\t\t\t\tawait this.cnDispatchAction({ ...this.confirmConfig.action, args: [...(this.confirmConfig.action.args || []), { ...this.values, reason }] })\n\t\t\t\t}\n\t\t\t\tthis.done = true\n\t\t\t\temit(PAGE_REFRESH, {})\n\t\t\t\t/** @event done The action ran. Payload: the field values and `reason`. */\n\t\t\t\tthis.$emit('done', { ...this.values, reason })\n\t\t\t} catch (failure) {\n\t\t\t\tconst data = failure && failure.response && failure.response.data\n\t\t\t\tthis.refusal = String((data && (data.message || data.error)) || t('nextcloud-vue', 'The action could not be completed.'))\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\n\t\t/** Close the dialog, unless the action is running. */\n\t\tclose() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/** @event close The dialog was closed. */\n\t\t\tthis.$emit('close')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-action-dialog {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n}\n\n.cn-action-dialog__summary {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-action-dialog__summary-text {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tmin-width: 0;\n}\n\n.cn-action-dialog__badge {\n\tpadding: 3px 10px;\n\tborder-radius: 12px;\n\tbackground: var(--color-background-dark);\n\tfont-size: 13px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n}\n\n.cn-action-dialog__title {\n\tmargin: 0 0 8px;\n\tfont-size: 14px;\n\tfont-weight: 600;\n}\n\n/* Facts in columns of at least 200px, each a label over its value above a\n hairline (DqArchiveren \"Bewaring volgens het zaaktype\"). */\n.cn-action-dialog__facts {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n\tgap: 12px 24px;\n\tmargin: 0;\n}\n\n.cn-action-dialog__fact {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 3px;\n\tpadding-bottom: 10px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-action-dialog__facts dt {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-action-dialog__facts dd {\n\tmargin: 0;\n\tfont-size: 15px;\n\tfont-weight: 500;\n}\n\n/* Checklist rows divided by hairlines, the detail at the row's end\n (DqArchiveren \"Klaar om te archiveren\"). */\n.cn-action-dialog__checks {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-action-dialog__check {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 2px 12px;\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-border);\n\tfont-size: 15px;\n}\n\n.cn-action-dialog__check-label {\n\tflex: 1 1 auto;\n\tfont-weight: 600;\n}\n\n.cn-action-dialog__check-icon {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-action-dialog__check-icon--met {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-action-dialog__muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 14px;\n}\n\n.cn-action-dialog__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n}\n\n.cn-action-dialog__error {\n\tmargin: 0;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["CnDialog","CnIcon","t","axios"],"mappings":"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"}
@@ -158,7 +158,8 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
158
158
  createElementVNode("dl", _hoisted_7, [
159
159
  (openBlock(true), createElementBlock(Fragment, null, renderList(section.facts, (fact) => {
160
160
  return (openBlock(), createElementBlock("div", {
161
- key: fact.label
161
+ key: fact.label,
162
+ class: "cn-action-dialog__fact"
162
163
  }, [
163
164
  createElementVNode("dt", null, toDisplayString(fact.label), 1 /* TEXT */),
164
165
  createElementVNode("dd", null, toDisplayString(fact.value), 1 /* TEXT */)
@@ -241,9 +242,10 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
241
242
  "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => (($data.reason) = $event)),
242
243
  label: $options.reasonLabel,
243
244
  labelOutside: false,
245
+ rows: $props.config.reason.rows || 2,
244
246
  resize: "vertical",
245
247
  "data-testid": "cn-action-dialog-reason"
246
- }, null, 8 /* PROPS */, ["id", "modelValue", "label"]),
248
+ }, null, 8 /* PROPS */, ["id", "modelValue", "label", "rows"]),
247
249
  ($props.config.reason.hint)
248
250
  ? (openBlock(), createElementBlock("span", _hoisted_17, toDisplayString($options.text($props.config.reason.hint)), 1 /* TEXT */))
249
251
  : createCommentVNode("v-if", true)