@conduction/nextcloud-vue 2.56.0-beta.30 → 2.56.0-beta.31

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 (173) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +3 -3
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  4. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
  5. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
  7. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  8. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +85 -6
  9. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  10. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +1 -1
  11. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  12. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +4 -1
  13. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  14. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  15. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
  16. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
  17. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
  18. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
  19. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
  20. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
  21. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +40 -3
  22. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +19 -11
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  27. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +43 -0
  28. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +1 -1
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  31. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
  32. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
  33. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
  34. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
  35. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
  36. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue3.js.map +1 -1
  37. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +125 -0
  38. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  39. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  40. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +3 -2
  41. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
  42. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
  43. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +302 -4
  44. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  45. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
  46. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  47. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
  48. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  49. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
  50. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
  51. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +60 -0
  52. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  53. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
  54. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +29 -1
  56. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  57. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +48 -4
  58. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  59. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
  60. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  61. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  62. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
  63. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
  64. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +1 -1
  65. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  66. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  67. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
  68. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  69. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
  70. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  71. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
  72. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  73. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
  74. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  75. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +13 -6
  76. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  77. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  78. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
  79. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
  80. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
  81. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
  82. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
  83. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  84. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  85. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  86. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
  87. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +197 -6
  88. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  89. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +94 -50
  90. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  91. package/dist/esm/composables/useListView.js +4 -4
  92. package/dist/esm/composables/useListView.js.map +1 -1
  93. package/dist/esm/index.js +10 -1
  94. package/dist/esm/index.js.map +1 -1
  95. package/dist/esm/l10n/en.json.js +8 -1
  96. package/dist/esm/l10n/en.json.js.map +1 -1
  97. package/dist/esm/l10n/nl.json.js +54 -1
  98. package/dist/esm/l10n/nl.json.js.map +1 -1
  99. package/dist/esm/nextcloud-vue.css +198 -8
  100. package/dist/esm/utils/facets.js +75 -3
  101. package/dist/esm/utils/facets.js.map +1 -1
  102. package/dist/esm/utils/fillTemplate.js +7 -1
  103. package/dist/esm/utils/fillTemplate.js.map +1 -1
  104. package/dist/esm/utils/pageControls.js +81 -0
  105. package/dist/esm/utils/pageControls.js.map +1 -0
  106. package/dist/esm/utils/personalLenses.js +40 -3
  107. package/dist/esm/utils/personalLenses.js.map +1 -1
  108. package/dist/esm/utils/resolveFilterTokens.js +1 -1
  109. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  110. package/dist/esm/utils/resolveManifestTokens.js +2 -0
  111. package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
  112. package/dist/esm/utils/sentinelTokens.js +19 -1
  113. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  114. package/dist/esm/utils/userGroupsPeek.js +91 -0
  115. package/dist/esm/utils/userGroupsPeek.js.map +1 -0
  116. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  117. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  118. package/dist/esm/utils/visibleWhen.js +57 -5
  119. package/dist/esm/utils/visibleWhen.js.map +1 -1
  120. package/dist/esm/utils/widgetVisibility.js +4 -57
  121. package/dist/esm/utils/widgetVisibility.js.map +1 -1
  122. package/dist/nextcloud-vue.cjs.js +4309 -2233
  123. package/dist/nextcloud-vue.cjs.js.map +2 -2
  124. package/dist/nextcloud-vue.css +198 -8
  125. package/l10n/en.json +8 -1
  126. package/l10n/nl.json +54 -1
  127. package/package.json +2 -1
  128. package/scripts/check-docs-mdx.mjs +108 -0
  129. package/src/components/CnActionsBar/CnActionsBar.vue +6 -1
  130. package/src/components/CnAppRoot/CnAppRoot.md +4 -2
  131. package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
  132. package/src/components/CnBannerWidget/CnBannerWidget.vue +112 -7
  133. package/src/components/CnCellRenderer/CnCellRenderer.vue +4 -1
  134. package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
  135. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
  136. package/src/components/CnDashboardPage/CnDashboardPage.vue +40 -3
  137. package/src/components/CnDataTable/CnDataTable.vue +19 -11
  138. package/src/components/CnDetailPage/CnDetailPage.vue +44 -1
  139. package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
  140. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
  141. package/src/components/CnFactsWidget/CnFactsWidget.vue +125 -0
  142. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +3 -2
  143. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
  144. package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
  145. package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
  146. package/src/components/CnFormPage/CnFormPage.vue +81 -0
  147. package/src/components/CnIndexPage/CnIndexPage.vue +56 -3
  148. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
  149. package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
  150. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +1 -1
  151. package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
  152. package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
  153. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +13 -6
  154. package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
  155. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
  156. package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  157. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +281 -10
  158. package/src/components/index.js +8 -0
  159. package/src/composables/useListView.js +4 -4
  160. package/src/css/look-board-index.css +16 -8
  161. package/src/index.js +8 -0
  162. package/src/schemas/app-manifest-v2.schema.json +93 -9
  163. package/src/utils/facets.js +74 -2
  164. package/src/utils/fillTemplate.js +7 -1
  165. package/src/utils/pageControls.js +78 -0
  166. package/src/utils/personalLenses.js +39 -2
  167. package/src/utils/resolveFilterTokens.js +3 -1
  168. package/src/utils/resolveManifestTokens.js +2 -0
  169. package/src/utils/sentinelTokens.js +19 -1
  170. package/src/utils/userGroupsPeek.js +87 -0
  171. package/src/utils/validateManifestV2.compiled.js +3 -3
  172. package/src/utils/visibleWhen.js +56 -4
  173. package/src/utils/widgetVisibility.js +7 -56
@@ -1 +1 @@
1
- {"version":3,"file":"CnStatsBlockWidget.vue3.js","sources":["../../../../src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue?vue&type=template&id=4f8583ef&lang.js"],"sourcesContent":["<!--\n CnStatsBlockWidget — Manifest-driven stats block.\n\n Thin wrapper around CnStatsBlock that pulls its count from a\n `dataSource` block on a dashboard widget definition. The\n dispatcher in CnDashboardPage mounts this when\n `widgetDef.type === 'stats-block'`.\n\n ADR-049 enrichment: an optional `entries[]` renders N related KPIs in one\n card, each entry self-fetching its own token-resolved count over the same\n REST /value aggregation. Exactly one of `dataSource` / `entries` must be\n provided; the single-`dataSource` path is unchanged.\n\n Static usage (no data fetching) is intentionally NOT supported —\n use CnStatsBlock directly when you already have a count number.\n-->\n<template>\n\t<div\n\t\tclass=\"cn-stats-block-widget\"\n\t\t:class=\"[iconClass, { 'cn-stats-block-widget--multi': hasEntries }]\">\n\t\t<!-- Multi-entry mode: one KPI per entry, all inside this widget card.\n\t\t An entry with hideWhenZero whose resolved count is 0 is omitted. -->\n\t\t<template v-if=\"hasEntries\">\n\t\t\t<CnStatsBlock\n\t\t\t\tv-for=\"view in entryViews\"\n\t\t\t\t:key=\"view.key\"\n\t\t\t\t:title=\"view.entry.title || ''\"\n\t\t\t\t:count=\"view.count\"\n\t\t\t\t:countLabel=\"view.entry.countLabel || ''\"\n\t\t\t\t:loading=\"view.loading\"\n\t\t\t\t:variant=\"view.entry.variant || 'default'\"\n\t\t\t\t:showZeroCount=\"showZeroCount\"\n\t\t\t\t:horizontal=\"horizontal\"\n\t\t\t\t:vertical=\"vertical\"\n\t\t\t\t:layout=\"layout\"\n\t\t\t\t:filled=\"filled\"\n\t\t\t\t:clickable=\"!!view.route\"\n\t\t\t\t:route=\"view.route || null\">\n\t\t\t\t<!-- An entry with a `format` (or `currency` / `currencyField`)\n\t\t\t\t shows its value formatted, money in its own currency. -->\n\t\t\t\t<template v-if=\"view.formatted !== null\" #value>\n\t\t\t\t\t{{ view.formatted }}\n\t\t\t\t</template>\n\t\t\t</CnStatsBlock>\n\t\t</template>\n\n\t\t<!-- Single-source mode (pre-existing interface, unchanged). -->\n\t\t<CnStatsBlock\n\t\t\tv-else\n\t\t\t:title=\"title\"\n\t\t\t:count=\"resolvedCount\"\n\t\t\t:countLabel=\"countLabel\"\n\t\t\t:loading=\"loading\"\n\t\t\t:variant=\"variant\"\n\t\t\t:showZeroCount=\"showZeroCount\"\n\t\t\t:horizontal=\"horizontal\"\n\t\t\t:vertical=\"vertical\"\n\t\t\t:layout=\"layout\"\n\t\t\t:filled=\"filled\"\n\t\t\t:clickable=\"!!route\"\n\t\t\t:route=\"route\">\n\t\t\t<template v-if=\"formattedSingle !== null\" #value>\n\t\t\t\t{{ formattedSingle }}\n\t\t\t</template>\n\t\t</CnStatsBlock>\n\t</div>\n</template>\n\n<script>\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport CnStatsBlock from '../CnStatsBlock/CnStatsBlock.vue'\nimport { useDataSource } from '../../composables/useDataSource.js'\nimport { formatMetricValue, normalizeMetricFormat, unwrapAppConfig } from '../../utils/formatMetric.js'\nimport { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\n\n// The canonical KPI scale (`--cn-kpi-*`) lives in one stylesheet. Imported\n// here as well as from css/index.css so the tokens resolve even when the\n// consuming app pulls in components individually.\nimport '../../css/kpi-card.css'\n\n/**\n * Event-bus channel the PAGE-level Refresh action broadcasts on\n * (CnDashboardPage / CnDetailPage pass it to their CnActionsMenu).\n */\nconst PAGE_REFRESH_BUS_CHANNEL = 'cn:page:refresh'\n\n/**\n * CnStatsBlockWidget — Stats-block dashboard widget.\n *\n * Reads a `dataSource` block (manifest shorthand or raw GraphQL)\n * and forwards the resolved count to CnStatsBlock. Loading state\n * propagates through; resolution errors fall back to count = 0\n * with an empty-state label so the dashboard doesn't blank.\n *\n * Multi-entry mode (ADR-049 / list-widget-enrichment): an `entries[]`\n * array renders N KPIs in one card. Each entry carries the same source\n * contract as a `type:\"stat\"` widget (`register`, `schema`, `metric`,\n * `field`, token-resolved `filter`) plus per-entry presentation\n * (`title`, `variant`, `countLabel`), an optional `route` deep link, and\n * `hideWhenZero` (the entry is omitted when its resolved count is 0).\n *\n * An entry formats its value with `format` (a style name such as\n * `\"currency\"`, or `{ style, currency, currencyField, decimals, prefix,\n * suffix }`), as a `type:\"stat\"` widget's `content.format` does. A money\n * value takes the currency from `currencyField` (a field of the detail\n * page's object, e.g. a contract's own `currency`), then `currency` (a code\n * or `@config.currency`), then the app's reporting currency, then EUR, and\n * is formatted in the user's locale. `currency` / `currencyField` may sit on\n * the entry itself and imply `format: \"currency\"`.\n * Exactly one of `dataSource` / `entries` must be provided — when\n * `entries` is absent, the single-`dataSource` path renders exactly as\n * before.\n *\n * Manifest example (decidesk dashboard):\n * ```json\n * {\n * \"id\": \"minutes-in-review\",\n * \"type\": \"stats-block\",\n * \"title\": \"Notulen ter goedkeuring\",\n * \"iconClass\": \"icon-file\",\n * \"props\": { \"countLabel\": \"notulen\", \"variant\": \"warning\" },\n * \"dataSource\": {\n * \"register\": \"decidesk\",\n * \"schema\": \"minutes\",\n * \"filter\": { \"lifecycle\": \"review\" },\n * \"aggregate\": \"count\"\n * }\n * }\n * ```\n *\n * Multi-entry example (a grouped retention card):\n * ```json\n * {\n * \"widgetKey\": \"stats-block\",\n * \"props\": {\n * \"entries\": [\n * { \"title\": \"Expiring soon\", \"register\": \"docudesk\", \"schema\": \"document\",\n * \"filter\": { \"retention\": { \"lt\": \"@today+30d\" } }, \"variant\": \"warning\",\n * \"route\": { \"name\": \"documents\" }, \"hideWhenZero\": true },\n * { \"title\": \"Archived\", \"register\": \"docudesk\", \"schema\": \"document\",\n * \"filter\": { \"status\": \"archived\" } }\n * ]\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnStatsBlockWidget',\n\n\tcomponents: { CnStatsBlock },\n\n\tinject: {\n\t\t/**\n\t\t * Detail-page object context provided by CnDetailPage — enables\n\t\t * `@objectId` / `@object.<field>` tokens in an entry's `filter`.\n\t\t * Null on dashboards.\n\t\t */\n\t\tcnObjectContext: { default: null },\n\t\t/**\n\t\t * Page-level workspace context (reactive `ref({})`) provided by\n\t\t * CnDashboardPage — enables `@workspace.<key>` tokens in an entry's\n\t\t * `filter`. Null on pages that don't provide it.\n\t\t */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/**\n\t\t * Page-level app config provided by the page — the reporting\n\t\t * currency (`currency`) and `@config.<key>` tokens in `format`.\n\t\t */\n\t\tcnAppConfig: { default: () => ({}) },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Manifest dataSource block. See `useDataSource`. Exactly one of\n\t\t * `dataSource` / `entries` must be provided — a stats-block widget\n\t\t * without any source has no count to render (flagged with a console\n\t\t * error at mount).\n\t\t *\n\t\t * @type {{\n\t\t * register?: string,\n\t\t * schema?: string,\n\t\t * filter?: object,\n\t\t * aggregate?: 'count',\n\t\t * graphql?: { query: string, variables?: object, selectors: object }\n\t\t * }}\n\t\t */\n\t\tdataSource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Multi-entry declarative sources (ADR-049). Each entry renders one\n\t\t * KPI within this widget's card and self-fetches its own count over\n\t\t * the REST /value aggregation: `{ title, register, schema, metric,\n\t\t * field, filter, route, variant, countLabel, hideWhenZero }`. Filter\n\t\t * values use the shared @-token grammar (`@today`, `@me`,\n\t\t * `@workspace.*`, `?`-optional clauses). An entry with `hideWhenZero`\n\t\t * is omitted when its resolved count is 0. Mutually exclusive with\n\t\t * `dataSource`; defaults to empty (single-source mode).\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\tentries: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * How the single-source value is shown: a style name (`'currency'`,\n\t\t * `'number'`, `'percent'`, `'decimal'`, `'duration-hours'`) or\n\t\t * `{ style, currency, currencyField, decimals, prefix, suffix }`.\n\t\t * Empty (the default) shows the plain localized count. Entries carry\n\t\t * their own `format`.\n\t\t *\n\t\t * @type {string|object|null}\n\t\t */\n\t\tformat: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Block title (manifest `widgetDef.title`). */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Label displayed next to the count. */\n\t\tcountLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Color variant: default | primary | success | warning | error. */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: 'default',\n\t\t},\n\n\t\t/** Whether to show 0 as a real count instead of the empty label. */\n\t\tshowZeroCount: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Lay the icon left of the content. The canonical KPI card already\n\t\t * does this, so the prop is redundant and kept only for existing\n\t\t * callers.\n\t\t *\n\t\t * @deprecated since 2.25.0, the horizontal layout is the default.\n\t\t */\n\t\thorizontal: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Stack the icon above a centred number instead of beside it. */\n\t\tvertical: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * `stacked` gives every block the board look (see CnStatsBlock\n\t\t * `layout`). Empty (the default) keeps the KPI card.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-stats-block-can-take-the-stacked-board-look\n\t\t */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Draw each tile's own grey box. Off by default — the widget is\n\t\t * rendered inside a CnWidgetWrapper that already draws a card.\n\t\t */\n\t\tfilled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Vue-router location to navigate to on click. When set, the\n\t\t * inner CnStatsBlock renders as a `<router-link>`.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\troute: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional CSS class applied to the widget's outermost wrapping\n\t\t * `<div>`. Designed for Nextcloud core icon classes (`icon-link`,\n\t\t * `icon-mail`, `icon-history`, …) that ship with NC and render a\n\t\t * 16×16 glyph via background-image. When empty (the default), the\n\t\t * wrapper carries only the base `cn-stats-block-widget` class and\n\t\t * is visually invisible.\n\t\t *\n\t\t * The wrapping element is a `<div>` with no layout styles of its\n\t\t * own, so the rendered KPI tile keeps the same dimensions and\n\t\t * spacing it had before this prop was added.\n\t\t *\n\t\t * MDI icon names (e.g. `'Database'`) are NOT supported here — that\n\t\t * would require an async dynamic-import. Use `CnStatsBlock`\n\t\t * directly when you need an MDI Vue component icon.\n\t\t */\n\t\ticonClass: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tsetup(props) {\n\t\t// GraphQL count path is hidden from useDataSource when a plain\n\t\t// register+schema source is given — that case is served over REST\n\t\t// (fetchRest) against OpenRegister's /value aggregation, which is more\n\t\t// robust than the GraphQL count shorthand. Raw `graphql` still flows here.\n\t\tconst dsForGraphql = () => {\n\t\t\tconst ds = props.dataSource\n\t\t\tif (ds && ds.register && ds.schema && !ds.graphql) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn ds\n\t\t}\n\t\tconst { data, loading, error, refetch } = useDataSource(dsForGraphql)\n\t\treturn { dsData: data, loading, error, dsRefetch: refetch }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trestCount: null,\n\t\t\t/** @type {Array<number|null>} Per-entry resolved counts (multi-entry mode; null = pending/failed). */\n\t\t\tentryCounts: [],\n\t\t\t/** @type {Array<boolean>} Per-entry fetch-in-flight flags (multi-entry mode). */\n\t\t\tentryFetching: [],\n\t\t\t/** Monotonic id used to discard a stale entries batch when `entries` changes mid-flight. */\n\t\t\tentriesRequestId: 0,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the widget runs in multi-entry mode (`entries[]` supplied).\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\thasEntries() {\n\t\t\treturn Array.isArray(this.entries) && this.entries.length > 0\n\t\t},\n\n\t\t/**\n\t\t * The unwrapped detail-page object context for token resolution, or\n\t\t * null on surfaces (dashboards) that don't provide one.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tobjectCtx() {\n\t\t\tconst c = this.cnObjectContext\n\t\t\tif (!c) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn (typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t},\n\n\t\t/**\n\t\t * The unwrapped workspace context bag (or null). Vue 2.7 inject may\n\t\t * hand back a raw ref; unwrap `.value` for token resolution.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tworkspaceCtx() {\n\t\t\tconst c = this.cnWorkspaceContext\n\t\t\tif (!c) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn (typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t},\n\n\t\t/**\n\t\t * The page-level app config map (reporting currency, `@config` keys).\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tconfigCtx() {\n\t\t\treturn unwrapAppConfig(this.cnAppConfig)\n\t\t},\n\n\t\t/**\n\t\t * The detail page's object, or null on a dashboard. Its fields feed\n\t\t * an entry's `currencyField`.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tpageObject() {\n\t\t\tconst object = this.objectCtx && this.objectCtx.object\n\t\t\treturn object && typeof object === 'object' ? object : null\n\t\t},\n\n\t\t/**\n\t\t * The single-source value formatted per `format`, or null to keep\n\t\t * the plain count.\n\t\t *\n\t\t * @return {string|null}\n\t\t * @spec openspec/changes/stat-currency-from-object/specs/dashboard-page/spec.md#requirement-a-stats-block-entry-formats-its-value\n\t\t */\n\t\tformattedSingle() {\n\t\t\tconst fmt = normalizeMetricFormat({ format: this.format })\n\t\t\tif (!fmt || this.loading) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn formatMetricValue(this.resolvedCount, fmt, this.configCtx, this.pageObject)\n\t\t},\n\n\t\t/**\n\t\t * Token-resolution context for entry filters, merged from the\n\t\t * detail-page object context and the page-level workspace bag.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\ttokenCtx() {\n\t\t\tconst base = this.objectCtx ? { ...this.objectCtx } : {}\n\t\t\tbase.workspace = this.workspaceCtx || {}\n\t\t\treturn base\n\t\t},\n\n\t\t/**\n\t\t * Renderable view models for multi-entry mode: each entry paired with\n\t\t * its fetched count and loading flag, with `hideWhenZero` entries\n\t\t * whose resolved count is 0 omitted.\n\t\t *\n\t\t * @return {Array<{entry: object, key: string, count: number, loading: boolean, formatted: (string|null)}>}\n\t\t * @spec openspec/changes/stat-currency-from-object/specs/dashboard-page/spec.md#requirement-a-stats-block-entry-formats-its-value\n\t\t */\n\t\tentryViews() {\n\t\t\treturn (this.entries || [])\n\t\t\t\t.map((entry, i) => {\n\t\t\t\t\tconst raw = this.entryCounts[i]\n\t\t\t\t\tconst resolved = typeof raw === 'number'\n\t\t\t\t\tconst fmt = normalizeMetricFormat(entry)\n\t\t\t\t\treturn {\n\t\t\t\t\t\t// The value as the entry's `format` says, money in the\n\t\t\t\t\t\t// currency the entry names; null keeps the plain count.\n\t\t\t\t\t\tformatted: fmt && resolved ? formatMetricValue(raw, fmt, this.configCtx, this.pageObject) : null,\n\t\t\t\t\t\tentry: entry || {},\n\t\t\t\t\t\tkey: `${(entry && entry.register) || ''}/${(entry && entry.schema) || ''}/${i}`,\n\t\t\t\t\t\tcount: resolved ? raw : 0,\n\t\t\t\t\t\tloading: !!this.entryFetching[i],\n\t\t\t\t\t\tresolved,\n\t\t\t\t\t\t// Token-resolve the deep link the same way entry.filter\n\t\t\t\t\t\t// is resolved — `route.query: { case: \"@objectId\" }` must\n\t\t\t\t\t\t// land on the target pre-scoped, never as a literal token\n\t\t\t\t\t\t// (ADR-062; parity with object-list's viewAllQuery).\n\t\t\t\t\t\troute: this.resolveEntryRoute(entry && entry.route),\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter((view) => !(view.entry.hideWhenZero && view.resolved && view.count === 0))\n\t\t},\n\n\t\t/**\n\t\t * Stable signature of a REST-fetchable source (else null).\n\t\t *\n\t\t * @spec openspec/specs/dashboard-page/spec.md\n\t\t */\n\t\trestKey() {\n\t\t\tconst ds = this.dataSource || {}\n\t\t\tif (!ds.register || !ds.schema || ds.graphql) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn JSON.stringify({\n\t\t\t\tregister: ds.register,\n\t\t\t\tschema: ds.schema,\n\t\t\t\tmetric: ds.metric || (ds.aggregate === 'count' ? 'count' : 'count'),\n\t\t\t\tfield: ds.field || '',\n\t\t\t\tfilter: ds.filter || {},\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stable signature of the multi-entry sources INCLUDING their\n\t\t * token-resolved filters, so the watcher refetches when page-level\n\t\t * state a `@workspace.*` token reads changes.\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tentriesKey() {\n\t\t\tif (!this.hasEntries) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn JSON.stringify(this.entries.map((entry) => ({\n\t\t\t\tregister: (entry && entry.register) || '',\n\t\t\t\tschema: (entry && entry.schema) || '',\n\t\t\t\tmetric: (entry && entry.metric) || 'count',\n\t\t\t\tfield: (entry && entry.field) || '',\n\t\t\t\tfilter: this.resolvedEntryFilter(entry),\n\t\t\t})))\n\t\t},\n\n\t\tresolvedCount() {\n\t\t\tif (typeof this.restCount === 'number') {\n\t\t\t\treturn this.restCount\n\t\t\t}\n\t\t\tconst value = this.dsData?.count\n\t\t\tif (typeof value === 'number') {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\tif (typeof value === 'string') {\n\t\t\t\tconst parsed = Number(value)\n\t\t\t\treturn Number.isFinite(parsed) ? parsed : 0\n\t\t\t}\n\t\t\treturn 0\n\t\t},\n\t},\n\n\twatch: {\n\t\trestKey() {\n\t\t\tthis.fetchRest()\n\t\t},\n\n\t\tentriesKey() {\n\t\t\tthis.fetchEntries()\n\t\t},\n\t},\n\n\tcreated() {\n\t\t// \"Exactly one of dataSource / entries\" — a misconfigured widget\n\t\t// fails loudly (console error + no counts) instead of rendering an\n\t\t// ambiguous card. Vue 2 prop validators cannot see sibling props, so\n\t\t// the cross-prop rule lives here (and, for manifests, in\n\t\t// validateManifestV2's post-schema check).\n\t\tconst hasDataSource = !!this.dataSource\n\t\tif (hasDataSource === this.hasEntries) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error('[CnStatsBlockWidget] Exactly one of `dataSource` / `entries` must be provided.')\n\t\t}\n\t},\n\n\tmounted() {\n\t\tthis.fetchRest()\n\t\tthis.fetchEntries()\n\t\t// Page-level Refresh. This tile is now rendered inside CnWidgetWrapper\n\t\t// like every other widget (it went flat in 2026-08-30's KPI-card\n\t\t// change, so the wrapper is what draws its card), but it is registered\n\t\t// as a CARD widget and cards carry no per-widget Actions menu — so the\n\t\t// page action is still its only refresh affordance. It reached nothing\n\t\t// before this subscription: the counts come from `fetchRest` /\n\t\t// `fetchEntries` and the useDataSource GraphQL path, none of which\n\t\t// subscribe on their own. No widgetId to match: a page refresh\n\t\t// refreshes everything on the page.\n\t\tthis._onPageRefresh = (payload) => {\n\t\t\tconst done = this.refresh()\n\t\t\tpayload?.waitUntil?.(done)\n\t\t}\n\t\tsubscribe(PAGE_REFRESH_BUS_CHANNEL, this._onPageRefresh)\n\t},\n\n\tbeforeUnmount() {\n\t\tif (this._onPageRefresh) {\n\t\t\tunsubscribe(PAGE_REFRESH_BUS_CHANNEL, this._onPageRefresh)\n\t\t\tthis._onPageRefresh = null\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Re-query the tile's counts — the REST `/value` aggregation, the\n\t\t * per-entry counts of multi-entry mode, and the `dataSource` GraphQL\n\t\t * path. Exposed as a ref-callable method for parity with the other data\n\t\t * widgets, and invoked by the page-level Refresh subscription.\n\t\t *\n\t\t * @return {Promise<unknown[]>} Settles when every re-query has.\n\t\t */\n\t\trefresh() {\n\t\t\treturn Promise.all([\n\t\t\t\ttypeof this.dsRefetch === 'function' ? this.dsRefetch() : null,\n\t\t\t\tthis.fetchRest(),\n\t\t\t\tthis.fetchEntries(),\n\t\t\t])\n\t\t},\n\n\t\t/**\n\t\t * An entry's `route` deep link with `@`-tokens in `query` / `params`\n\t\t * resolved against `tokenCtx` (same treatment as `entry.filter`), so\n\t\t * a KPI card can deep-link to an index pre-scoped to the current\n\t\t * object. Unresolved optional query clauses are dropped; a route\n\t\t * whose query still carries a REQUIRED unresolved token is returned\n\t\t * without that query entry resolution left as-is (router will show\n\t\t * the unfiltered index rather than a broken literal-token filter).\n\t\t *\n\t\t * @param {object|string|null} route The entry's route location.\n\t\t * @return {object|string|null} The token-resolved route.\n\t\t */\n\t\tresolveEntryRoute(route) {\n\t\t\tif (!route || typeof route !== 'object') {\n\t\t\t\treturn route || null\n\t\t\t}\n\t\t\tconst out = { ...route }\n\t\t\tif (out.query && typeof out.query === 'object') {\n\t\t\t\tout.query = dropOptionalUnresolved(resolveFilterTokens(out.query, this.tokenCtx))\n\t\t\t}\n\t\t\tif (out.params && typeof out.params === 'object') {\n\t\t\t\tout.params = resolveFilterTokens(out.params, this.tokenCtx)\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * An entry's filter with every @-token resolved against `tokenCtx`\n\t\t * and UNRESOLVED OPTIONAL clauses (`@workspace.<key>?`) dropped.\n\t\t *\n\t\t * @param {object} entry The entry definition.\n\t\t * @return {object} The resolved filter map.\n\t\t */\n\t\tresolvedEntryFilter(entry) {\n\t\t\treturn dropOptionalUnresolved(resolveFilterTokens((entry && entry.filter) || {}, this.tokenCtx))\n\t\t},\n\n\t\t/**\n\t\t * Fetch one count/value over REST from OpenRegister's /value\n\t\t * aggregation. Shared by the single-`dataSource` path and the\n\t\t * per-entry multi-entry path. Lazily imports axios/router (the\n\t\t * library never hard-depends on them at module load).\n\t\t *\n\t\t * @param {{register: string, schema: string, metric?: string, aggregate?: string, field?: string}} src The source block.\n\t\t * @param {object} filter The ALREADY-RESOLVED filter map.\n\t\t * @return {Promise<number|null>} The value, or null on failure.\n\t\t */\n\t\tasync fetchValue(src, filter) {\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(\n\t\t\t\t\t'/apps/openregister/api/objects/aggregations/{register}/{schema}/value',\n\t\t\t\t\t{ register: src.register, schema: src.schema },\n\t\t\t\t)\n\t\t\t\tconst params = { metric: src.metric || (src.aggregate === 'count' ? 'count' : 'count') }\n\t\t\t\tif (src.field) {\n\t\t\t\t\tparams.field = src.field\n\t\t\t\t}\n\t\t\t\tfor (const [k, v] of Object.entries(filter || {})) {\n\t\t\t\t\tif (v && typeof v === 'object') {\n\t\t\t\t\t\tfor (const [op, ov] of Object.entries(v)) {\n\t\t\t\t\t\t\tparams[`filter[${k}][${op}]`] = ov\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (v !== '' && v !== null && v !== undefined) {\n\t\t\t\t\t\tparams[`filter[${k}]`] = v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst res = await axios.get(url, { params })\n\t\t\t\treturn Number(res?.data?.value ?? 0) || 0\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fetch the count/value over REST when the single `dataSource` is a\n\t\t * plain register+schema block (operator + dynamic tokens supported).\n\t\t * No-op for raw GraphQL or multi-entry mode. Delegates the request to\n\t\t * `fetchValue` (the per-entry refactor) — behaviour is unchanged.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchRest() {\n\t\t\tconst ds = this.dataSource || {}\n\t\t\tif (!ds.register || !ds.schema || ds.graphql) {\n\t\t\t\tthis.restCount = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.restCount = await this.fetchValue(ds, resolveFilterTokens(ds.filter || {}))\n\t\t},\n\n\t\t/**\n\t\t * Fetch every entry's count in parallel (multi-entry mode). An entry\n\t\t * whose filter still carries an unresolved REQUIRED token (e.g.\n\t\t * `@workspace.selectedClient` with no selection) is skipped rather\n\t\t * than counted across the whole register. A monotonic request id\n\t\t * discards a stale batch when `entries` changes mid-flight.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchEntries() {\n\t\t\tif (!this.hasEntries) {\n\t\t\t\tthis.entryCounts = []\n\t\t\t\tthis.entryFetching = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst id = ++this.entriesRequestId\n\t\t\tconst entries = this.entries\n\t\t\tthis.entryFetching = entries.map(() => true)\n\t\t\tconst counts = await Promise.all(entries.map(async (entry) => {\n\t\t\t\tif (!entry || !entry.register || !entry.schema) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst filter = this.resolvedEntryFilter(entry)\n\t\t\t\tif (hasUnresolvedTokens(filter)) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\treturn this.fetchValue(entry, filter)\n\t\t\t}))\n\t\t\tif (id !== this.entriesRequestId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.entryCounts = counts\n\t\t\tthis.entryFetching = entries.map(() => false)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/*\n * This widget draws no KPI of its own — every tile it shows is a CnStatsBlock,\n * so the canonical card in src/css/kpi-card.css is already what a reader sees.\n * All that is left here is how N of those tiles stack inside one widget, and\n * that spacing comes from the shared `--cn-kpi-stack-gap` so a multi-entry\n * widget and a CnStatsPanel stack of the same tiles are spaced identically.\n */\n/*\n * Fill the host cell rather than sizing to content.\n *\n * 🔴 WITHOUT THIS THE WIDGET COLLAPSES TO THE WIDTH OF ITS ICON, and the\n * number it exists to show is clipped to a sliver. Measured on a live\n * instance: a 282px `.cn-widget-wrapper__content` held a 20px widget, a 20px\n * `.cn-kpi-card` and a 0px `.cn-kpi-card__body`. 20px is the icon.\n *\n * The mechanism is a sizing cycle, which is why the KPI card's own CSS looks\n * correct in isolation and is. `.cn-widget-wrapper__content` is `display:\n * flex`, so this root is a flex ITEM and defaults to `flex: 0 1 auto` —\n * content-sized. Its content is `.cn-kpi-card { width: 100% }`, which resolves\n * against THIS element, which is resolving against its content. The card's\n * body carries `min-width: 0` (deliberately, so a long number can shrink\n * instead of overflowing), so the cycle settles with the body at zero and only\n * the icon's intrinsic width surviving.\n *\n * `flex: 1 1 auto` + `width: 100%` gives the card a definite width to resolve\n * against and breaks the cycle; `min-width: 0` keeps the shrink behaviour the\n * body's rule was written for.\n *\n * On the BASE class, not `--multi`: both the single-source and multi-entry\n * modes render this root, so both collapse. The bug was found on a\n * single-entry stats-block.\n */\n.cn-stats-block-widget {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n\twidth: 100%;\n}\n\n.cn-stats-block-widget--multi {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--cn-kpi-stack-gap, 12px);\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_Fragment","_renderList","_createTextVNode","_toDisplayString"],"mappings":";;;;;uBAiCIA;AACA,IAAA,KAAG,EAACC,cAAA,CAAA,CAAA,uBAAW,EACR,CAAA,MAAA,CAAA,SAAQ,oCAAA,QAAA,CAAA,UAAA,EAAA,CAAA,CAAA,CAAA;;KAGD,QAAU,CAAA,UAAA;4CAqBV,CAAAC,QAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,EAAAC,UAAA,CAnBC,QAAU,CAAA,UAAA,EAAA,CAAlB,IAAI,KAAA;0CAmBG,CAAA,uBAAA,EAAA;YAlBb,GAAG,EAAE,IAAI,CAAC,GAAG;AACb,YAAA,KAAK,EAAE,IAAE,CAAA,KAAA,CAAA,KAAA,IAAA,EAAA;YACT,KAAK,EAAE,IAAI,CAAC,KAAA;AACZ,YAAA,UAAO,EAAA,IAAA,CAAA,KAAA,CAAA,UAAA,IAAA,EAAA;;AAEP,YAAA,OAAO,EAAE,IAAI,CAAC,KAAK,CAAE,OAAM,IAAA,SAAA;AAC3B,YAAA,aAAS,EAAA,MAAA,CAAA,aAAA;AACT,YAAA,UAAG,EAAA,MAAA,CAAA,UAAA;AACH,YAAA,QAAQ,EAAE,MAAC,CAAA,QAAA;AACX,YAAA,MAAM,EAAE,MAAM,CAAA,MAAA;AACd,YAAA,MAAM,EAAE,MAAM,CAAA,MAAA;YACd,SAAS,EAAA,CAAA,CAAI,IAAE,CAAA,KAAA;YACf,KAAK,EAAE,IAAI,CAAC,KAAG,IAAA,IAAA;;AAGA,YAAA,CAAA,IAAE,CAAA,SAAA,KAAA,IAAA;;wBAAA,OAAA;8BACjB,MAAa;AAAV,oBAAAC,eAAA,CAAAC,eAAA,CAAA,IAAI,CAAC,SAAK,CAAA,EAAA,CAAA,YAAA;;;;;;;;;AAQd,UAAA,KAAE,EAAA,MAAA,CAAA,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"CnStatsBlockWidget.vue3.js","sources":["../../../../src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue?vue&type=template&id=4f8583ef&lang.js"],"sourcesContent":["<!--\n CnStatsBlockWidget — Manifest-driven stats block.\n\n Thin wrapper around CnStatsBlock that pulls its count from a\n `dataSource` block on a dashboard widget definition. The\n dispatcher in CnDashboardPage mounts this when\n `widgetDef.type === 'stats-block'`.\n\n ADR-049 enrichment: an optional `entries[]` renders N related KPIs in one\n card, each entry self-fetching its own token-resolved count over the same\n REST /value aggregation. Exactly one of `dataSource` / `entries` must be\n provided; the single-`dataSource` path is unchanged.\n\n Static usage (no data fetching) is intentionally NOT supported —\n use CnStatsBlock directly when you already have a count number.\n-->\n<template>\n\t<div\n\t\tclass=\"cn-stats-block-widget\"\n\t\t:class=\"[iconClass, { 'cn-stats-block-widget--multi': hasEntries && !isRows, 'cn-stats-block-widget--rows': isRows }]\">\n\t\t<!-- Rows layout: a title and one label-left, number-right row per entry\n\t\t (the board's \"Mijn Zuiddrecht nu\" card). -->\n\t\t<template v-if=\"isRows\">\n\t\t\t<h2 v-if=\"title\" class=\"cn-stats-block-widget__rows-title\" data-testid=\"cn-stats-block-rows-title\">\n\t\t\t\t{{ tl(title) }}\n\t\t\t</h2>\n\t\t\t<dl class=\"cn-stats-block-widget__rows\" data-testid=\"cn-stats-block-rows\">\n\t\t\t\t<div\n\t\t\t\t\tv-for=\"view in rowViews\"\n\t\t\t\t\t:key=\"view.key\"\n\t\t\t\t\tclass=\"cn-stats-block-widget__row\"\n\t\t\t\t\tdata-testid=\"cn-stats-block-row\">\n\t\t\t\t\t<dt class=\"cn-stats-block-widget__row-label\">\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\tv-if=\"view.href\"\n\t\t\t\t\t\t\t:href=\"view.href\"\n\t\t\t\t\t\t\tdata-testid=\"cn-stats-block-row-link\"\n\t\t\t\t\t\t\t@click=\"onRowClick($event, view.route)\">{{ view.label }}</a>\n\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t{{ view.label }}\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</dt>\n\t\t\t\t\t<dd\n\t\t\t\t\t\tclass=\"cn-stats-block-widget__row-value\"\n\t\t\t\t\t\t:class=\"'cn-stats-block-widget__row-value--' + view.variant\"\n\t\t\t\t\t\tdata-testid=\"cn-stats-block-row-value\">\n\t\t\t\t\t\t{{ view.display }}\n\t\t\t\t\t</dd>\n\t\t\t\t</div>\n\t\t\t</dl>\n\t\t</template>\n\n\t\t<!-- Multi-entry mode: one KPI per entry, all inside this widget card.\n\t\t An entry with hideWhenZero whose resolved count is 0 is omitted. -->\n\t\t<template v-else-if=\"hasEntries\">\n\t\t\t<CnStatsBlock\n\t\t\t\tv-for=\"view in entryViews\"\n\t\t\t\t:key=\"view.key\"\n\t\t\t\t:title=\"view.entry.title || ''\"\n\t\t\t\t:count=\"view.count\"\n\t\t\t\t:countLabel=\"view.entry.countLabel || ''\"\n\t\t\t\t:loading=\"view.loading\"\n\t\t\t\t:variant=\"view.entry.variant || 'default'\"\n\t\t\t\t:showZeroCount=\"showZeroCount\"\n\t\t\t\t:horizontal=\"horizontal\"\n\t\t\t\t:vertical=\"vertical\"\n\t\t\t\t:layout=\"layout\"\n\t\t\t\t:filled=\"filled\"\n\t\t\t\t:clickable=\"!!view.route\"\n\t\t\t\t:route=\"view.route || null\">\n\t\t\t\t<!-- An entry with a `format` (or `currency` / `currencyField`)\n\t\t\t\t shows its value formatted, money in its own currency. -->\n\t\t\t\t<template v-if=\"view.formatted !== null\" #value>\n\t\t\t\t\t{{ view.formatted }}\n\t\t\t\t</template>\n\t\t\t</CnStatsBlock>\n\t\t</template>\n\n\t\t<!-- Single-source mode (pre-existing interface, unchanged). -->\n\t\t<CnStatsBlock\n\t\t\tv-else\n\t\t\t:title=\"title\"\n\t\t\t:count=\"resolvedCount\"\n\t\t\t:countLabel=\"countLabel\"\n\t\t\t:loading=\"loading\"\n\t\t\t:variant=\"variant\"\n\t\t\t:showZeroCount=\"showZeroCount\"\n\t\t\t:horizontal=\"horizontal\"\n\t\t\t:vertical=\"vertical\"\n\t\t\t:layout=\"layout\"\n\t\t\t:filled=\"filled\"\n\t\t\t:clickable=\"!!route\"\n\t\t\t:route=\"route\">\n\t\t\t<template v-if=\"formattedSingle !== null\" #value>\n\t\t\t\t{{ formattedSingle }}\n\t\t\t</template>\n\t\t</CnStatsBlock>\n\t</div>\n</template>\n\n<script>\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport CnStatsBlock from '../CnStatsBlock/CnStatsBlock.vue'\nimport { useDataSource } from '../../composables/useDataSource.js'\nimport { formatMetricValue, normalizeMetricFormat, unwrapAppConfig } from '../../utils/formatMetric.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\n\n// The canonical KPI scale (`--cn-kpi-*`) lives in one stylesheet. Imported\n// here as well as from css/index.css so the tokens resolve even when the\n// consuming app pulls in components individually.\nimport '../../css/kpi-card.css'\n\n/**\n * Event-bus channel the PAGE-level Refresh action broadcasts on\n * (CnDashboardPage / CnDetailPage pass it to their CnActionsMenu).\n */\nconst PAGE_REFRESH_BUS_CHANNEL = 'cn:page:refresh'\n\n/**\n * CnStatsBlockWidget — Stats-block dashboard widget.\n *\n * Reads a `dataSource` block (manifest shorthand or raw GraphQL)\n * and forwards the resolved count to CnStatsBlock. Loading state\n * propagates through; resolution errors fall back to count = 0\n * with an empty-state label so the dashboard doesn't blank.\n *\n * Multi-entry mode (ADR-049 / list-widget-enrichment): an `entries[]`\n * array renders N KPIs in one card. Each entry carries the same source\n * contract as a `type:\"stat\"` widget (`register`, `schema`, `metric`,\n * `field`, token-resolved `filter`) plus per-entry presentation\n * (`title`, `variant`, `countLabel`), an optional `route` deep link, and\n * `hideWhenZero` (the entry is omitted when its resolved count is 0).\n *\n * An entry formats its value with `format` (a style name such as\n * `\"currency\"`, or `{ style, currency, currencyField, decimals, prefix,\n * suffix }`), as a `type:\"stat\"` widget's `content.format` does. A money\n * value takes the currency from `currencyField` (a field of the detail\n * page's object, e.g. a contract's own `currency`), then `currency` (a code\n * or `@config.currency`), then the app's reporting currency, then EUR, and\n * is formatted in the user's locale. `currency` / `currencyField` may sit on\n * the entry itself and imply `format: \"currency\"`.\n * Exactly one of `dataSource` / `entries` must be provided — when\n * `entries` is absent, the single-`dataSource` path renders exactly as\n * before.\n *\n * Manifest example (decidesk dashboard):\n * ```json\n * {\n * \"id\": \"minutes-in-review\",\n * \"type\": \"stats-block\",\n * \"title\": \"Notulen ter goedkeuring\",\n * \"iconClass\": \"icon-file\",\n * \"props\": { \"countLabel\": \"notulen\", \"variant\": \"warning\" },\n * \"dataSource\": {\n * \"register\": \"decidesk\",\n * \"schema\": \"minutes\",\n * \"filter\": { \"lifecycle\": \"review\" },\n * \"aggregate\": \"count\"\n * }\n * }\n * ```\n *\n * Multi-entry example (a grouped retention card):\n * ```json\n * {\n * \"widgetKey\": \"stats-block\",\n * \"props\": {\n * \"entries\": [\n * { \"title\": \"Expiring soon\", \"register\": \"docudesk\", \"schema\": \"document\",\n * \"filter\": { \"retention\": { \"lt\": \"@today+30d\" } }, \"variant\": \"warning\",\n * \"route\": { \"name\": \"documents\" }, \"hideWhenZero\": true },\n * { \"title\": \"Archived\", \"register\": \"docudesk\", \"schema\": \"document\",\n * \"filter\": { \"status\": \"archived\" } }\n * ]\n * }\n * }\n * ```\n */\nexport default {\n\tname: 'CnStatsBlockWidget',\n\n\tcomponents: { CnStatsBlock },\n\n\tinject: {\n\t\t/**\n\t\t * Detail-page object context provided by CnDetailPage — enables\n\t\t * `@objectId` / `@object.<field>` tokens in an entry's `filter`.\n\t\t * Null on dashboards.\n\t\t */\n\t\tcnObjectContext: { default: null },\n\t\t/**\n\t\t * Page-level workspace context (reactive `ref({})`) provided by\n\t\t * CnDashboardPage — enables `@workspace.<key>` tokens in an entry's\n\t\t * `filter`. Null on pages that don't provide it.\n\t\t */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/**\n\t\t * Page-level app config provided by the page — the reporting\n\t\t * currency (`currency`) and `@config.<key>` tokens in `format`.\n\t\t */\n\t\tcnAppConfig: { default: () => ({}) },\n\t\t/** The host's label lookup (CnAppRoot provides it); identity without one. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Manifest dataSource block. See `useDataSource`. Exactly one of\n\t\t * `dataSource` / `entries` must be provided — a stats-block widget\n\t\t * without any source has no count to render (flagged with a console\n\t\t * error at mount).\n\t\t *\n\t\t * @type {{\n\t\t * register?: string,\n\t\t * schema?: string,\n\t\t * filter?: object,\n\t\t * aggregate?: 'count',\n\t\t * graphql?: { query: string, variables?: object, selectors: object }\n\t\t * }}\n\t\t */\n\t\tdataSource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Multi-entry declarative sources (ADR-049). Each entry renders one\n\t\t * KPI within this widget's card and self-fetches its own count over\n\t\t * the REST /value aggregation: `{ title, register, schema, metric,\n\t\t * field, filter, route, variant, countLabel, hideWhenZero }`. Filter\n\t\t * values use the shared @-token grammar (`@today`, `@me`,\n\t\t * `@workspace.*`, `?`-optional clauses). An entry with `hideWhenZero`\n\t\t * is omitted when its resolved count is 0. Mutually exclusive with\n\t\t * `dataSource`; defaults to empty (single-source mode).\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\tentries: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * How the single-source value is shown: a style name (`'currency'`,\n\t\t * `'number'`, `'percent'`, `'decimal'`, `'duration-hours'`) or\n\t\t * `{ style, currency, currencyField, decimals, prefix, suffix }`.\n\t\t * Empty (the default) shows the plain localized count. Entries carry\n\t\t * their own `format`.\n\t\t *\n\t\t * @type {string|object|null}\n\t\t */\n\t\tformat: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Block title (manifest `widgetDef.title`). */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Label displayed next to the count. */\n\t\tcountLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Color variant: default | primary | success | warning | error. */\n\t\tvariant: {\n\t\t\ttype: String,\n\t\t\tdefault: 'default',\n\t\t},\n\n\t\t/** Whether to show 0 as a real count instead of the empty label. */\n\t\tshowZeroCount: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Lay the icon left of the content. The canonical KPI card already\n\t\t * does this, so the prop is redundant and kept only for existing\n\t\t * callers.\n\t\t *\n\t\t * @deprecated since 2.25.0, the horizontal layout is the default.\n\t\t */\n\t\thorizontal: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Stack the icon above a centred number instead of beside it. */\n\t\tvertical: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * `stacked` gives every block the board look (see CnStatsBlock\n\t\t * `layout`). `rows` draws the title and then one row per entry (or the\n\t\t * single source): the label on the left, the number on the right, the\n\t\t * label a link when the entry has a `route`. Empty (the default) keeps\n\t\t * the KPI card.\n\t\t *\n\t\t * @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-stats-block-can-take-the-stacked-board-look\n\t\t */\n\t\tlayout: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Draw each tile's own grey box. Off by default — the widget is\n\t\t * rendered inside a CnWidgetWrapper that already draws a card.\n\t\t */\n\t\tfilled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Vue-router location to navigate to on click. When set, the\n\t\t * inner CnStatsBlock renders as a `<router-link>`.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\troute: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional CSS class applied to the widget's outermost wrapping\n\t\t * `<div>`. Designed for Nextcloud core icon classes (`icon-link`,\n\t\t * `icon-mail`, `icon-history`, …) that ship with NC and render a\n\t\t * 16×16 glyph via background-image. When empty (the default), the\n\t\t * wrapper carries only the base `cn-stats-block-widget` class and\n\t\t * is visually invisible.\n\t\t *\n\t\t * The wrapping element is a `<div>` with no layout styles of its\n\t\t * own, so the rendered KPI tile keeps the same dimensions and\n\t\t * spacing it had before this prop was added.\n\t\t *\n\t\t * MDI icon names (e.g. `'Database'`) are NOT supported here — that\n\t\t * would require an async dynamic-import. Use `CnStatsBlock`\n\t\t * directly when you need an MDI Vue component icon.\n\t\t */\n\t\ticonClass: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tsetup(props) {\n\t\t// GraphQL count path is hidden from useDataSource when a plain\n\t\t// register+schema source is given — that case is served over REST\n\t\t// (fetchRest) against OpenRegister's /value aggregation, which is more\n\t\t// robust than the GraphQL count shorthand. Raw `graphql` still flows here.\n\t\tconst dsForGraphql = () => {\n\t\t\tconst ds = props.dataSource\n\t\t\tif (ds && ds.register && ds.schema && !ds.graphql) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn ds\n\t\t}\n\t\tconst { data, loading, error, refetch } = useDataSource(dsForGraphql)\n\t\treturn { dsData: data, loading, error, dsRefetch: refetch }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\trestCount: null,\n\t\t\t/** @type {Array<number|null>} Per-entry resolved counts (multi-entry mode; null = pending/failed). */\n\t\t\tentryCounts: [],\n\t\t\t/** @type {Array<boolean>} Per-entry fetch-in-flight flags (multi-entry mode). */\n\t\t\tentryFetching: [],\n\t\t\t/** Monotonic id used to discard a stale entries batch when `entries` changes mid-flight. */\n\t\t\tentriesRequestId: 0,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the widget runs in multi-entry mode (`entries[]` supplied).\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\thasEntries() {\n\t\t\treturn Array.isArray(this.entries) && this.entries.length > 0\n\t\t},\n\n\t\t/**\n\t\t * Whether the widget draws label/number rows (`layout: \"rows\"`).\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisRows() {\n\t\t\treturn this.layout === 'rows'\n\t\t},\n\n\t\t/**\n\t\t * The rows of the rows layout: one per entry, or a single row from the\n\t\t * `dataSource` (labelled by `countLabel`). A row is `{ key, label,\n\t\t * display, variant, route, href }`; `display` is the formatted value,\n\t\t * a dash while it loads or when the count could not be read.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\trowViews() {\n\t\t\tif (!this.hasEntries) {\n\t\t\t\tconst formatted = this.formattedSingle\n\t\t\t\treturn [{\n\t\t\t\t\tkey: 'single',\n\t\t\t\t\tlabel: this.tl(this.countLabel),\n\t\t\t\t\tdisplay: this.loading ? '\\u2014' : (formatted !== null ? formatted : this.formatCount(this.resolvedCount)),\n\t\t\t\t\tvariant: this.variant || 'default',\n\t\t\t\t\troute: this.route,\n\t\t\t\t\thref: this.route ? resolveHref(this.route, this.$router) : '',\n\t\t\t\t}]\n\t\t\t}\n\t\t\treturn this.entryViews.map((view) => {\n\t\t\t\tconst resolved = view.resolved === true\n\t\t\t\tlet display = '\\u2014'\n\t\t\t\tif (resolved) {\n\t\t\t\t\tdisplay = view.formatted !== null ? view.formatted : this.formatCount(view.count)\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tkey: view.key,\n\t\t\t\t\tlabel: this.tl(view.entry.title || ''),\n\t\t\t\t\tdisplay,\n\t\t\t\t\tvariant: view.entry.variant || 'default',\n\t\t\t\t\troute: view.route,\n\t\t\t\t\thref: view.route ? resolveHref(view.route, this.$router) : '',\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The unwrapped detail-page object context for token resolution, or\n\t\t * null on surfaces (dashboards) that don't provide one.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tobjectCtx() {\n\t\t\tconst c = this.cnObjectContext\n\t\t\tif (!c) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn (typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t},\n\n\t\t/**\n\t\t * The unwrapped workspace context bag (or null). Vue 2.7 inject may\n\t\t * hand back a raw ref; unwrap `.value` for token resolution.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tworkspaceCtx() {\n\t\t\tconst c = this.cnWorkspaceContext\n\t\t\tif (!c) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn (typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t},\n\n\t\t/**\n\t\t * The page-level app config map (reporting currency, `@config` keys).\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tconfigCtx() {\n\t\t\treturn unwrapAppConfig(this.cnAppConfig)\n\t\t},\n\n\t\t/**\n\t\t * The detail page's object, or null on a dashboard. Its fields feed\n\t\t * an entry's `currencyField`.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tpageObject() {\n\t\t\tconst object = this.objectCtx && this.objectCtx.object\n\t\t\treturn object && typeof object === 'object' ? object : null\n\t\t},\n\n\t\t/**\n\t\t * The single-source value formatted per `format`, or null to keep\n\t\t * the plain count.\n\t\t *\n\t\t * @return {string|null}\n\t\t * @spec openspec/changes/stat-currency-from-object/specs/dashboard-page/spec.md#requirement-a-stats-block-entry-formats-its-value\n\t\t */\n\t\tformattedSingle() {\n\t\t\tconst fmt = normalizeMetricFormat({ format: this.format })\n\t\t\tif (!fmt || this.loading) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn formatMetricValue(this.resolvedCount, fmt, this.configCtx, this.pageObject)\n\t\t},\n\n\t\t/**\n\t\t * Token-resolution context for entry filters, merged from the\n\t\t * detail-page object context and the page-level workspace bag.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\ttokenCtx() {\n\t\t\tconst base = this.objectCtx ? { ...this.objectCtx } : {}\n\t\t\tbase.workspace = this.workspaceCtx || {}\n\t\t\treturn base\n\t\t},\n\n\t\t/**\n\t\t * Renderable view models for multi-entry mode: each entry paired with\n\t\t * its fetched count and loading flag, with `hideWhenZero` entries\n\t\t * whose resolved count is 0 omitted.\n\t\t *\n\t\t * @return {Array<{entry: object, key: string, count: number, loading: boolean, formatted: (string|null)}>}\n\t\t * @spec openspec/changes/stat-currency-from-object/specs/dashboard-page/spec.md#requirement-a-stats-block-entry-formats-its-value\n\t\t */\n\t\tentryViews() {\n\t\t\treturn (this.entries || [])\n\t\t\t\t.map((entry, i) => {\n\t\t\t\t\tconst raw = this.entryCounts[i]\n\t\t\t\t\tconst resolved = typeof raw === 'number'\n\t\t\t\t\tconst fmt = normalizeMetricFormat(entry)\n\t\t\t\t\treturn {\n\t\t\t\t\t\t// The value as the entry's `format` says, money in the\n\t\t\t\t\t\t// currency the entry names; null keeps the plain count.\n\t\t\t\t\t\tformatted: fmt && resolved ? formatMetricValue(raw, fmt, this.configCtx, this.pageObject) : null,\n\t\t\t\t\t\tentry: entry || {},\n\t\t\t\t\t\tkey: `${(entry && entry.register) || ''}/${(entry && entry.schema) || ''}/${i}`,\n\t\t\t\t\t\tcount: resolved ? raw : 0,\n\t\t\t\t\t\tloading: !!this.entryFetching[i],\n\t\t\t\t\t\tresolved,\n\t\t\t\t\t\t// Token-resolve the deep link the same way entry.filter\n\t\t\t\t\t\t// is resolved — `route.query: { case: \"@objectId\" }` must\n\t\t\t\t\t\t// land on the target pre-scoped, never as a literal token\n\t\t\t\t\t\t// (ADR-062; parity with object-list's viewAllQuery).\n\t\t\t\t\t\troute: this.resolveEntryRoute(entry && entry.route),\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter((view) => !(view.entry.hideWhenZero && view.resolved && view.count === 0))\n\t\t},\n\n\t\t/**\n\t\t * Stable signature of a REST-fetchable source (else null).\n\t\t *\n\t\t * @spec openspec/specs/dashboard-page/spec.md\n\t\t */\n\t\trestKey() {\n\t\t\tconst ds = this.dataSource || {}\n\t\t\tif (!ds.register || !ds.schema || ds.graphql) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn JSON.stringify({\n\t\t\t\tregister: ds.register,\n\t\t\t\tschema: ds.schema,\n\t\t\t\tmetric: ds.metric || (ds.aggregate === 'count' ? 'count' : 'count'),\n\t\t\t\tfield: ds.field || '',\n\t\t\t\tfilter: ds.filter || {},\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stable signature of the multi-entry sources INCLUDING their\n\t\t * token-resolved filters, so the watcher refetches when page-level\n\t\t * state a `@workspace.*` token reads changes.\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tentriesKey() {\n\t\t\tif (!this.hasEntries) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn JSON.stringify(this.entries.map((entry) => ({\n\t\t\t\tregister: (entry && entry.register) || '',\n\t\t\t\tschema: (entry && entry.schema) || '',\n\t\t\t\tmetric: (entry && entry.metric) || 'count',\n\t\t\t\tfield: (entry && entry.field) || '',\n\t\t\t\tfilter: this.resolvedEntryFilter(entry),\n\t\t\t})))\n\t\t},\n\n\t\tresolvedCount() {\n\t\t\tif (typeof this.restCount === 'number') {\n\t\t\t\treturn this.restCount\n\t\t\t}\n\t\t\tconst value = this.dsData?.count\n\t\t\tif (typeof value === 'number') {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\tif (typeof value === 'string') {\n\t\t\t\tconst parsed = Number(value)\n\t\t\t\treturn Number.isFinite(parsed) ? parsed : 0\n\t\t\t}\n\t\t\treturn 0\n\t\t},\n\t},\n\n\twatch: {\n\t\trestKey() {\n\t\t\tthis.fetchRest()\n\t\t},\n\n\t\tentriesKey() {\n\t\t\tthis.fetchEntries()\n\t\t},\n\t},\n\n\tcreated() {\n\t\t// \"Exactly one of dataSource / entries\" — a misconfigured widget\n\t\t// fails loudly (console error + no counts) instead of rendering an\n\t\t// ambiguous card. Vue 2 prop validators cannot see sibling props, so\n\t\t// the cross-prop rule lives here (and, for manifests, in\n\t\t// validateManifestV2's post-schema check).\n\t\tconst hasDataSource = !!this.dataSource\n\t\tif (hasDataSource === this.hasEntries) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error('[CnStatsBlockWidget] Exactly one of `dataSource` / `entries` must be provided.')\n\t\t}\n\t},\n\n\tmounted() {\n\t\tthis.fetchRest()\n\t\tthis.fetchEntries()\n\t\t// Page-level Refresh. This tile is now rendered inside CnWidgetWrapper\n\t\t// like every other widget (it went flat in 2026-08-30's KPI-card\n\t\t// change, so the wrapper is what draws its card), but it is registered\n\t\t// as a CARD widget and cards carry no per-widget Actions menu — so the\n\t\t// page action is still its only refresh affordance. It reached nothing\n\t\t// before this subscription: the counts come from `fetchRest` /\n\t\t// `fetchEntries` and the useDataSource GraphQL path, none of which\n\t\t// subscribe on their own. No widgetId to match: a page refresh\n\t\t// refreshes everything on the page.\n\t\tthis._onPageRefresh = (payload) => {\n\t\t\tconst done = this.refresh()\n\t\t\tpayload?.waitUntil?.(done)\n\t\t}\n\t\tsubscribe(PAGE_REFRESH_BUS_CHANNEL, this._onPageRefresh)\n\t},\n\n\tbeforeUnmount() {\n\t\tif (this._onPageRefresh) {\n\t\t\tunsubscribe(PAGE_REFRESH_BUS_CHANNEL, this._onPageRefresh)\n\t\t\tthis._onPageRefresh = null\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * A manifest string through the host's label lookup.\n\t\t *\n\t\t * @param {string} text The text as written in the manifest.\n\t\t * @return {string} The translated text.\n\t\t */\n\t\ttl(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * A count in the user's locale.\n\t\t *\n\t\t * @param {number} count The count.\n\t\t * @return {string} The localized count.\n\t\t */\n\t\tformatCount(count) {\n\t\t\treturn new Intl.NumberFormat().format(count)\n\t\t},\n\n\t\t/**\n\t\t * Follow a row label's link in the router, leaving a modified click to the browser.\n\t\t *\n\t\t * @param {MouseEvent} event The click.\n\t\t * @param {object} route The route location.\n\t\t * @return {void}\n\t\t */\n\t\tonRowClick(event, route) {\n\t\t\tfollowLinkClick(event, route, this.$router)\n\t\t},\n\n\t\t/**\n\t\t * Re-query the tile's counts — the REST `/value` aggregation, the\n\t\t * per-entry counts of multi-entry mode, and the `dataSource` GraphQL\n\t\t * path. Exposed as a ref-callable method for parity with the other data\n\t\t * widgets, and invoked by the page-level Refresh subscription.\n\t\t *\n\t\t * @return {Promise<unknown[]>} Settles when every re-query has.\n\t\t */\n\t\trefresh() {\n\t\t\treturn Promise.all([\n\t\t\t\ttypeof this.dsRefetch === 'function' ? this.dsRefetch() : null,\n\t\t\t\tthis.fetchRest(),\n\t\t\t\tthis.fetchEntries(),\n\t\t\t])\n\t\t},\n\n\t\t/**\n\t\t * An entry's `route` deep link with `@`-tokens in `query` / `params`\n\t\t * resolved against `tokenCtx` (same treatment as `entry.filter`), so\n\t\t * a KPI card can deep-link to an index pre-scoped to the current\n\t\t * object. Unresolved optional query clauses are dropped; a route\n\t\t * whose query still carries a REQUIRED unresolved token is returned\n\t\t * without that query entry resolution left as-is (router will show\n\t\t * the unfiltered index rather than a broken literal-token filter).\n\t\t *\n\t\t * @param {object|string|null} route The entry's route location.\n\t\t * @return {object|string|null} The token-resolved route.\n\t\t */\n\t\tresolveEntryRoute(route) {\n\t\t\tif (!route || typeof route !== 'object') {\n\t\t\t\treturn route || null\n\t\t\t}\n\t\t\tconst out = { ...route }\n\t\t\tif (out.query && typeof out.query === 'object') {\n\t\t\t\tout.query = dropOptionalUnresolved(resolveFilterTokens(out.query, this.tokenCtx))\n\t\t\t}\n\t\t\tif (out.params && typeof out.params === 'object') {\n\t\t\t\tout.params = resolveFilterTokens(out.params, this.tokenCtx)\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * An entry's filter with every @-token resolved against `tokenCtx`\n\t\t * and UNRESOLVED OPTIONAL clauses (`@workspace.<key>?`) dropped.\n\t\t *\n\t\t * @param {object} entry The entry definition.\n\t\t * @return {object} The resolved filter map.\n\t\t */\n\t\tresolvedEntryFilter(entry) {\n\t\t\treturn dropOptionalUnresolved(resolveFilterTokens((entry && entry.filter) || {}, this.tokenCtx))\n\t\t},\n\n\t\t/**\n\t\t * Fetch one count/value over REST from OpenRegister's /value\n\t\t * aggregation. Shared by the single-`dataSource` path and the\n\t\t * per-entry multi-entry path. Lazily imports axios/router (the\n\t\t * library never hard-depends on them at module load).\n\t\t *\n\t\t * @param {{register: string, schema: string, metric?: string, aggregate?: string, field?: string}} src The source block.\n\t\t * @param {object} filter The ALREADY-RESOLVED filter map.\n\t\t * @return {Promise<number|null>} The value, or null on failure.\n\t\t */\n\t\tasync fetchValue(src, filter) {\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(\n\t\t\t\t\t'/apps/openregister/api/objects/aggregations/{register}/{schema}/value',\n\t\t\t\t\t{ register: src.register, schema: src.schema },\n\t\t\t\t)\n\t\t\t\tconst params = { metric: src.metric || (src.aggregate === 'count' ? 'count' : 'count') }\n\t\t\t\tif (src.field) {\n\t\t\t\t\tparams.field = src.field\n\t\t\t\t}\n\t\t\t\tfor (const [k, v] of Object.entries(filter || {})) {\n\t\t\t\t\tif (v && typeof v === 'object') {\n\t\t\t\t\t\tfor (const [op, ov] of Object.entries(v)) {\n\t\t\t\t\t\t\tparams[`filter[${k}][${op}]`] = ov\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (v !== '' && v !== null && v !== undefined) {\n\t\t\t\t\t\tparams[`filter[${k}]`] = v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst res = await axios.get(url, { params })\n\t\t\t\treturn Number(res?.data?.value ?? 0) || 0\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fetch the count/value over REST when the single `dataSource` is a\n\t\t * plain register+schema block (operator + dynamic tokens supported).\n\t\t * No-op for raw GraphQL or multi-entry mode. Delegates the request to\n\t\t * `fetchValue` (the per-entry refactor) — behaviour is unchanged.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchRest() {\n\t\t\tconst ds = this.dataSource || {}\n\t\t\tif (!ds.register || !ds.schema || ds.graphql) {\n\t\t\t\tthis.restCount = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.restCount = await this.fetchValue(ds, resolveFilterTokens(ds.filter || {}))\n\t\t},\n\n\t\t/**\n\t\t * Fetch every entry's count in parallel (multi-entry mode). An entry\n\t\t * whose filter still carries an unresolved REQUIRED token (e.g.\n\t\t * `@workspace.selectedClient` with no selection) is skipped rather\n\t\t * than counted across the whole register. A monotonic request id\n\t\t * discards a stale batch when `entries` changes mid-flight.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchEntries() {\n\t\t\tif (!this.hasEntries) {\n\t\t\t\tthis.entryCounts = []\n\t\t\t\tthis.entryFetching = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst id = ++this.entriesRequestId\n\t\t\tconst entries = this.entries\n\t\t\tthis.entryFetching = entries.map(() => true)\n\t\t\tconst counts = await Promise.all(entries.map(async (entry) => {\n\t\t\t\tif (!entry || !entry.register || !entry.schema) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tconst filter = this.resolvedEntryFilter(entry)\n\t\t\t\tif (hasUnresolvedTokens(filter)) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\treturn this.fetchValue(entry, filter)\n\t\t\t}))\n\t\t\tif (id !== this.entriesRequestId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.entryCounts = counts\n\t\t\tthis.entryFetching = entries.map(() => false)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/*\n * This widget draws no KPI of its own — every tile it shows is a CnStatsBlock,\n * so the canonical card in src/css/kpi-card.css is already what a reader sees.\n * All that is left here is how N of those tiles stack inside one widget, and\n * that spacing comes from the shared `--cn-kpi-stack-gap` so a multi-entry\n * widget and a CnStatsPanel stack of the same tiles are spaced identically.\n */\n/*\n * Fill the host cell rather than sizing to content.\n *\n * 🔴 WITHOUT THIS THE WIDGET COLLAPSES TO THE WIDTH OF ITS ICON, and the\n * number it exists to show is clipped to a sliver. Measured on a live\n * instance: a 282px `.cn-widget-wrapper__content` held a 20px widget, a 20px\n * `.cn-kpi-card` and a 0px `.cn-kpi-card__body`. 20px is the icon.\n *\n * The mechanism is a sizing cycle, which is why the KPI card's own CSS looks\n * correct in isolation and is. `.cn-widget-wrapper__content` is `display:\n * flex`, so this root is a flex ITEM and defaults to `flex: 0 1 auto` —\n * content-sized. Its content is `.cn-kpi-card { width: 100% }`, which resolves\n * against THIS element, which is resolving against its content. The card's\n * body carries `min-width: 0` (deliberately, so a long number can shrink\n * instead of overflowing), so the cycle settles with the body at zero and only\n * the icon's intrinsic width surviving.\n *\n * `flex: 1 1 auto` + `width: 100%` gives the card a definite width to resolve\n * against and breaks the cycle; `min-width: 0` keeps the shrink behaviour the\n * body's rule was written for.\n *\n * On the BASE class, not `--multi`: both the single-source and multi-entry\n * modes render this root, so both collapse. The bug was found on a\n * single-entry stats-block.\n */\n.cn-stats-block-widget {\n\tflex: 1 1 auto;\n\tmin-width: 0;\n\twidth: 100%;\n}\n\n.cn-stats-block-widget--multi {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--cn-kpi-stack-gap, 12px);\n}\n\n/* Rows layout: the board's \"label left, number right\" card body. */\n.cn-stats-block-widget--rows {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tbox-sizing: border-box;\n\tpadding: 20px 22px;\n}\n\n.cn-stats-block-widget__rows-title {\n\tmargin: 0;\n\tfont-size: 15px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-stats-block-widget__rows {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmargin: 0;\n\tfont-size: 14px;\n}\n\n.cn-stats-block-widget__row {\n\tdisplay: flex;\n\talign-items: baseline;\n\tjustify-content: space-between;\n\tgap: 16px;\n}\n\n.cn-stats-block-widget__row-label {\n\tmin-width: 0;\n\tmargin: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-stats-block-widget__row-value {\n\tflex: none;\n\tmargin: 0;\n\tfont-size: 17px;\n\tfont-weight: 700;\n\ttext-align: right;\n}\n\n.cn-stats-block-widget__row-value--success {\n\tcolor: var(--color-text-success, var(--color-success-text));\n}\n\n.cn-stats-block-widget__row-value--warning {\n\tcolor: var(--color-text-warning, var(--color-warning-text));\n}\n\n.cn-stats-block-widget__row-value--error {\n\tcolor: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-stats-block-widget__row-value--primary {\n\tcolor: var(--color-primary-element);\n}\n</style>\n"],"names":["_normalizeClass","_createElementBlock","_Fragment","_toDisplayString","_createElementVNode","_renderList","_createTextVNode"],"mappings":"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"}
@@ -7,6 +7,7 @@ import { useEndpointSource } from '../../composables/useEndpointSource.js';
7
7
  import { useObjectStore } from '../../store/useObjectStore.js';
8
8
  import { dispatchAction, resolveObjectOpType } from '../../utils/actionsDispatcher.js';
9
9
  import { resolveObjectTokenContext } from '../../utils/detailObjectContext.js';
10
+ import { pageFilter, pageSort } from '../../utils/pageControls.js';
10
11
  import { dropOptionalUnresolved, resolveFilterTokens, hasUnresolvedTokens } from '../../utils/resolveFilterTokens.js';
11
12
  import { readVisibleWhenPath, compareVisibleWhen } from '../../utils/visibleWhen.js';
12
13
  import __sfc_main$3 from '../CnIcon/CnIcon.vue.js';
@@ -436,7 +437,8 @@ var script = {
436
437
  * @return {object}
437
438
  */
438
439
  resolvedFilter() {
439
- return dropOptionalUnresolved(resolveFilterTokens((this.source && this.source.filter) || {}, this.tokenCtx))
440
+ const filter = pageFilter(this.source && this.source.pageControls, this.workspaceCtx, this.source && this.source.filter);
441
+ return dropOptionalUnresolved(resolveFilterTokens(filter, this.tokenCtx))
440
442
  },
441
443
 
442
444
  /**
@@ -509,7 +511,10 @@ var script = {
509
511
  resolvedFetchParams() {
510
512
  const src = this.source || {};
511
513
  const params = {};
512
- for (const [field, dir] of Object.entries(src.order || {})) {
514
+ // A page sort control (`source.pageControls.sort`) replaces the order.
515
+ const chosen = pageSort(src.pageControls, this.workspaceCtx, null);
516
+ const order = chosen ? { [chosen.field]: chosen.dir } : (src.order || {});
517
+ for (const [field, dir] of Object.entries(order)) {
513
518
  params[`_order[${field}]`] = dir === 'desc' ? 'desc' : 'asc';
514
519
  }
515
520
  if (typeof src.limit === 'number' && src.limit > 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"CnWidgetObjectTable.vue2.js","sources":["../../../../src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue"],"sourcesContent":["<!--\n CnWidgetObjectTable — built-in v2 widget wrapping CnDataTable.\n\n Referenced in v2 manifests via `widgetKey: \"object-table\"`. Renders the\n table on the shared CnWidgetWrapper chrome (title + standard overflow\n Actions menu: Refresh / Documentation / Request a feature) and forwards\n all data props to CnDataTable so the table itself is a transparent\n pass-through.\n\n ADR-049 enrichment: a declarative token-resolved `source` drives\n CnDataTable's existing self-fetch (externally supplied `rows` always win),\n the compact-list surface (hideHeader / borderless / rowRoute /\n viewAllRoute / emptyText / rowIcon / #footer) passes straight through,\n and a declarative `actions[]` renders row-scoped actions via CnRowActions\n plus widget-scoped `object-op` create affordances in the footer.\n\n Spec: REQ-MVR-006 (manifest-v2-renderer) — built-in widget: object-table\n Spec: openspec/changes/list-widget-enrichment/specs/manifest-v2-renderer/spec.md\n-->\n<template>\n\t<!-- `refreshing` mirrors the endpoint fetch so the chrome's Refresh\n\t action spins (and stays disabled) for the real duration of an\n\t in-flight refetch. -->\n\t<component\n\t\t:is=\"hideWrapper ? 'CnWidgetHostShell' : 'CnWidgetWrapper'\"\n\t\tv-bind=\"hideWrapper ? {} : { title, widgetId, documentationUrl, flush: true, refreshing: epLoading }\">\n\t\t<div class=\"cn-widget-object-table\">\n\t\t\t<CnDataTable ref=\"dataTable\" v-bind=\"{ ...innerProps, ...$attrs }\">\n\t\t\t\t<!-- Forward every host-supplied CnDataTable scoped slot verbatim\n\t\t\t\t (#footer { total, shown }, #empty, #column-<key>, …). The\n\t\t\t\t #row-actions slot is NOT forwarded while the widget renders\n\t\t\t\t its own declarative actions[] menu. -->\n\t\t\t\t<template v-for=\"(_, name) in forwardedScopedSlots\" #[name]=\"slotData\">\n\t\t\t\t\t<!-- @slot Any CnDataTable scoped slot, forwarded verbatim — e.g. #footer ({ total, shown }), #empty, #column-<key>. #row-actions is withheld while the declarative actions[] menu renders. -->\n\t\t\t\t\t<slot :name=\"name\" v-bind=\"slotData\" />\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"rowScopedActions.length > 0\" #row-actions=\"{ row }\">\n\t\t\t\t\t<CnRowActions :actions=\"mappedRowActions\" :row=\"row\" />\n\t\t\t\t</template>\n\t\t\t</CnDataTable>\n\n\t\t\t<!-- Widget-scoped object-op create affordance: `create` has no row\n\t\t\t to mutate, so it renders in the list footer, never inside the\n\t\t\t per-row actions menu (ADR-049 / list-widget-enrichment). -->\n\t\t\t<div v-if=\"createActions.length > 0\" class=\"cn-widget-object-table__footer\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-for=\"action in createActions\"\n\t\t\t\t\t:key=\"action.id || action.label\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:data-testid=\"`cn-widget-create-${action.id || 'action'}`\"\n\t\t\t\t\t@click=\"onActionTriggered(action, null)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\n\t\t\t<!-- A rejected write (OpenRegister RBAC) surfaces here — local row\n\t\t\t state is never mutated on failure (the store only writes its\n\t\t\t caches on success). -->\n\t\t\t<p v-if=\"opError\" class=\"cn-widget-object-table__error\" data-testid=\"cn-widget-object-table-error\">\n\t\t\t\t{{ opError }}\n\t\t\t</p>\n\n\t\t\t<CnConfirmDialog\n\t\t\t\tv-if=\"confirmAction\"\n\t\t\t\tref=\"confirmDialog\"\n\t\t\t\t:dialogTitle=\"confirmAction.label\"\n\t\t\t\t:message=\"confirmMessage\"\n\t\t\t\t:variant=\"confirmAction.op === 'delete' ? 'error' : 'primary'\"\n\t\t\t\t:confirmLabel=\"confirmAction.label\"\n\t\t\t\t@confirm=\"onConfirmConfirmed\"\n\t\t\t\t@close=\"closeConfirm\" />\n\t\t</div>\n\t</component>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport { inject, ref } from 'vue'\nimport CnConfirmDialog from '../../dialogs/CnConfirmDialog.vue'\nimport CnDataTable from '../CnDataTable/CnDataTable.vue'\nimport { useEndpointSource } from '../../composables/useEndpointSource.js'\nimport { useObjectStore } from '../../store/useObjectStore.js'\nimport { dispatchAction, resolveObjectOpType } from '../../utils/actionsDispatcher.js'\nimport { resolveObjectTokenContext } from '../../utils/detailObjectContext.js'\nimport { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { compareVisibleWhen, readVisibleWhenPath } from '../../utils/visibleWhen.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnRowActions } from '../CnRowActions/index.js'\nimport { CnWidgetWrapper } from '../CnWidgetWrapper/index.js'\n// Chrome-less pass-through used when `hideWrapper` is set (see hostShell.js\n// for why it lives in its own module).\nimport { CnWidgetHostShell } from './hostShell.js'\n\n/**\n * CnWidgetObjectTable — built-in v2 widget wrapping CnDataTable.\n *\n * Renders a data table on the shared CnWidgetWrapper chrome, which supplies\n * the widget title and the standard overflow Actions menu (Refresh /\n * Documentation / Request a feature). All data props are forwarded to the\n * inner CnDataTable; the chrome props (`title`, `documentationUrl`,\n * `widgetId`) are consumed by the wrapper and not passed down.\n *\n * A declarative `source` makes the widget self-fetch from OpenRegister:\n * `source.filter` is token-resolved with the shared `resolveFilterTokens`\n * grammar (`@me`, `@today`, `@workspace.*`, `?`-optional clauses dropped\n * when unresolved via `dropOptionalUnresolved`), `source.order` /\n * `source.limit` drive the fetch, and an `@resolve:` register sentinel is\n * passed through unexpanded (resolution is the host loader's job).\n * Externally supplied `rows` always win — the pre-existing pass-through\n * interface is unchanged.\n *\n * Wave 2 (#91): a declarative `endpointSource` (`{ url, method?, params?,\n * responsePath? }`) is the alternative to the OpenRegister `source` — rows\n * come from an app REST endpoint's payload at `responsePath`, fetched by the\n * shared `useEndpointSource` engine (token-resolved params, request dedup +\n * short-TTL cache, `cn:page:refresh` / `cn:widget:refresh` subscription).\n * Exactly one of `source` | `endpointSource`; columns / formatters /\n * `rowRoute` / `actions` apply unchanged on top of endpoint rows.\n *\n * A declarative `actions[]` array renders `patch` / `delete` `object-op`\n * actions (and reused `handler` / `open-modal` / `open-page` / `navigate`\n * actions) per row through `CnRowActions`; an `object-op` `create` action\n * renders as a widget-scoped footer affordance. `delete` is ALWAYS\n * confirm-gated through `CnConfirmDialog`; `patch` / `create` confirm only\n * on `confirm: true`. Mutations dispatch via the shared object store —\n * the manifest declares intent only, OpenRegister RBAC is the authority,\n * and a rejected write surfaces as an error without local mutation.\n *\n * A declarative `rowClass` (#91) — `[{ when: { field, op?, value }, class }]` reusing\n * the shared `visibleWhen` predicate grammar — is compiled into CnDataTable's\n * `rowClass` function so overdue / at-risk rows can be highlighted from the manifest\n * (a host-supplied `rowClass` FUNCTION still passes straight through).\n */\nexport default {\n\tname: 'CnWidgetObjectTable',\n\n\tcomponents: { CnDataTable, CnWidgetWrapper, CnWidgetHostShell, CnRowActions, CnIcon, NcButton, CnConfirmDialog },\n\n\tinject: {\n\t\t/**\n\t\t * Detail-page object context (`{ objectId, object, register, schema }`)\n\t\t * provided by CnDetailPage — enables `@objectId` / `@object.<field>`\n\t\t * tokens in `source.filter`. Null on dashboards.\n\t\t */\n\t\tcnObjectContext: { default: null },\n\t\t/**\n\t\t * v2 slot-grid detail context holder (`{ value: { objectData, schema,\n\t\t * objectType, objectId, register, store } | null }`) provided by\n\t\t * CnPageRenderer — backfills the object token context so\n\t\t * `@objectId` / `@object.<field>` resolve on detail surfaces where\n\t\t * CnDetailPage is not an ancestor (#91 Wave 3).\n\t\t */\n\t\tcnDetailObjectContext: { default: null },\n\t\t/**\n\t\t * Page-level workspace context (reactive `ref({})`) provided by\n\t\t * CnDashboardPage — enables `@workspace.<key>` tokens in\n\t\t * `source.filter`. Null on pages that don't provide it.\n\t\t */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/**\n\t\t * Pre-bound `dispatchAction` provided by CnPageRenderer (router,\n\t\t * registry, handlers, openModal already wired). Used for the reused\n\t\t * non-mutating action types; falls back to a direct `dispatchAction`\n\t\t * call when mounted outside a CnPageRenderer tree.\n\t\t */\n\t\tcnDispatchAction: { default: null },\n\t},\n\n\tinheritAttrs: false,\n\n\tprops: {\n\t\t/**\n\t\t * Widget title shown in the CnWidgetWrapper header.\n\t\t */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Table'),\n\t\t},\n\n\t\t/**\n\t\t * Documentation link surfaced in the widget's overflow Actions menu.\n\t\t * Empty (the default) hides the Documentation item; the Refresh and\n\t\t * Request-a-feature items always render.\n\t\t */\n\t\tdocumentationUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Stable id forwarded to the widget chrome for the Refresh /\n\t\t * Request-a-feature payloads.\n\t\t */\n\t\twidgetId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Render content-only, WITHOUT the widget's own CnWidgetWrapper\n\t\t * chrome. Set by hosts that already provide the card chrome — e.g.\n\t\t * CnDashboardPage's dashboard widget grid, whose registry branch\n\t\t * wraps every catalog widget in its own CnWidgetWrapper (a naive\n\t\t * mount would double-card). Default `false` keeps the pre-existing\n\t\t * self-chromed v2 rendering byte-for-byte.\n\t\t */\n\t\thideWrapper: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Register slug. Forwarded to CnDataTable. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Schema slug. Forwarded to CnDataTable. */\n\t\tschema: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Column definitions. Forwarded to CnDataTable — bare string keys OR\n\t\t * the object form (`{ key, label, sortable, width, cellClass,\n\t\t * formatter, widget, format, aggregate }`) both pass straight through.\n\t\t *\n\t\t * @type {Array<object|string>}\n\t\t */\n\t\tcolumns: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Rows array. Forwarded to CnDataTable. External rows always win over `source`. */\n\t\trows: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Loading state. Forwarded to CnDataTable. */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Declarative self-fetch source (ADR-049). When set and no external\n\t\t * `rows` are supplied, the widget resolves `filter` @-tokens and\n\t\t * drives CnDataTable's existing self-fetch with the resolved filter,\n\t\t * `order`, and `limit`. `register` MAY carry an `@resolve:` sentinel —\n\t\t * it is passed through unexpanded. `extend` (#91 Wave 3) forwards\n\t\t * OpenRegister `_extend[]` values (e.g. `[\"calculations\"]`) on the\n\t\t * fetch so virtual/declarative calc fields ride along and render as\n\t\t * ordinary columns. Default `null` keeps the pre-existing\n\t\t * pass-through behaviour byte-for-byte.\n\t\t *\n\t\t * @type {{register?: string, schema?: string, filter?: object, order?: object, limit?: number, extend?: string[]}|null}\n\t\t */\n\t\tsource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Endpoint data binding (Wave 2, #91) — the alternative to the\n\t\t * OpenRegister `source` for rows an app REST endpoint computes (e.g. a\n\t\t * per-source performance report). Rows come from the payload at\n\t\t * `responsePath` (a dot-path; the plucked value must be an array —\n\t\t * anything else renders as empty). `params` values use the shared\n\t\t * filter-token grammar (`@workspace.dateFrom?`, `@me`, `@today±Nd`, …)\n\t\t * and re-resolve + refetch when the page context changes. Requests are\n\t\t * deduped + short-TTL cached per (url+params) by the shared\n\t\t * `useEndpointSource` engine, which also subscribes to\n\t\t * `cn:page:refresh` / `cn:widget:refresh`. Exactly one of `source` |\n\t\t * `endpointSource` (validator-enforced); external `rows` still win\n\t\t * over both. `columns` / formatters / `rowRoute` / `actions` apply\n\t\t * unchanged on top of endpoint rows.\n\t\t *\n\t\t * @type {{url: string, method?: string, params?: object, responsePath?: string}|null}\n\t\t */\n\t\tendpointSource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Declarative actions (unified manifest action shape — `handler` |\n\t\t * `open-modal` | `open-page` | `navigate` | `object-op`). `object-op`\n\t\t * `patch` / `delete` (and every non-mutating type) render per row via\n\t\t * CnRowActions; `object-op` `create` renders as a widget-scoped footer\n\t\t * affordance. `delete` always confirms; `patch` / `create` confirm on\n\t\t * `confirm: true`. Authorization-shaped fields on an action have no\n\t\t * effect — OpenRegister RBAC is the only authority.\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 * Hide the column-header row. Forwarded to CnDataTable — the compact\n\t\t * dashboard-list surface.\n\t\t */\n\t\thideHeader: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Drop CnDataTable's card chrome (border, radius, shadow) so the table\n\t\t * sits flush inside the widget card. Forwarded to CnDataTable.\n\t\t */\n\t\tborderless: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Fit the table to the tile: the title column (a column's `grow: true`,\n\t\t * else `title` or `name`, else the first) shrinks and ends in an\n\t\t * ellipsis so the trailing columns stay visible. On by default; pass\n\t\t * `false` for the table's own auto layout. Forwarded to CnDataTable.\n\t\t *\n\t\t * @spec openspec/changes/narrow-object-table-widgets/specs/narrow-object-table-widgets/spec.md#requirement-a-narrow-object-table-keeps-its-trailing-columns-in-view\n\t\t */\n\t\tfitWidth: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Route NAME for row-click navigation. Mapped onto CnDataTable's\n\t\t * `rowClickRoute` as `{ name: rowRoute, params: { id } }` with the\n\t\t * row's `id` (or `@self.id`). Empty (the default) = no navigation.\n\t\t */\n\t\trowRoute: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Client-side row cap, forwarded to CnDataTable (`0` = all rows).\n\t\t * This is what makes `viewAllRoute` reachable for `endpointSource`\n\t\t * tables: the endpoint's full payload stays the `totalRowCount`\n\t\t * while CnDataTable renders only the first `limit` rows, so the\n\t\t * \"View all\" footer condition (total > shown) can finally hold.\n\t\t * The OpenRegister `source` mode keeps using `source.limit` (an\n\t\t * explicit assignment that wins over this passthrough).\n\t\t */\n\t\tlimit: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0,\n\t\t},\n\n\t\t/**\n\t\t * vue-router route object for the \"View all\" footer link. Forwarded\n\t\t * to CnDataTable (footer shows when the rows are a `limit`-ed subset).\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tviewAllRoute: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Pre-translated \"View all\" footer label. Forwarded to CnDataTable. */\n\t\tviewAllLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: undefined,\n\t\t},\n\n\t\t/** Text shown when there are no rows. Forwarded to CnDataTable. */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: undefined,\n\t\t},\n\n\t\t/**\n\t\t * App wording for an unavailable personal lens (a `source.filter._recent`\n\t\t * tile on an instance that does not log reads), keyed `<lens>.<reason>`\n\t\t * or `<reason>`. Forwarded to CnDataTable, which shows the library's\n\t\t * object-neutral text without it.\n\t\t *\n\t\t * @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-an-unavailable-lens-explains-its-empty-page\n\t\t */\n\t\tlensReasonTexts: {\n\t\t\ttype: Object,\n\t\t\tdefault: undefined,\n\t\t},\n\n\t\t/**\n\t\t * Leading per-row icon: a static MDI icon name string, or a function\n\t\t * `(row) => iconName`. Forwarded to CnDataTable.\n\t\t *\n\t\t * @type {string|((row: object) => string)|null}\n\t\t */\n\t\trowIcon: {\n\t\t\ttype: [String, Function],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Per-row CSS class binding (#91). Two forms:\n\t\t * - a FUNCTION `(row) => string` — passed straight through to\n\t\t * CnDataTable's `rowClass` (the pre-existing host-supplied contract).\n\t\t * - an ARRAY of declarative rules `[{ when: { field, op?, value }, class }]`\n\t\t * compiled here into that function. Each rule adds its `class` to a\n\t\t * row when the shared `visibleWhen` predicate holds against the row:\n\t\t * `field` is a dot-path into the row, `op` is `eq|neq|gt|gte|lt|lte`\n\t\t * (default `eq`), `value` is the literal right-hand side. Rules are\n\t\t * evaluated in order and every matching `class` is space-joined, so an\n\t\t * overdue / at-risk row can be highlighted declaratively from a\n\t\t * manifest with no bespoke function. Default `null` = no row class.\n\t\t *\n\t\t * @type {((row: object) => string)|Array<{when: {field: string, op?: string, value: unknown}, class: string}>|null}\n\t\t */\n\t\trowClass: {\n\t\t\ttype: [Function, Array],\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['object-op'],\n\n\tsetup(props) {\n\t\t// Endpoint binding (Wave 2, #91): the shared useEndpointSource engine\n\t\t// owns token resolution, request dedup + TTL caching, and the\n\t\t// cn:page:refresh / cn:widget:refresh subscriptions (the widget's own\n\t\t// `widgetId` is matched against widget-scoped refresh payloads).\n\t\t// No-op while `endpointSource` is null, so the OpenRegister `source`\n\t\t// self-fetch path is untouched. Injects re-read in setup (same\n\t\t// resolution as the Options `inject` block).\n\t\tconst objectCtxRaw = inject('cnObjectContext', null)\n\t\tconst detailCtxRaw = inject('cnDetailObjectContext', null)\n\t\tconst workspaceRaw = inject('cnWorkspaceContext', ref(null))\n\t\tconst appConfigRaw = inject('cnAppConfig', ref({}))\n\t\tconst unwrap = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\t\t// External rows always win — and suppress the request entirely (the\n\t\t// pre-existing \"when rows are provided, no API calls are made\"\n\t\t// contract), so the getter resolves to null while `rows` are supplied.\n\t\tconst { data, loading, error, refetch } = useEndpointSource(\n\t\t\t() => ((props.rows && props.rows.length > 0) ? null : props.endpointSource),\n\t\t\t{\n\t\t\t\tctx: () => ({\n\t\t\t\t\t...(resolveObjectTokenContext(objectCtxRaw, detailCtxRaw) || {}),\n\t\t\t\t\tworkspace: unwrap(workspaceRaw) || {},\n\t\t\t\t\tconfig: unwrap(appConfigRaw) || {},\n\t\t\t\t}),\n\t\t\t\twidgetId: () => props.widgetId,\n\t\t\t},\n\t\t)\n\t\treturn { epData: data, epLoading: loading, epError: error, epRefetch: refetch }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Error message from the last rejected object-op write ('' = none). */\n\t\t\topError: '',\n\t\t\t/** @type {object|null} The object-op action awaiting confirmation (null = no dialog). */\n\t\t\tconfirmAction: null,\n\t\t\t/** @type {object|null} The row the pending confirm-gated action targets. */\n\t\t\tconfirmRow: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The merged detail-page object context for token resolution — both\n\t\t * detail-surface injects, holder fields backfilling (#91 Wave 3) —\n\t\t * or null on surfaces (dashboards) that don't provide one.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tobjectCtx() {\n\t\t\treturn resolveObjectTokenContext(this.cnObjectContext, this.cnDetailObjectContext)\n\t\t},\n\n\t\t/**\n\t\t * The unwrapped workspace context bag (or null). Vue 2.7 inject may\n\t\t * hand back a raw ref; unwrap `.value` for token resolution.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tworkspaceCtx() {\n\t\t\tconst c = this.cnWorkspaceContext\n\t\t\tif (!c) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn (typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t},\n\n\t\t/**\n\t\t * Token-resolution context merged from the detail-page object context\n\t\t * and the page-level workspace bag (`@workspace.<key>`).\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\ttokenCtx() {\n\t\t\tconst base = this.objectCtx ? { ...this.objectCtx } : {}\n\t\t\tbase.workspace = this.workspaceCtx || {}\n\t\t\treturn base\n\t\t},\n\n\t\t/**\n\t\t * `source.filter` with every @-token resolved against `tokenCtx`, then\n\t\t * with any UNRESOLVED OPTIONAL clause (`@workspace.<key>?`) dropped —\n\t\t * an optional clause simply shows all rows until its context is set.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tresolvedFilter() {\n\t\t\treturn dropOptionalUnresolved(resolveFilterTokens((this.source && this.source.filter) || {}, this.tokenCtx))\n\t\t},\n\n\t\t/**\n\t\t * Whether a REQUIRED context token in `source.filter` is still\n\t\t * unresolved — the widget then skips the fetch instead of querying the\n\t\t * whole register.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\twaitingForContext() {\n\t\t\treturn !!this.source && hasUnresolvedTokens(this.resolvedFilter)\n\t\t},\n\n\t\t/**\n\t\t * Whether the Wave-2 `endpointSource` drives the rows: a usable\n\t\t * endpoint config and no external rows (external rows always win).\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tendpointActive() {\n\t\t\treturn !!(this.endpointSource && this.endpointSource.url)\n\t\t\t\t&& (!this.rows || this.rows.length === 0)\n\t\t},\n\n\t\t/**\n\t\t * Rows resolved from the endpoint payload. The payload (after the\n\t\t * `responsePath` pluck the shared engine applies) must be an ARRAY;\n\t\t * anything else renders as an empty table rather than crashing the\n\t\t * column renderer.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tendpointRows() {\n\t\t\treturn Array.isArray(this.epData) ? this.epData : []\n\t\t},\n\n\t\t/**\n\t\t * Whether the declarative `source` drives a self-fetch: a usable\n\t\t * source, no external rows (external rows always win), no active\n\t\t * endpoint binding (exactly-one-of — endpointSource wins when both\n\t\t * slip past the validator), and no unresolved required token.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tselfFetchActive() {\n\t\t\treturn !!(this.source && this.source.register && this.source.schema)\n\t\t\t\t&& (!this.rows || this.rows.length === 0)\n\t\t\t\t&& !this.endpointActive\n\t\t\t\t&& !this.waitingForContext\n\t\t},\n\n\t\t/**\n\t\t * Query params handed to CnDataTable's self-fetch: the resolved filter\n\t\t * as direct field params (OpenRegister object-search shape),\n\t\t * `_order[field]` entries from `source.order`, and — when\n\t\t * `source.limit` is set — `_limit` of limit + 1 so CnDataTable's\n\t\t * View-all footer can detect that more rows exist while the display\n\t\t * caps at `limit`.\n\t\t *\n\t\t * IN-lists: an ARRAY filter value (either directly on the field or via\n\t\t * the `{ in: [...] }` operator form) is emitted as the bare field with\n\t\t * the array value, which axios serializes as repeated bracket params\n\t\t * (`field[]=a&field[]=b`) — the same shape `buildQueryString` /\n\t\t * `useObjectStore` send and the only IN form OpenRegister parses (a\n\t\t * `field[in]`-style key is NOT understood server-side). Empty arrays\n\t\t * are skipped (no constraint).\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tresolvedFetchParams() {\n\t\t\tconst src = this.source || {}\n\t\t\tconst params = {}\n\t\t\tfor (const [field, dir] of Object.entries(src.order || {})) {\n\t\t\t\tparams[`_order[${field}]`] = dir === 'desc' ? 'desc' : 'asc'\n\t\t\t}\n\t\t\tif (typeof src.limit === 'number' && src.limit > 0) {\n\t\t\t\tparams._limit = src.limit + 1\n\t\t\t}\n\t\t\t// `source.extend` (#91 Wave 3): forwarded as OR's repeated\n\t\t\t// `_extend[]` param so virtual/declarative fields (e.g.\n\t\t\t// `calculations` — procest daysOverdue) ride the fetch and are\n\t\t\t// displayable as ordinary columns. Serialized by the shared\n\t\t\t// query builder (array value → repeated `key[]`).\n\t\t\tif (Array.isArray(src.extend) && src.extend.length > 0) {\n\t\t\t\tparams._extend = src.extend.filter((e) => typeof e === 'string' && e !== '')\n\t\t\t}\n\t\t\tconst filter = this.resolvedFilter\n\t\t\tif (filter && typeof filter === 'object') {\n\t\t\t\tfor (const [k, v] of Object.entries(filter)) {\n\t\t\t\t\tif (Array.isArray(v)) {\n\t\t\t\t\t\tif (v.length > 0) {\n\t\t\t\t\t\t\tparams[k] = v\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (v && typeof v === 'object') {\n\t\t\t\t\t\tfor (const [op, ov] of Object.entries(v)) {\n\t\t\t\t\t\t\tif (op === 'in' && Array.isArray(ov)) {\n\t\t\t\t\t\t\t\t// OR's IN form is the bare repeated field param.\n\t\t\t\t\t\t\t\tif (ov.length > 0) {\n\t\t\t\t\t\t\t\t\tparams[k] = ov\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tparams[`${k}[${op}]`] = ov\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (v !== '' && v !== null && v !== undefined) {\n\t\t\t\t\t\tparams[k] = v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn params\n\t\t},\n\n\t\t/**\n\t\t * `$props` minus the chrome props (`title`, `documentationUrl`,\n\t\t * `widgetId`, `hideWrapper`) and the widget-only props (`source`,\n\t\t * `endpointSource`, `actions`, `rowRoute`), so they are consumed here\n\t\t * and never forwarded to the inner CnDataTable. `undefined` values are\n\t\t * dropped so CnDataTable's own prop defaults apply. When the\n\t\t * declarative `source` is active it supplies `register` / `schemaId` /\n\t\t * `fetchParams` / `limit`; an `@resolve:` register sentinel passes\n\t\t * through unexpanded. When the Wave-2 `endpointSource` is active it\n\t\t * supplies the resolved `rows` + `loading` instead — columns,\n\t\t * formatters, and row navigation apply unchanged on top.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tinnerProps() {\n\t\t\tconst { title, documentationUrl, widgetId, hideWrapper, source, endpointSource, actions, rowRoute, rowClass, schema, ...rest } = this.$props\n\t\t\tconst inner = {}\n\t\t\tfor (const [k, v] of Object.entries(rest)) {\n\t\t\t\tif (v !== undefined) {\n\t\t\t\t\tinner[k] = v\n\t\t\t\t}\n\t\t\t}\n\t\t\t// This widget's `schema` is a slug, while CnDataTable's `schema` is a\n\t\t\t// schema object, so it is not forwarded. Nor is it mapped to `schemaId`:\n\t\t\t// that would turn on CnDataTable's unscoped self-fetch whenever the\n\t\t\t// widget's own rows are empty. Self-fetch comes from `source` alone.\n\t\t\t// `rowClass` is consumed here (function or declarative rules[]) and\n\t\t\t// forwarded to CnDataTable as a compiled `(row) => class` function.\n\t\t\tif (this.compiledRowClass) {\n\t\t\t\tinner.rowClass = this.compiledRowClass\n\t\t\t}\n\t\t\tif (this.selfFetchActive) {\n\t\t\t\tinner.register = this.source.register\n\t\t\t\tinner.schemaId = this.source.schema\n\t\t\t\tinner.fetchParams = this.resolvedFetchParams\n\t\t\t\tif (typeof this.source.limit === 'number' && this.source.limit > 0) {\n\t\t\t\t\tinner.limit = this.source.limit\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.endpointActive) {\n\t\t\t\tinner.rows = this.endpointRows\n\t\t\t\tinner.loading = this.epLoading || this.loading\n\t\t\t}\n\t\t\tif (this.rowRoute) {\n\t\t\t\tinner.rowClickRoute = this.rowRouteFn\n\t\t\t}\n\t\t\treturn inner\n\t\t},\n\n\t\t/**\n\t\t * The effective `rowClass` function forwarded to CnDataTable. A\n\t\t * host-supplied FUNCTION passes straight through; a declarative\n\t\t * rules[] array is compiled into a `(row) => string` that space-joins\n\t\t * every rule whose shared `visibleWhen` predicate holds against the\n\t\t * row (`field` dot-path + `op` + `value`). Null when `rowClass` is\n\t\t * unset or an empty array — the widget then forwards nothing so\n\t\t * CnDataTable's own default applies.\n\t\t *\n\t\t * @return {?(row: object) => string} The row-class function, or null.\n\t\t */\n\t\tcompiledRowClass() {\n\t\t\tconst rc = this.rowClass\n\t\t\tif (typeof rc === 'function') {\n\t\t\t\treturn rc\n\t\t\t}\n\t\t\tif (!Array.isArray(rc) || rc.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst rules = rc\n\t\t\treturn (row) => {\n\t\t\t\tconst classes = []\n\t\t\t\tfor (const rule of rules) {\n\t\t\t\t\tif (!rule || !rule.class) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tconst when = rule.when || {}\n\t\t\t\t\tconst actual = readVisibleWhenPath(row, when.field)\n\t\t\t\t\tif (compareVisibleWhen(actual, when.op || 'eq', when.value)) {\n\t\t\t\t\t\tclasses.push(rule.class)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn classes.join(' ')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Row-click navigation function derived from the `rowRoute` route\n\t\t * name (CnDataTable's `rowClickRoute` shape).\n\t\t *\n\t\t * @return {(row: object) => (object|null)} A vue-router location, or null for a row with no id.\n\t\t */\n\t\trowRouteFn() {\n\t\t\treturn (row) => {\n\t\t\t\tconst id = row && (row.id || (row['@self'] && row['@self'].id))\n\t\t\t\treturn id ? { name: this.rowRoute, params: { id: String(id) } } : null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Actions rendered per row: every non-`object-op` type plus\n\t\t * `object-op` `patch` / `delete` (they mutate THAT row's object).\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\trowScopedActions() {\n\t\t\treturn (this.actions || []).filter((a) => a && (a.type !== 'object-op' || a.op === 'patch' || a.op === 'delete'))\n\t\t},\n\n\t\t/**\n\t\t * `object-op` `create` actions — widget-scoped (no row to mutate), so\n\t\t * they render in the list footer, never in the row menu.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tcreateActions() {\n\t\t\treturn (this.actions || []).filter((a) => a && a.type === 'object-op' && a.op === 'create')\n\t\t},\n\n\t\t/**\n\t\t * Row-scoped actions mapped onto the CnRowActions shape: label + icon\n\t\t * pass through, `object-op` `delete` renders destructive, and the\n\t\t * handler routes back through the widget's confirm-gated dispatcher.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tmappedRowActions() {\n\t\t\treturn this.rowScopedActions.map((action) => ({\n\t\t\t\tlabel: action.label,\n\t\t\t\ticon: action.icon,\n\t\t\t\tdestructive: action.type === 'object-op' && action.op === 'delete',\n\t\t\t\thandler: (row) => this.onActionTriggered(action, row),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Host-supplied CnDataTable scoped slots to forward (keys only). The\n\t\t * `row-actions` slot is withheld while the widget renders its own\n\t\t * declarative actions menu.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tforwardedScopedSlots() {\n\t\t\tconst out = {}\n\t\t\tfor (const name of Object.keys(this.$slots || {})) {\n\t\t\t\tif (name === 'row-actions' && this.rowScopedActions.length > 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tout[name] = true\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * Confirmation question for the pending confirm-gated action —\n\t\t * destructive copy for `delete`, a generic confirm otherwise.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tconfirmMessage() {\n\t\t\tif (this.confirmAction && this.confirmAction.op === 'delete') {\n\t\t\t\treturn t('nextcloud-vue', 'Are you sure you want to delete this item? This action cannot be undone.')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Are you sure you want to continue?')\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Entry point for every declarative action trigger (row menu or\n\t\t * footer create button). `object-op` `delete` ALWAYS confirms;\n\t\t * `patch` / `create` confirm only when the action sets\n\t\t * `confirm: true`; everything else dispatches immediately.\n\t\t *\n\t\t * @param {object} action The manifest action.\n\t\t * @param {object|null} row The row for row-scoped actions (null for create).\n\t\t * @return {Promise<void>|void}\n\t\t */\n\t\tonActionTriggered(action, row) {\n\t\t\tif (action && action.type === 'object-op' && (action.op === 'delete' || action.confirm === true)) {\n\t\t\t\tthis.confirmAction = action\n\t\t\t\tthis.confirmRow = row\n\t\t\t\treturn\n\t\t\t}\n\t\t\treturn this.runAction(action, row)\n\t\t},\n\n\t\t/**\n\t\t * Confirm-dialog primary button: run the pending action and report\n\t\t * the outcome back into the dialog's result phase.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onConfirmConfirmed() {\n\t\t\tconst outcome = await this.runAction(this.confirmAction, this.confirmRow)\n\t\t\tconst dialog = this.$refs.confirmDialog\n\t\t\tif (dialog) {\n\t\t\t\tdialog.setResult(outcome.ok ? { success: true } : { error: outcome.error })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Close/cancel the confirm dialog and clear the pending action.\n\t\t */\n\t\tcloseConfirm() {\n\t\t\tthis.confirmAction = null\n\t\t\tthis.confirmRow = null\n\t\t},\n\n\t\t/**\n\t\t * Dispatch an action. Non-mutating types go through the page's\n\t\t * pre-bound `cnDispatchAction` (falling back to a bare\n\t\t * `dispatchAction`); for `handler` actions the row is appended as the\n\t\t * final handler argument so row context reaches the registry function.\n\t\t * `object-op` actions dispatch via the shared object store — a\n\t\t * rejected (RBAC) write surfaces `opError` and mutates NO local state;\n\t\t * a success clears the error and refreshes the self-fetched rows.\n\t\t *\n\t\t * @param {object} action The manifest action.\n\t\t * @param {object|null} row The target row (null for create).\n\t\t * @return {Promise<{ok: boolean, error?: string}>}\n\t\t */\n\t\tasync runAction(action, row) {\n\t\t\tif (!action) {\n\t\t\t\treturn { ok: false, error: '' }\n\t\t\t}\n\t\t\tif (action.type !== 'object-op') {\n\t\t\t\tconst wrapped = (!action.type || action.type === 'handler')\n\t\t\t\t\t? { ...action, args: [...(action.args || []), row] }\n\t\t\t\t\t: action\n\t\t\t\tif (typeof this.cnDispatchAction === 'function') {\n\t\t\t\t\tthis.cnDispatchAction(wrapped)\n\t\t\t\t} else {\n\t\t\t\t\tdispatchAction(wrapped, { router: this.$router || null })\n\t\t\t\t}\n\t\t\t\treturn { ok: true }\n\t\t\t}\n\n\t\t\tconst store = this.getObjectStore()\n\t\t\tconst result = await dispatchAction(action, {\n\t\t\t\tobjectStore: store,\n\t\t\t\tsource: this.source,\n\t\t\t\trow,\n\t\t\t})\n\t\t\tconst ok = !(result === null || result === false || result === undefined)\n\t\t\tif (!ok) {\n\t\t\t\tconst error = this.readStoreError(store)\n\t\t\t\tthis.opError = error\n\t\t\t\treturn { ok: false, error }\n\t\t\t}\n\t\t\tthis.opError = ''\n\t\t\tthis.refresh()\n\t\t\t/**\n\t\t\t * @event object-op Emitted after a successful declarative mutation\n\t\t\t * so a host that supplies external `rows` can refetch them.\n\t\t\t * @type {{action: object, row: (object|null), result: unknown}}\n\t\t\t */\n\t\t\tthis.$emit('object-op', { action, row, result })\n\t\t\treturn { ok: true }\n\t\t},\n\n\t\t/**\n\t\t * Re-run the active fetch: the endpoint binding (force-refetch past\n\t\t * the shared cache) or the inner CnDataTable's self-fetch. No-op when\n\t\t * the widget is fed external rows.\n\t\t */\n\t\trefresh() {\n\t\t\tif (this.endpointActive) {\n\t\t\t\tthis.epRefetch()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst table = this.$refs.dataTable\n\t\t\tif (this.selfFetchActive && table && typeof table.fetchData === 'function') {\n\t\t\t\ttable.fetchData()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The shared object store instance, or null when no Pinia is active\n\t\t * (`dispatchAction` then warns and no-ops).\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tgetObjectStore() {\n\t\t\ttry {\n\t\t\t\treturn useObjectStore()\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Human-readable message for the last store error on this widget's\n\t\t * source type (RBAC rejection, network failure, …).\n\t\t *\n\t\t * @param {object|null} store The object store used for the dispatch.\n\t\t * @return {string}\n\t\t */\n\t\treadStoreError(store) {\n\t\t\ttry {\n\t\t\t\tif (store && this.source) {\n\t\t\t\t\tconst type = resolveObjectOpType(store, this.source)\n\t\t\t\t\tconst err = store.errors && store.errors[type]\n\t\t\t\t\tif (err && err.message) {\n\t\t\t\t\t\treturn err.message\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// fall through to the generic message\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'The operation was rejected')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-widget-object-table {\n\twidth: 100%;\n}\n\n.cn-widget-object-table__footer {\n\tdisplay: flex;\n\tgap: 4px;\n\tpadding: 4px 8px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-widget-object-table__error {\n\tcolor: var(--color-text-error, var(--color-error-text));\n\tfont-size: 0.85em;\n\tmargin: 4px 8px 0;\n}\n</style>\n"],"names":["CnDataTable","CnWidgetWrapper","CnRowActions","CnIcon","CnConfirmDialog","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnWidgetObjectTable.vue2.js","sources":["../../../../src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue"],"sourcesContent":["<!--\n CnWidgetObjectTable — built-in v2 widget wrapping CnDataTable.\n\n Referenced in v2 manifests via `widgetKey: \"object-table\"`. Renders the\n table on the shared CnWidgetWrapper chrome (title + standard overflow\n Actions menu: Refresh / Documentation / Request a feature) and forwards\n all data props to CnDataTable so the table itself is a transparent\n pass-through.\n\n ADR-049 enrichment: a declarative token-resolved `source` drives\n CnDataTable's existing self-fetch (externally supplied `rows` always win),\n the compact-list surface (hideHeader / borderless / rowRoute /\n viewAllRoute / emptyText / rowIcon / #footer) passes straight through,\n and a declarative `actions[]` renders row-scoped actions via CnRowActions\n plus widget-scoped `object-op` create affordances in the footer.\n\n Spec: REQ-MVR-006 (manifest-v2-renderer) — built-in widget: object-table\n Spec: openspec/changes/list-widget-enrichment/specs/manifest-v2-renderer/spec.md\n-->\n<template>\n\t<!-- `refreshing` mirrors the endpoint fetch so the chrome's Refresh\n\t action spins (and stays disabled) for the real duration of an\n\t in-flight refetch. -->\n\t<component\n\t\t:is=\"hideWrapper ? 'CnWidgetHostShell' : 'CnWidgetWrapper'\"\n\t\tv-bind=\"hideWrapper ? {} : { title, widgetId, documentationUrl, flush: true, refreshing: epLoading }\">\n\t\t<div class=\"cn-widget-object-table\">\n\t\t\t<CnDataTable ref=\"dataTable\" v-bind=\"{ ...innerProps, ...$attrs }\">\n\t\t\t\t<!-- Forward every host-supplied CnDataTable scoped slot verbatim\n\t\t\t\t (#footer { total, shown }, #empty, #column-<key>, …). The\n\t\t\t\t #row-actions slot is NOT forwarded while the widget renders\n\t\t\t\t its own declarative actions[] menu. -->\n\t\t\t\t<template v-for=\"(_, name) in forwardedScopedSlots\" #[name]=\"slotData\">\n\t\t\t\t\t<!-- @slot Any CnDataTable scoped slot, forwarded verbatim — e.g. #footer ({ total, shown }), #empty, #column-<key>. #row-actions is withheld while the declarative actions[] menu renders. -->\n\t\t\t\t\t<slot :name=\"name\" v-bind=\"slotData\" />\n\t\t\t\t</template>\n\t\t\t\t<template v-if=\"rowScopedActions.length > 0\" #row-actions=\"{ row }\">\n\t\t\t\t\t<CnRowActions :actions=\"mappedRowActions\" :row=\"row\" />\n\t\t\t\t</template>\n\t\t\t</CnDataTable>\n\n\t\t\t<!-- Widget-scoped object-op create affordance: `create` has no row\n\t\t\t to mutate, so it renders in the list footer, never inside the\n\t\t\t per-row actions menu (ADR-049 / list-widget-enrichment). -->\n\t\t\t<div v-if=\"createActions.length > 0\" class=\"cn-widget-object-table__footer\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-for=\"action in createActions\"\n\t\t\t\t\t:key=\"action.id || action.label\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t:data-testid=\"`cn-widget-create-${action.id || 'action'}`\"\n\t\t\t\t\t@click=\"onActionTriggered(action, null)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\n\t\t\t<!-- A rejected write (OpenRegister RBAC) surfaces here — local row\n\t\t\t state is never mutated on failure (the store only writes its\n\t\t\t caches on success). -->\n\t\t\t<p v-if=\"opError\" class=\"cn-widget-object-table__error\" data-testid=\"cn-widget-object-table-error\">\n\t\t\t\t{{ opError }}\n\t\t\t</p>\n\n\t\t\t<CnConfirmDialog\n\t\t\t\tv-if=\"confirmAction\"\n\t\t\t\tref=\"confirmDialog\"\n\t\t\t\t:dialogTitle=\"confirmAction.label\"\n\t\t\t\t:message=\"confirmMessage\"\n\t\t\t\t:variant=\"confirmAction.op === 'delete' ? 'error' : 'primary'\"\n\t\t\t\t:confirmLabel=\"confirmAction.label\"\n\t\t\t\t@confirm=\"onConfirmConfirmed\"\n\t\t\t\t@close=\"closeConfirm\" />\n\t\t</div>\n\t</component>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton } from '@nextcloud/vue'\nimport { inject, ref } from 'vue'\nimport CnConfirmDialog from '../../dialogs/CnConfirmDialog.vue'\nimport CnDataTable from '../CnDataTable/CnDataTable.vue'\nimport { useEndpointSource } from '../../composables/useEndpointSource.js'\nimport { useObjectStore } from '../../store/useObjectStore.js'\nimport { dispatchAction, resolveObjectOpType } from '../../utils/actionsDispatcher.js'\nimport { resolveObjectTokenContext } from '../../utils/detailObjectContext.js'\nimport { pageFilter, pageSort } from '../../utils/pageControls.js'\nimport { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\nimport { compareVisibleWhen, readVisibleWhenPath } from '../../utils/visibleWhen.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnRowActions } from '../CnRowActions/index.js'\nimport { CnWidgetWrapper } from '../CnWidgetWrapper/index.js'\n// Chrome-less pass-through used when `hideWrapper` is set (see hostShell.js\n// for why it lives in its own module).\nimport { CnWidgetHostShell } from './hostShell.js'\n\n/**\n * CnWidgetObjectTable — built-in v2 widget wrapping CnDataTable.\n *\n * Renders a data table on the shared CnWidgetWrapper chrome, which supplies\n * the widget title and the standard overflow Actions menu (Refresh /\n * Documentation / Request a feature). All data props are forwarded to the\n * inner CnDataTable; the chrome props (`title`, `documentationUrl`,\n * `widgetId`) are consumed by the wrapper and not passed down.\n *\n * A declarative `source` makes the widget self-fetch from OpenRegister:\n * `source.filter` is token-resolved with the shared `resolveFilterTokens`\n * grammar (`@me`, `@today`, `@workspace.*`, `?`-optional clauses dropped\n * when unresolved via `dropOptionalUnresolved`), `source.order` /\n * `source.limit` drive the fetch, and an `@resolve:` register sentinel is\n * passed through unexpanded (resolution is the host loader's job).\n * Externally supplied `rows` always win — the pre-existing pass-through\n * interface is unchanged.\n *\n * Wave 2 (#91): a declarative `endpointSource` (`{ url, method?, params?,\n * responsePath? }`) is the alternative to the OpenRegister `source` — rows\n * come from an app REST endpoint's payload at `responsePath`, fetched by the\n * shared `useEndpointSource` engine (token-resolved params, request dedup +\n * short-TTL cache, `cn:page:refresh` / `cn:widget:refresh` subscription).\n * Exactly one of `source` | `endpointSource`; columns / formatters /\n * `rowRoute` / `actions` apply unchanged on top of endpoint rows.\n *\n * A declarative `actions[]` array renders `patch` / `delete` `object-op`\n * actions (and reused `handler` / `open-modal` / `open-page` / `navigate`\n * actions) per row through `CnRowActions`; an `object-op` `create` action\n * renders as a widget-scoped footer affordance. `delete` is ALWAYS\n * confirm-gated through `CnConfirmDialog`; `patch` / `create` confirm only\n * on `confirm: true`. Mutations dispatch via the shared object store —\n * the manifest declares intent only, OpenRegister RBAC is the authority,\n * and a rejected write surfaces as an error without local mutation.\n *\n * A declarative `rowClass` (#91) — `[{ when: { field, op?, value }, class }]` reusing\n * the shared `visibleWhen` predicate grammar — is compiled into CnDataTable's\n * `rowClass` function so overdue / at-risk rows can be highlighted from the manifest\n * (a host-supplied `rowClass` FUNCTION still passes straight through).\n */\nexport default {\n\tname: 'CnWidgetObjectTable',\n\n\tcomponents: { CnDataTable, CnWidgetWrapper, CnWidgetHostShell, CnRowActions, CnIcon, NcButton, CnConfirmDialog },\n\n\tinject: {\n\t\t/**\n\t\t * Detail-page object context (`{ objectId, object, register, schema }`)\n\t\t * provided by CnDetailPage — enables `@objectId` / `@object.<field>`\n\t\t * tokens in `source.filter`. Null on dashboards.\n\t\t */\n\t\tcnObjectContext: { default: null },\n\t\t/**\n\t\t * v2 slot-grid detail context holder (`{ value: { objectData, schema,\n\t\t * objectType, objectId, register, store } | null }`) provided by\n\t\t * CnPageRenderer — backfills the object token context so\n\t\t * `@objectId` / `@object.<field>` resolve on detail surfaces where\n\t\t * CnDetailPage is not an ancestor (#91 Wave 3).\n\t\t */\n\t\tcnDetailObjectContext: { default: null },\n\t\t/**\n\t\t * Page-level workspace context (reactive `ref({})`) provided by\n\t\t * CnDashboardPage — enables `@workspace.<key>` tokens in\n\t\t * `source.filter`. Null on pages that don't provide it.\n\t\t */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/**\n\t\t * Pre-bound `dispatchAction` provided by CnPageRenderer (router,\n\t\t * registry, handlers, openModal already wired). Used for the reused\n\t\t * non-mutating action types; falls back to a direct `dispatchAction`\n\t\t * call when mounted outside a CnPageRenderer tree.\n\t\t */\n\t\tcnDispatchAction: { default: null },\n\t},\n\n\tinheritAttrs: false,\n\n\tprops: {\n\t\t/**\n\t\t * Widget title shown in the CnWidgetWrapper header.\n\t\t */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Table'),\n\t\t},\n\n\t\t/**\n\t\t * Documentation link surfaced in the widget's overflow Actions menu.\n\t\t * Empty (the default) hides the Documentation item; the Refresh and\n\t\t * Request-a-feature items always render.\n\t\t */\n\t\tdocumentationUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Stable id forwarded to the widget chrome for the Refresh /\n\t\t * Request-a-feature payloads.\n\t\t */\n\t\twidgetId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Render content-only, WITHOUT the widget's own CnWidgetWrapper\n\t\t * chrome. Set by hosts that already provide the card chrome — e.g.\n\t\t * CnDashboardPage's dashboard widget grid, whose registry branch\n\t\t * wraps every catalog widget in its own CnWidgetWrapper (a naive\n\t\t * mount would double-card). Default `false` keeps the pre-existing\n\t\t * self-chromed v2 rendering byte-for-byte.\n\t\t */\n\t\thideWrapper: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Register slug. Forwarded to CnDataTable. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Schema slug. Forwarded to CnDataTable. */\n\t\tschema: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Column definitions. Forwarded to CnDataTable — bare string keys OR\n\t\t * the object form (`{ key, label, sortable, width, cellClass,\n\t\t * formatter, widget, format, aggregate }`) both pass straight through.\n\t\t *\n\t\t * @type {Array<object|string>}\n\t\t */\n\t\tcolumns: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Rows array. Forwarded to CnDataTable. External rows always win over `source`. */\n\t\trows: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Loading state. Forwarded to CnDataTable. */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Declarative self-fetch source (ADR-049). When set and no external\n\t\t * `rows` are supplied, the widget resolves `filter` @-tokens and\n\t\t * drives CnDataTable's existing self-fetch with the resolved filter,\n\t\t * `order`, and `limit`. `register` MAY carry an `@resolve:` sentinel —\n\t\t * it is passed through unexpanded. `extend` (#91 Wave 3) forwards\n\t\t * OpenRegister `_extend[]` values (e.g. `[\"calculations\"]`) on the\n\t\t * fetch so virtual/declarative calc fields ride along and render as\n\t\t * ordinary columns. Default `null` keeps the pre-existing\n\t\t * pass-through behaviour byte-for-byte.\n\t\t *\n\t\t * @type {{register?: string, schema?: string, filter?: object, order?: object, limit?: number, extend?: string[]}|null}\n\t\t */\n\t\tsource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Endpoint data binding (Wave 2, #91) — the alternative to the\n\t\t * OpenRegister `source` for rows an app REST endpoint computes (e.g. a\n\t\t * per-source performance report). Rows come from the payload at\n\t\t * `responsePath` (a dot-path; the plucked value must be an array —\n\t\t * anything else renders as empty). `params` values use the shared\n\t\t * filter-token grammar (`@workspace.dateFrom?`, `@me`, `@today±Nd`, …)\n\t\t * and re-resolve + refetch when the page context changes. Requests are\n\t\t * deduped + short-TTL cached per (url+params) by the shared\n\t\t * `useEndpointSource` engine, which also subscribes to\n\t\t * `cn:page:refresh` / `cn:widget:refresh`. Exactly one of `source` |\n\t\t * `endpointSource` (validator-enforced); external `rows` still win\n\t\t * over both. `columns` / formatters / `rowRoute` / `actions` apply\n\t\t * unchanged on top of endpoint rows.\n\t\t *\n\t\t * @type {{url: string, method?: string, params?: object, responsePath?: string}|null}\n\t\t */\n\t\tendpointSource: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Declarative actions (unified manifest action shape — `handler` |\n\t\t * `open-modal` | `open-page` | `navigate` | `object-op`). `object-op`\n\t\t * `patch` / `delete` (and every non-mutating type) render per row via\n\t\t * CnRowActions; `object-op` `create` renders as a widget-scoped footer\n\t\t * affordance. `delete` always confirms; `patch` / `create` confirm on\n\t\t * `confirm: true`. Authorization-shaped fields on an action have no\n\t\t * effect — OpenRegister RBAC is the only authority.\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 * Hide the column-header row. Forwarded to CnDataTable — the compact\n\t\t * dashboard-list surface.\n\t\t */\n\t\thideHeader: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Drop CnDataTable's card chrome (border, radius, shadow) so the table\n\t\t * sits flush inside the widget card. Forwarded to CnDataTable.\n\t\t */\n\t\tborderless: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Fit the table to the tile: the title column (a column's `grow: true`,\n\t\t * else `title` or `name`, else the first) shrinks and ends in an\n\t\t * ellipsis so the trailing columns stay visible. On by default; pass\n\t\t * `false` for the table's own auto layout. Forwarded to CnDataTable.\n\t\t *\n\t\t * @spec openspec/changes/narrow-object-table-widgets/specs/narrow-object-table-widgets/spec.md#requirement-a-narrow-object-table-keeps-its-trailing-columns-in-view\n\t\t */\n\t\tfitWidth: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Route NAME for row-click navigation. Mapped onto CnDataTable's\n\t\t * `rowClickRoute` as `{ name: rowRoute, params: { id } }` with the\n\t\t * row's `id` (or `@self.id`). Empty (the default) = no navigation.\n\t\t */\n\t\trowRoute: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Client-side row cap, forwarded to CnDataTable (`0` = all rows).\n\t\t * This is what makes `viewAllRoute` reachable for `endpointSource`\n\t\t * tables: the endpoint's full payload stays the `totalRowCount`\n\t\t * while CnDataTable renders only the first `limit` rows, so the\n\t\t * \"View all\" footer condition (total > shown) can finally hold.\n\t\t * The OpenRegister `source` mode keeps using `source.limit` (an\n\t\t * explicit assignment that wins over this passthrough).\n\t\t */\n\t\tlimit: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0,\n\t\t},\n\n\t\t/**\n\t\t * vue-router route object for the \"View all\" footer link. Forwarded\n\t\t * to CnDataTable (footer shows when the rows are a `limit`-ed subset).\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tviewAllRoute: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Pre-translated \"View all\" footer label. Forwarded to CnDataTable. */\n\t\tviewAllLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: undefined,\n\t\t},\n\n\t\t/** Text shown when there are no rows. Forwarded to CnDataTable. */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: undefined,\n\t\t},\n\n\t\t/**\n\t\t * App wording for an unavailable personal lens (a `source.filter._recent`\n\t\t * tile on an instance that does not log reads), keyed `<lens>.<reason>`\n\t\t * or `<reason>`. Forwarded to CnDataTable, which shows the library's\n\t\t * object-neutral text without it.\n\t\t *\n\t\t * @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-an-unavailable-lens-explains-its-empty-page\n\t\t */\n\t\tlensReasonTexts: {\n\t\t\ttype: Object,\n\t\t\tdefault: undefined,\n\t\t},\n\n\t\t/**\n\t\t * Leading per-row icon: a static MDI icon name string, or a function\n\t\t * `(row) => iconName`. Forwarded to CnDataTable.\n\t\t *\n\t\t * @type {string|((row: object) => string)|null}\n\t\t */\n\t\trowIcon: {\n\t\t\ttype: [String, Function],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Per-row CSS class binding (#91). Two forms:\n\t\t * - a FUNCTION `(row) => string` — passed straight through to\n\t\t * CnDataTable's `rowClass` (the pre-existing host-supplied contract).\n\t\t * - an ARRAY of declarative rules `[{ when: { field, op?, value }, class }]`\n\t\t * compiled here into that function. Each rule adds its `class` to a\n\t\t * row when the shared `visibleWhen` predicate holds against the row:\n\t\t * `field` is a dot-path into the row, `op` is `eq|neq|gt|gte|lt|lte`\n\t\t * (default `eq`), `value` is the literal right-hand side. Rules are\n\t\t * evaluated in order and every matching `class` is space-joined, so an\n\t\t * overdue / at-risk row can be highlighted declaratively from a\n\t\t * manifest with no bespoke function. Default `null` = no row class.\n\t\t *\n\t\t * @type {((row: object) => string)|Array<{when: {field: string, op?: string, value: unknown}, class: string}>|null}\n\t\t */\n\t\trowClass: {\n\t\t\ttype: [Function, Array],\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['object-op'],\n\n\tsetup(props) {\n\t\t// Endpoint binding (Wave 2, #91): the shared useEndpointSource engine\n\t\t// owns token resolution, request dedup + TTL caching, and the\n\t\t// cn:page:refresh / cn:widget:refresh subscriptions (the widget's own\n\t\t// `widgetId` is matched against widget-scoped refresh payloads).\n\t\t// No-op while `endpointSource` is null, so the OpenRegister `source`\n\t\t// self-fetch path is untouched. Injects re-read in setup (same\n\t\t// resolution as the Options `inject` block).\n\t\tconst objectCtxRaw = inject('cnObjectContext', null)\n\t\tconst detailCtxRaw = inject('cnDetailObjectContext', null)\n\t\tconst workspaceRaw = inject('cnWorkspaceContext', ref(null))\n\t\tconst appConfigRaw = inject('cnAppConfig', ref({}))\n\t\tconst unwrap = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\t\t// External rows always win — and suppress the request entirely (the\n\t\t// pre-existing \"when rows are provided, no API calls are made\"\n\t\t// contract), so the getter resolves to null while `rows` are supplied.\n\t\tconst { data, loading, error, refetch } = useEndpointSource(\n\t\t\t() => ((props.rows && props.rows.length > 0) ? null : props.endpointSource),\n\t\t\t{\n\t\t\t\tctx: () => ({\n\t\t\t\t\t...(resolveObjectTokenContext(objectCtxRaw, detailCtxRaw) || {}),\n\t\t\t\t\tworkspace: unwrap(workspaceRaw) || {},\n\t\t\t\t\tconfig: unwrap(appConfigRaw) || {},\n\t\t\t\t}),\n\t\t\t\twidgetId: () => props.widgetId,\n\t\t\t},\n\t\t)\n\t\treturn { epData: data, epLoading: loading, epError: error, epRefetch: refetch }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Error message from the last rejected object-op write ('' = none). */\n\t\t\topError: '',\n\t\t\t/** @type {object|null} The object-op action awaiting confirmation (null = no dialog). */\n\t\t\tconfirmAction: null,\n\t\t\t/** @type {object|null} The row the pending confirm-gated action targets. */\n\t\t\tconfirmRow: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The merged detail-page object context for token resolution — both\n\t\t * detail-surface injects, holder fields backfilling (#91 Wave 3) —\n\t\t * or null on surfaces (dashboards) that don't provide one.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tobjectCtx() {\n\t\t\treturn resolveObjectTokenContext(this.cnObjectContext, this.cnDetailObjectContext)\n\t\t},\n\n\t\t/**\n\t\t * The unwrapped workspace context bag (or null). Vue 2.7 inject may\n\t\t * hand back a raw ref; unwrap `.value` for token resolution.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tworkspaceCtx() {\n\t\t\tconst c = this.cnWorkspaceContext\n\t\t\tif (!c) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn (typeof c === 'object' && 'value' in c) ? c.value : c\n\t\t},\n\n\t\t/**\n\t\t * Token-resolution context merged from the detail-page object context\n\t\t * and the page-level workspace bag (`@workspace.<key>`).\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\ttokenCtx() {\n\t\t\tconst base = this.objectCtx ? { ...this.objectCtx } : {}\n\t\t\tbase.workspace = this.workspaceCtx || {}\n\t\t\treturn base\n\t\t},\n\n\t\t/**\n\t\t * `source.filter` with every @-token resolved against `tokenCtx`, then\n\t\t * with any UNRESOLVED OPTIONAL clause (`@workspace.<key>?`) dropped —\n\t\t * an optional clause simply shows all rows until its context is set.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tresolvedFilter() {\n\t\t\tconst filter = pageFilter(this.source && this.source.pageControls, this.workspaceCtx, this.source && this.source.filter)\n\t\t\treturn dropOptionalUnresolved(resolveFilterTokens(filter, this.tokenCtx))\n\t\t},\n\n\t\t/**\n\t\t * Whether a REQUIRED context token in `source.filter` is still\n\t\t * unresolved — the widget then skips the fetch instead of querying the\n\t\t * whole register.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\twaitingForContext() {\n\t\t\treturn !!this.source && hasUnresolvedTokens(this.resolvedFilter)\n\t\t},\n\n\t\t/**\n\t\t * Whether the Wave-2 `endpointSource` drives the rows: a usable\n\t\t * endpoint config and no external rows (external rows always win).\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tendpointActive() {\n\t\t\treturn !!(this.endpointSource && this.endpointSource.url)\n\t\t\t\t&& (!this.rows || this.rows.length === 0)\n\t\t},\n\n\t\t/**\n\t\t * Rows resolved from the endpoint payload. The payload (after the\n\t\t * `responsePath` pluck the shared engine applies) must be an ARRAY;\n\t\t * anything else renders as an empty table rather than crashing the\n\t\t * column renderer.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tendpointRows() {\n\t\t\treturn Array.isArray(this.epData) ? this.epData : []\n\t\t},\n\n\t\t/**\n\t\t * Whether the declarative `source` drives a self-fetch: a usable\n\t\t * source, no external rows (external rows always win), no active\n\t\t * endpoint binding (exactly-one-of — endpointSource wins when both\n\t\t * slip past the validator), and no unresolved required token.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tselfFetchActive() {\n\t\t\treturn !!(this.source && this.source.register && this.source.schema)\n\t\t\t\t&& (!this.rows || this.rows.length === 0)\n\t\t\t\t&& !this.endpointActive\n\t\t\t\t&& !this.waitingForContext\n\t\t},\n\n\t\t/**\n\t\t * Query params handed to CnDataTable's self-fetch: the resolved filter\n\t\t * as direct field params (OpenRegister object-search shape),\n\t\t * `_order[field]` entries from `source.order`, and — when\n\t\t * `source.limit` is set — `_limit` of limit + 1 so CnDataTable's\n\t\t * View-all footer can detect that more rows exist while the display\n\t\t * caps at `limit`.\n\t\t *\n\t\t * IN-lists: an ARRAY filter value (either directly on the field or via\n\t\t * the `{ in: [...] }` operator form) is emitted as the bare field with\n\t\t * the array value, which axios serializes as repeated bracket params\n\t\t * (`field[]=a&field[]=b`) — the same shape `buildQueryString` /\n\t\t * `useObjectStore` send and the only IN form OpenRegister parses (a\n\t\t * `field[in]`-style key is NOT understood server-side). Empty arrays\n\t\t * are skipped (no constraint).\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tresolvedFetchParams() {\n\t\t\tconst src = this.source || {}\n\t\t\tconst params = {}\n\t\t\t// A page sort control (`source.pageControls.sort`) replaces the order.\n\t\t\tconst chosen = pageSort(src.pageControls, this.workspaceCtx, null)\n\t\t\tconst order = chosen ? { [chosen.field]: chosen.dir } : (src.order || {})\n\t\t\tfor (const [field, dir] of Object.entries(order)) {\n\t\t\t\tparams[`_order[${field}]`] = dir === 'desc' ? 'desc' : 'asc'\n\t\t\t}\n\t\t\tif (typeof src.limit === 'number' && src.limit > 0) {\n\t\t\t\tparams._limit = src.limit + 1\n\t\t\t}\n\t\t\t// `source.extend` (#91 Wave 3): forwarded as OR's repeated\n\t\t\t// `_extend[]` param so virtual/declarative fields (e.g.\n\t\t\t// `calculations` — procest daysOverdue) ride the fetch and are\n\t\t\t// displayable as ordinary columns. Serialized by the shared\n\t\t\t// query builder (array value → repeated `key[]`).\n\t\t\tif (Array.isArray(src.extend) && src.extend.length > 0) {\n\t\t\t\tparams._extend = src.extend.filter((e) => typeof e === 'string' && e !== '')\n\t\t\t}\n\t\t\tconst filter = this.resolvedFilter\n\t\t\tif (filter && typeof filter === 'object') {\n\t\t\t\tfor (const [k, v] of Object.entries(filter)) {\n\t\t\t\t\tif (Array.isArray(v)) {\n\t\t\t\t\t\tif (v.length > 0) {\n\t\t\t\t\t\t\tparams[k] = v\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (v && typeof v === 'object') {\n\t\t\t\t\t\tfor (const [op, ov] of Object.entries(v)) {\n\t\t\t\t\t\t\tif (op === 'in' && Array.isArray(ov)) {\n\t\t\t\t\t\t\t\t// OR's IN form is the bare repeated field param.\n\t\t\t\t\t\t\t\tif (ov.length > 0) {\n\t\t\t\t\t\t\t\t\tparams[k] = ov\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tparams[`${k}[${op}]`] = ov\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (v !== '' && v !== null && v !== undefined) {\n\t\t\t\t\t\tparams[k] = v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn params\n\t\t},\n\n\t\t/**\n\t\t * `$props` minus the chrome props (`title`, `documentationUrl`,\n\t\t * `widgetId`, `hideWrapper`) and the widget-only props (`source`,\n\t\t * `endpointSource`, `actions`, `rowRoute`), so they are consumed here\n\t\t * and never forwarded to the inner CnDataTable. `undefined` values are\n\t\t * dropped so CnDataTable's own prop defaults apply. When the\n\t\t * declarative `source` is active it supplies `register` / `schemaId` /\n\t\t * `fetchParams` / `limit`; an `@resolve:` register sentinel passes\n\t\t * through unexpanded. When the Wave-2 `endpointSource` is active it\n\t\t * supplies the resolved `rows` + `loading` instead — columns,\n\t\t * formatters, and row navigation apply unchanged on top.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tinnerProps() {\n\t\t\tconst { title, documentationUrl, widgetId, hideWrapper, source, endpointSource, actions, rowRoute, rowClass, schema, ...rest } = this.$props\n\t\t\tconst inner = {}\n\t\t\tfor (const [k, v] of Object.entries(rest)) {\n\t\t\t\tif (v !== undefined) {\n\t\t\t\t\tinner[k] = v\n\t\t\t\t}\n\t\t\t}\n\t\t\t// This widget's `schema` is a slug, while CnDataTable's `schema` is a\n\t\t\t// schema object, so it is not forwarded. Nor is it mapped to `schemaId`:\n\t\t\t// that would turn on CnDataTable's unscoped self-fetch whenever the\n\t\t\t// widget's own rows are empty. Self-fetch comes from `source` alone.\n\t\t\t// `rowClass` is consumed here (function or declarative rules[]) and\n\t\t\t// forwarded to CnDataTable as a compiled `(row) => class` function.\n\t\t\tif (this.compiledRowClass) {\n\t\t\t\tinner.rowClass = this.compiledRowClass\n\t\t\t}\n\t\t\tif (this.selfFetchActive) {\n\t\t\t\tinner.register = this.source.register\n\t\t\t\tinner.schemaId = this.source.schema\n\t\t\t\tinner.fetchParams = this.resolvedFetchParams\n\t\t\t\tif (typeof this.source.limit === 'number' && this.source.limit > 0) {\n\t\t\t\t\tinner.limit = this.source.limit\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.endpointActive) {\n\t\t\t\tinner.rows = this.endpointRows\n\t\t\t\tinner.loading = this.epLoading || this.loading\n\t\t\t}\n\t\t\tif (this.rowRoute) {\n\t\t\t\tinner.rowClickRoute = this.rowRouteFn\n\t\t\t}\n\t\t\treturn inner\n\t\t},\n\n\t\t/**\n\t\t * The effective `rowClass` function forwarded to CnDataTable. A\n\t\t * host-supplied FUNCTION passes straight through; a declarative\n\t\t * rules[] array is compiled into a `(row) => string` that space-joins\n\t\t * every rule whose shared `visibleWhen` predicate holds against the\n\t\t * row (`field` dot-path + `op` + `value`). Null when `rowClass` is\n\t\t * unset or an empty array — the widget then forwards nothing so\n\t\t * CnDataTable's own default applies.\n\t\t *\n\t\t * @return {?(row: object) => string} The row-class function, or null.\n\t\t */\n\t\tcompiledRowClass() {\n\t\t\tconst rc = this.rowClass\n\t\t\tif (typeof rc === 'function') {\n\t\t\t\treturn rc\n\t\t\t}\n\t\t\tif (!Array.isArray(rc) || rc.length === 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst rules = rc\n\t\t\treturn (row) => {\n\t\t\t\tconst classes = []\n\t\t\t\tfor (const rule of rules) {\n\t\t\t\t\tif (!rule || !rule.class) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tconst when = rule.when || {}\n\t\t\t\t\tconst actual = readVisibleWhenPath(row, when.field)\n\t\t\t\t\tif (compareVisibleWhen(actual, when.op || 'eq', when.value)) {\n\t\t\t\t\t\tclasses.push(rule.class)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn classes.join(' ')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Row-click navigation function derived from the `rowRoute` route\n\t\t * name (CnDataTable's `rowClickRoute` shape).\n\t\t *\n\t\t * @return {(row: object) => (object|null)} A vue-router location, or null for a row with no id.\n\t\t */\n\t\trowRouteFn() {\n\t\t\treturn (row) => {\n\t\t\t\tconst id = row && (row.id || (row['@self'] && row['@self'].id))\n\t\t\t\treturn id ? { name: this.rowRoute, params: { id: String(id) } } : null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Actions rendered per row: every non-`object-op` type plus\n\t\t * `object-op` `patch` / `delete` (they mutate THAT row's object).\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\trowScopedActions() {\n\t\t\treturn (this.actions || []).filter((a) => a && (a.type !== 'object-op' || a.op === 'patch' || a.op === 'delete'))\n\t\t},\n\n\t\t/**\n\t\t * `object-op` `create` actions — widget-scoped (no row to mutate), so\n\t\t * they render in the list footer, never in the row menu.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tcreateActions() {\n\t\t\treturn (this.actions || []).filter((a) => a && a.type === 'object-op' && a.op === 'create')\n\t\t},\n\n\t\t/**\n\t\t * Row-scoped actions mapped onto the CnRowActions shape: label + icon\n\t\t * pass through, `object-op` `delete` renders destructive, and the\n\t\t * handler routes back through the widget's confirm-gated dispatcher.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tmappedRowActions() {\n\t\t\treturn this.rowScopedActions.map((action) => ({\n\t\t\t\tlabel: action.label,\n\t\t\t\ticon: action.icon,\n\t\t\t\tdestructive: action.type === 'object-op' && action.op === 'delete',\n\t\t\t\thandler: (row) => this.onActionTriggered(action, row),\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Host-supplied CnDataTable scoped slots to forward (keys only). The\n\t\t * `row-actions` slot is withheld while the widget renders its own\n\t\t * declarative actions menu.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tforwardedScopedSlots() {\n\t\t\tconst out = {}\n\t\t\tfor (const name of Object.keys(this.$slots || {})) {\n\t\t\t\tif (name === 'row-actions' && this.rowScopedActions.length > 0) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tout[name] = true\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\n\t\t/**\n\t\t * Confirmation question for the pending confirm-gated action —\n\t\t * destructive copy for `delete`, a generic confirm otherwise.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tconfirmMessage() {\n\t\t\tif (this.confirmAction && this.confirmAction.op === 'delete') {\n\t\t\t\treturn t('nextcloud-vue', 'Are you sure you want to delete this item? This action cannot be undone.')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Are you sure you want to continue?')\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Entry point for every declarative action trigger (row menu or\n\t\t * footer create button). `object-op` `delete` ALWAYS confirms;\n\t\t * `patch` / `create` confirm only when the action sets\n\t\t * `confirm: true`; everything else dispatches immediately.\n\t\t *\n\t\t * @param {object} action The manifest action.\n\t\t * @param {object|null} row The row for row-scoped actions (null for create).\n\t\t * @return {Promise<void>|void}\n\t\t */\n\t\tonActionTriggered(action, row) {\n\t\t\tif (action && action.type === 'object-op' && (action.op === 'delete' || action.confirm === true)) {\n\t\t\t\tthis.confirmAction = action\n\t\t\t\tthis.confirmRow = row\n\t\t\t\treturn\n\t\t\t}\n\t\t\treturn this.runAction(action, row)\n\t\t},\n\n\t\t/**\n\t\t * Confirm-dialog primary button: run the pending action and report\n\t\t * the outcome back into the dialog's result phase.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onConfirmConfirmed() {\n\t\t\tconst outcome = await this.runAction(this.confirmAction, this.confirmRow)\n\t\t\tconst dialog = this.$refs.confirmDialog\n\t\t\tif (dialog) {\n\t\t\t\tdialog.setResult(outcome.ok ? { success: true } : { error: outcome.error })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Close/cancel the confirm dialog and clear the pending action.\n\t\t */\n\t\tcloseConfirm() {\n\t\t\tthis.confirmAction = null\n\t\t\tthis.confirmRow = null\n\t\t},\n\n\t\t/**\n\t\t * Dispatch an action. Non-mutating types go through the page's\n\t\t * pre-bound `cnDispatchAction` (falling back to a bare\n\t\t * `dispatchAction`); for `handler` actions the row is appended as the\n\t\t * final handler argument so row context reaches the registry function.\n\t\t * `object-op` actions dispatch via the shared object store — a\n\t\t * rejected (RBAC) write surfaces `opError` and mutates NO local state;\n\t\t * a success clears the error and refreshes the self-fetched rows.\n\t\t *\n\t\t * @param {object} action The manifest action.\n\t\t * @param {object|null} row The target row (null for create).\n\t\t * @return {Promise<{ok: boolean, error?: string}>}\n\t\t */\n\t\tasync runAction(action, row) {\n\t\t\tif (!action) {\n\t\t\t\treturn { ok: false, error: '' }\n\t\t\t}\n\t\t\tif (action.type !== 'object-op') {\n\t\t\t\tconst wrapped = (!action.type || action.type === 'handler')\n\t\t\t\t\t? { ...action, args: [...(action.args || []), row] }\n\t\t\t\t\t: action\n\t\t\t\tif (typeof this.cnDispatchAction === 'function') {\n\t\t\t\t\tthis.cnDispatchAction(wrapped)\n\t\t\t\t} else {\n\t\t\t\t\tdispatchAction(wrapped, { router: this.$router || null })\n\t\t\t\t}\n\t\t\t\treturn { ok: true }\n\t\t\t}\n\n\t\t\tconst store = this.getObjectStore()\n\t\t\tconst result = await dispatchAction(action, {\n\t\t\t\tobjectStore: store,\n\t\t\t\tsource: this.source,\n\t\t\t\trow,\n\t\t\t})\n\t\t\tconst ok = !(result === null || result === false || result === undefined)\n\t\t\tif (!ok) {\n\t\t\t\tconst error = this.readStoreError(store)\n\t\t\t\tthis.opError = error\n\t\t\t\treturn { ok: false, error }\n\t\t\t}\n\t\t\tthis.opError = ''\n\t\t\tthis.refresh()\n\t\t\t/**\n\t\t\t * @event object-op Emitted after a successful declarative mutation\n\t\t\t * so a host that supplies external `rows` can refetch them.\n\t\t\t * @type {{action: object, row: (object|null), result: unknown}}\n\t\t\t */\n\t\t\tthis.$emit('object-op', { action, row, result })\n\t\t\treturn { ok: true }\n\t\t},\n\n\t\t/**\n\t\t * Re-run the active fetch: the endpoint binding (force-refetch past\n\t\t * the shared cache) or the inner CnDataTable's self-fetch. No-op when\n\t\t * the widget is fed external rows.\n\t\t */\n\t\trefresh() {\n\t\t\tif (this.endpointActive) {\n\t\t\t\tthis.epRefetch()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst table = this.$refs.dataTable\n\t\t\tif (this.selfFetchActive && table && typeof table.fetchData === 'function') {\n\t\t\t\ttable.fetchData()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The shared object store instance, or null when no Pinia is active\n\t\t * (`dispatchAction` then warns and no-ops).\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tgetObjectStore() {\n\t\t\ttry {\n\t\t\t\treturn useObjectStore()\n\t\t\t} catch {\n\t\t\t\treturn null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Human-readable message for the last store error on this widget's\n\t\t * source type (RBAC rejection, network failure, …).\n\t\t *\n\t\t * @param {object|null} store The object store used for the dispatch.\n\t\t * @return {string}\n\t\t */\n\t\treadStoreError(store) {\n\t\t\ttry {\n\t\t\t\tif (store && this.source) {\n\t\t\t\t\tconst type = resolveObjectOpType(store, this.source)\n\t\t\t\t\tconst err = store.errors && store.errors[type]\n\t\t\t\t\tif (err && err.message) {\n\t\t\t\t\t\treturn err.message\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// fall through to the generic message\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'The operation was rejected')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-widget-object-table {\n\twidth: 100%;\n}\n\n.cn-widget-object-table__footer {\n\tdisplay: flex;\n\tgap: 4px;\n\tpadding: 4px 8px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-widget-object-table__error {\n\tcolor: var(--color-text-error, var(--color-error-text));\n\tfont-size: 0.85em;\n\tmargin: 4px 8px 0;\n}\n</style>\n"],"names":["CnDataTable","CnWidgetWrapper","CnRowActions","CnIcon","CnConfirmDialog","t"],"mappings":"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"}