@conduction/nextcloud-vue 2.108.0 → 2.110.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +115 -12
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +36 -9
  5. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
  6. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue.js +10 -0
  7. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue.js.map +1 -0
  8. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue2.js +305 -0
  9. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue2.js.map +1 -0
  10. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue3.js +38 -0
  11. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue3.js.map +1 -0
  12. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js +10 -0
  13. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js.map +1 -0
  14. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +398 -0
  15. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -0
  16. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js +155 -0
  17. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -0
  18. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue.js +9 -0
  19. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue.js.map +1 -0
  20. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue2.js +281 -0
  21. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue2.js.map +1 -0
  22. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue3.js +137 -0
  23. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue3.js.map +1 -0
  24. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue.js +9 -0
  25. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue.js.map +1 -0
  26. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue2.js +225 -0
  27. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue2.js.map +1 -0
  28. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue3.js +110 -0
  29. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue3.js.map +1 -0
  30. package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
  31. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +19 -3
  32. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  33. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  34. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +30 -0
  35. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  36. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +513 -481
  37. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  38. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js +9 -0
  39. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js.map +1 -0
  40. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js +181 -0
  41. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js.map +1 -0
  42. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js +50 -0
  43. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js.map +1 -0
  44. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
  45. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +102 -1
  46. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  47. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -6
  48. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  49. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue.js +9 -0
  50. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue.js.map +1 -0
  51. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue2.js +190 -0
  52. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue2.js.map +1 -0
  53. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue3.js +91 -0
  54. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue3.js.map +1 -0
  55. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js +9 -0
  56. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js.map +1 -0
  57. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js +64 -0
  58. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js.map +1 -0
  59. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js +49 -0
  60. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js.map +1 -0
  61. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue.js +9 -0
  62. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue.js.map +1 -0
  63. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue2.js +382 -0
  64. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue2.js.map +1 -0
  65. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue3.js +285 -0
  66. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue3.js.map +1 -0
  67. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +9 -0
  68. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js.map +1 -0
  69. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js +277 -0
  70. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -0
  71. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +183 -0
  72. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -0
  73. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +16 -14
  74. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  75. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +53 -22
  76. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  77. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue.js +9 -0
  78. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue.js.map +1 -0
  79. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue2.js +253 -0
  80. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue2.js.map +1 -0
  81. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue3.js +197 -0
  82. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue3.js.map +1 -0
  83. package/dist/esm/index.js +12 -0
  84. package/dist/esm/index.js.map +1 -1
  85. package/dist/esm/l10n/en.json.js +106 -1
  86. package/dist/esm/l10n/en.json.js.map +1 -1
  87. package/dist/esm/l10n/nl.json.js +105 -1
  88. package/dist/esm/l10n/nl.json.js.map +1 -1
  89. package/dist/esm/library-style.js +1 -1
  90. package/dist/esm/nextcloud-vue.css +510 -76
  91. package/dist/esm/utils/actionsDispatcher.js +11 -3
  92. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  93. package/dist/esm/utils/builtInModals.js +3 -1
  94. package/dist/esm/utils/builtInModals.js.map +1 -1
  95. package/dist/esm/utils/duplicateWarning.js +62 -0
  96. package/dist/esm/utils/duplicateWarning.js.map +1 -0
  97. package/dist/esm/utils/errors.js +4 -0
  98. package/dist/esm/utils/errors.js.map +1 -1
  99. package/dist/esm/utils/libraryWidgetKeys.js +4 -0
  100. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  101. package/dist/esm/utils/openDuplicateWarning.js +39 -0
  102. package/dist/esm/utils/openDuplicateWarning.js.map +1 -0
  103. package/dist/esm/utils/optionsFrom.js +69 -2
  104. package/dist/esm/utils/optionsFrom.js.map +1 -1
  105. package/dist/esm/utils/planTree.js +57 -0
  106. package/dist/esm/utils/planTree.js.map +1 -0
  107. package/dist/esm/utils/redactionSegments.js +53 -0
  108. package/dist/esm/utils/redactionSegments.js.map +1 -0
  109. package/dist/esm/utils/registerNcDashboardWidgets.js +61 -0
  110. package/dist/esm/utils/registerNcDashboardWidgets.js.map +1 -0
  111. package/dist/esm/utils/searchRefusal.js +62 -0
  112. package/dist/esm/utils/searchRefusal.js.map +1 -0
  113. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  114. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  115. package/dist/nextcloud-vue.cjs.js +17560 -12844
  116. package/dist/nextcloud-vue.cjs.js.map +2 -2
  117. package/dist/nextcloud-vue.css +510 -76
  118. package/l10n/en.json +106 -1
  119. package/l10n/nl.json +105 -1
  120. package/package.json +1 -1
  121. package/src/components/CnActionDialog/CnActionDialog.vue +134 -13
  122. package/src/components/CnAddressSearch/CnAddressSearch.vue +343 -0
  123. package/src/components/CnAddressSearch/index.js +12 -0
  124. package/src/components/CnAssignDialog/CnAssignDialog.vue +525 -0
  125. package/src/components/CnAssignDialog/index.js +12 -0
  126. package/src/components/CnDecisionListWidget/CnDecisionListWidget.vue +388 -0
  127. package/src/components/CnDecisionListWidget/index.js +12 -0
  128. package/src/components/CnDuplicateWarning/CnDuplicateWarning.vue +318 -0
  129. package/src/components/CnDuplicateWarning/index.js +12 -0
  130. package/src/components/CnFormDialog/CnFormDialog.vue +19 -3
  131. package/src/components/CnIndexPage/CnIndexPage.vue +52 -0
  132. package/src/components/CnNcDashboardList/CnNcDashboardList.vue +218 -0
  133. package/src/components/CnNcDashboardList/index.js +12 -0
  134. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +107 -6
  135. package/src/components/CnPlanWidget/CnPlanWidget.vue +246 -0
  136. package/src/components/CnPlanWidget/CnPlanWidgetRow.vue +117 -0
  137. package/src/components/CnPlanWidget/index.js +12 -0
  138. package/src/components/CnRedactionEditor/CnRedactionEditor.vue +655 -0
  139. package/src/components/CnRedactionEditor/index.js +13 -0
  140. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +413 -0
  141. package/src/components/CnRetentionWidget/index.js +12 -0
  142. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  143. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +31 -0
  144. package/src/components/CnWorkQueue/CnWorkQueue.vue +463 -0
  145. package/src/components/CnWorkQueue/index.js +12 -0
  146. package/src/components/index.js +9 -0
  147. package/src/css/index-page.css +24 -0
  148. package/src/index.js +12 -0
  149. package/src/schemas/app-manifest-v2.schema.json +119 -3
  150. package/src/utils/actionsDispatcher.js +10 -2
  151. package/src/utils/builtInModals.js +3 -1
  152. package/src/utils/duplicateWarning.js +59 -0
  153. package/src/utils/errors.js +4 -0
  154. package/src/utils/libraryWidgetKeys.js +4 -0
  155. package/src/utils/openDuplicateWarning.js +35 -0
  156. package/src/utils/optionsFrom.js +68 -1
  157. package/src/utils/planTree.js +54 -0
  158. package/src/utils/redactionSegments.js +50 -0
  159. package/src/utils/registerNcDashboardWidgets.js +57 -0
  160. package/src/utils/searchRefusal.js +59 -0
  161. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnAssignDialog.vue3.js","sources":["../../../../src/components/CnAssignDialog/CnAssignDialog.vue?vue&type=template&id=9921b726&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<CnDialog\n\t\t:name=\"text(config.title) || t('nextcloud-vue', 'Assign')\"\n\t\t:eyebrow=\"fill(config.eyebrow)\"\n\t\t:subtitle=\"text(config.subtitle)\"\n\t\twidth=\"form\"\n\t\t:noClose=\"busy\"\n\t\tdata-testid=\"cn-assign-dialog\"\n\t\t@closing=\"close\">\n\t\t<div v-if=\"!done\" class=\"cn-assign-dialog\">\n\t\t\t<div class=\"cn-assign-dialog__field\">\n\t\t\t\t<CnSearchSelect\n\t\t\t\t\t:modelValue=\"picked ? picked.id : null\"\n\t\t\t\t\t:inputId=\"assigneeId\"\n\t\t\t\t\t:inputLabel=\"assigneeLabel\"\n\t\t\t\t\t:labelOutside=\"true\"\n\t\t\t\t\t:options=\"shownOptions\"\n\t\t\t\t\t:loading=\"searching\"\n\t\t\t\t\t:filterable=\"false\"\n\t\t\t\t\t:placeholder=\"text(assigneeConfig.placeholder)\"\n\t\t\t\t\tdata-testid=\"cn-assign-dialog-assignee\"\n\t\t\t\t\t@search=\"onSearch\"\n\t\t\t\t\t@update:modelValue=\"onPick\" />\n\t\t\t\t<span v-if=\"assigneeHint\" class=\"cn-assign-dialog__hint\">{{ assigneeHint }}</span>\n\t\t\t</div>\n\n\t\t\t<div v-if=\"config.note !== false\" class=\"cn-assign-dialog__field\">\n\t\t\t\t<NcTextArea\n\t\t\t\t\t:id=\"noteId\"\n\t\t\t\t\tv-model=\"note\"\n\t\t\t\t\t:label=\"noteLabel\"\n\t\t\t\t\t:labelOutside=\"false\"\n\t\t\t\t\tresize=\"vertical\"\n\t\t\t\t\tdata-testid=\"cn-assign-dialog-note\" />\n\t\t\t\t<span v-if=\"noteConfig.hint\" class=\"cn-assign-dialog__hint\">{{ text(noteConfig.hint) }}</span>\n\t\t\t</div>\n\n\t\t\t<div v-for=\"toggle in toggles\"\n\t\t\t\t:key=\"toggle.field\"\n\t\t\t\tclass=\"cn-assign-dialog__toggle\"\n\t\t\t\t:class=\"{ 'cn-assign-dialog__toggle--on': flags[toggle.field] }\"\n\t\t\t\t:data-testid=\"`cn-assign-dialog-toggle-${toggle.field}`\">\n\t\t\t\t<NcCheckboxRadioSwitch :modelValue=\"flags[toggle.field] === true\" @update:modelValue=\"flags[toggle.field] = $event\">\n\t\t\t\t\t<span class=\"cn-assign-dialog__toggle-title\">{{ text(toggle.label) }}</span>\n\t\t\t\t</NcCheckboxRadioSwitch>\n\t\t\t\t<span v-if=\"toggle.note\" class=\"cn-assign-dialog__hint cn-assign-dialog__toggle-note\">{{ text(toggle.note) }}</span>\n\t\t\t</div>\n\n\t\t\t<p v-if=\"refusal\"\n\t\t\t\tclass=\"cn-assign-dialog__error\"\n\t\t\t\trole=\"alert\"\n\t\t\t\tdata-testid=\"cn-assign-dialog-refusal\">\n\t\t\t\t{{ refusal }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p v-else role=\"status\" data-testid=\"cn-assign-dialog-done\">\n\t\t\t{{ text(config.doneText) || t('nextcloud-vue', 'Done') }}\n\t\t</p>\n\n\t\t<template #actions>\n\t\t\t<NcButton :disabled=\"busy\" data-testid=\"cn-assign-dialog-cancel\" @click=\"close\">\n\t\t\t\t{{ done ? t('nextcloud-vue', 'Close') : t('nextcloud-vue', 'Cancel') }}\n\t\t\t</NcButton>\n\t\t\t<NcButton v-if=\"!done\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"!canConfirm\"\n\t\t\t\tdata-testid=\"cn-assign-dialog-confirm\"\n\t\t\t\t@click=\"confirm\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<CnIcon :name=\"confirmConfig.icon || 'SwapHorizontal'\" :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ text(confirmConfig.label) || t('nextcloud-vue', 'Assign') }}\n\t\t\t</NcButton>\n\t\t</template>\n\t</CnDialog>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcCheckboxRadioSwitch, NcTextArea } from '@nextcloud/vue'\nimport { fetchEndpointSource, interpolateUrlTokens } from '../../composables/useEndpointSource.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { searchNextcloudGroups, searchNextcloudUsers } from '../../utils/userAutocomplete.js'\nimport { CnDialog } from '../CnDialog/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnSearchSelect } from '../CnSearchSelect/index.js'\n\n/** The page refresh channel the built-in widgets listen on. */\nconst PAGE_REFRESH = 'cn:page:refresh'\n\n/** How long typing rests before a search runs, in ms. */\nconst SEARCH_DELAY = 250\n\n/** Gives each dialog's fields their own ids. */\nlet fieldSeq = 0\n\n/**\n * The found rows, with what was typed offered as an assignee of its own when\n * no row carries that id or name: where sharing is restricted the search\n * answers nothing, and an endpoint may still take the name.\n *\n * @param {Array<object>} found The rows found.\n * @param {string} typed What was typed.\n * @param {string} label The typed row's label.\n * @return {Array<object>} The rows.\n */\nexport function withTypedAssignee(found, typed, label) {\n\tconst lower = typed.toLowerCase()\n\tif (typed === '' || found.some((row) => String(row.id) === typed || String(row.label).toLowerCase() === lower)) {\n\t\treturn found\n\t}\n\treturn [...found, { id: typed, label, icon: 'AccountGroupOutline', kind: 'typed' }]\n}\n\n/**\n * CnAssignDialog — hand a record to another user or team, with a note and\n * optional switches, configured from the manifest (an `open-modal` action's\n * props). dossiq's case handover (board DqOverdragen: new team, why it moves,\n * \"this is a doorzending (Awb 2:3)\") is the first; pipelinq tickets and bulk\n * reassignment use the same dialog.\n *\n * `config`:\n * - `title`, `subtitle`, `doneText`: texts, through the host translate;\n * `eyebrow` a template filled from the record.\n * - `source`: an endpointSource to read the record from, else `object`.\n * - `assignee`: `{ kinds?, label?, placeholder?, hint?, currentPath?,\n * currentHint?, currentAs?, allowTyped?, field?, kindField? }`. `kinds` is `[\"group\"]`\n * (default), `[\"user\"]` or both; `currentPath` reads who has it now from the\n * record and `currentHint` (\"Now with {current}.\", the placeholder named\n * by `currentAs`) says so, in place of `hint`; `allowTyped`\n * offers what was typed as a choice; the id is sent as `field` (default\n * `assignee`) and its kind as `kindField` when set.\n * - `note`: `{ label?, hint?, required?, field? }`, required by default and\n * sent as `field` (default `note`); `false` drops it.\n * - `toggles`: `[{ field, label, note? }]`: switches that start off and are\n * sent as booleans.\n * - `confirm`: `{ label?, icon?, request?: { method?, url, body? }, action? }`.\n * `request` sends the choice itself (`body` templates filled from the\n * record); `action` dispatches a manifest action with the choice as its\n * last argument.\n *\n * Tokens (`@objectId`, `@object.<field>`) resolve in every url. A refusal is\n * shown in the server's own words and the dialog stays open.\n */\nexport default {\n\tname: 'CnAssignDialog',\n\n\tcomponents: { CnDialog, CnIcon, CnSearchSelect, NcButton, NcCheckboxRadioSwitch, NcTextArea },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The page's pre-bound action dispatcher (CnPageRenderer), for `confirm.action`. */\n\t\tcnDispatchAction: { default: null },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The dialog's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tconfig: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * The record, when the host has it.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['close', 'done'],\n\n\tdata() {\n\t\tconst seq = ++fieldSeq\n\t\tconst flags = {}\n\t\tfor (const toggle of Array.isArray(this.config.toggles) ? this.config.toggles : []) {\n\t\t\tif (toggle && toggle.field) {\n\t\t\t\tflags[toggle.field] = false\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\trecord: this.object,\n\t\t\toptions: [],\n\t\t\tpicked: null,\n\t\t\tsearching: false,\n\t\t\ttimer: null,\n\t\t\tsearchSeq: 0,\n\t\t\tnote: '',\n\t\t\tflags,\n\t\t\tbusy: false,\n\t\t\trefusal: '',\n\t\t\tdone: false,\n\t\t\tassigneeId: `cn-assign-dialog-assignee-${seq}`,\n\t\t\tnoteId: `cn-assign-dialog-note-${seq}`,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {object} The token context. */\n\t\ttokenCtx() {\n\t\t\treturn { objectId: this.recordId, object: this.record || {} }\n\t\t},\n\n\t\t/**\n\t\t * The record's id: `objectId`, or the route's `id` when the prop is\n\t\t * empty or a token the opener could not resolve.\n\t\t *\n\t\t * @return {string|number}\n\t\t */\n\t\trecordId() {\n\t\t\tconst given = this.objectId\n\t\t\tif (given !== '' && given !== null && given !== undefined && !String(given).startsWith('@')) {\n\t\t\t\treturn given\n\t\t\t}\n\t\t\treturn (this.$route && this.$route.params && this.$route.params.id) || ''\n\t\t},\n\n\t\t/** @return {object} The assignee config. */\n\t\tassigneeConfig() {\n\t\t\treturn this.config.assignee && typeof this.config.assignee === 'object' ? this.config.assignee : {}\n\t\t},\n\n\t\t/** @return {object} The note config. */\n\t\tnoteConfig() {\n\t\t\treturn this.config.note && typeof this.config.note === 'object' ? this.config.note : {}\n\t\t},\n\n\t\t/** @return {object} The confirm config. */\n\t\tconfirmConfig() {\n\t\t\treturn this.config.confirm && typeof this.config.confirm === 'object' ? this.config.confirm : {}\n\t\t},\n\n\t\t/** @return {Array<object>} The switches with a field. */\n\t\ttoggles() {\n\t\t\treturn (Array.isArray(this.config.toggles) ? this.config.toggles : []).filter((toggle) => toggle && toggle.field)\n\t\t},\n\n\t\t/** @return {string[]} The kinds searched: `group`, `user`. */\n\t\tkinds() {\n\t\t\tconst kinds = Array.isArray(this.assigneeConfig.kinds) ? this.assigneeConfig.kinds.filter((k) => k === 'group' || k === 'user') : []\n\t\t\treturn kinds.length ? kinds : ['group']\n\t\t},\n\n\t\t/** @return {string} The assignee field's label. */\n\t\tassigneeLabel() {\n\t\t\tif (this.assigneeConfig.label) {\n\t\t\t\treturn this.text(this.assigneeConfig.label)\n\t\t\t}\n\t\t\treturn this.kinds.length === 1 && this.kinds[0] === 'user' ? t('nextcloud-vue', 'New handler') : t('nextcloud-vue', 'New team')\n\t\t},\n\n\t\t/** @return {string} Who has the record now, read from it. */\n\t\tcurrent() {\n\t\t\tconst path = this.assigneeConfig.currentPath\n\t\t\tconst value = path && this.record ? readPath(this.record, path) : ''\n\t\t\treturn value === undefined || value === null ? '' : String(value)\n\t\t},\n\n\t\t/**\n\t\t * The hint under the assignee field: who has it now, else the configured hint.\n\t\t *\n\t\t * @spec openspec/changes/assign-dialog/specs/assign-dialog/spec.md#requirement-a-manifest-configures-an-assign-dialog\n\t\t * @return {string}\n\t\t */\n\t\tassigneeHint() {\n\t\t\tconst name = this.assigneeConfig.currentAs || 'current'\n\t\t\tconst now = this.current && this.assigneeConfig.currentHint ? fillTemplate(this.cnTranslate(this.assigneeConfig.currentHint), { [name]: this.current }) : ''\n\t\t\treturn now || this.text(this.assigneeConfig.hint)\n\t\t},\n\n\t\t/** @return {Array<object>} The rows, keeping the picked one listed. */\n\t\tshownOptions() {\n\t\t\tif (this.picked && !this.options.some((o) => String(o.id) === String(this.picked.id))) {\n\t\t\t\treturn [this.picked, ...this.options]\n\t\t\t}\n\t\t\treturn this.options\n\t\t},\n\n\t\t/** @return {boolean} Whether a note is asked and needed. */\n\t\tnoteNeeded() {\n\t\t\treturn this.config.note !== false && this.noteConfig.required !== false\n\t\t},\n\n\t\t/** @return {string} The note field's label. */\n\t\tnoteLabel() {\n\t\t\treturn this.text(this.noteConfig.label) || t('nextcloud-vue', 'Why it is moving')\n\t\t},\n\n\t\t/**\n\t\t * Whether the assignment may run: an assignee, a note when needed, nothing in flight.\n\t\t *\n\t\t * @spec openspec/changes/assign-dialog/specs/assign-dialog/spec.md#requirement-a-manifest-configures-an-assign-dialog\n\t\t * @return {boolean}\n\t\t */\n\t\tcanConfirm() {\n\t\t\treturn !this.busy && this.picked !== null && (!this.noteNeeded || this.note.trim() !== '')\n\t\t},\n\t},\n\n\tasync mounted() {\n\t\tconst source = this.config.source\n\t\tif (source && source.url) {\n\t\t\ttry {\n\t\t\t\tconst data = await fetchEndpointSource(source, this.tokenCtx, { fresh: true })\n\t\t\t\tthis.record = data && typeof data === 'object' ? data : this.record\n\t\t\t} catch {\n\t\t\t\t// The hint stays without the current assignee; the dialog still works.\n\t\t\t}\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tclearTimeout(this.timer)\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate, '' for anything else.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * A template filled from the record, translated first.\n\t\t *\n\t\t * @param {unknown} template The template.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template) {\n\t\t\tif (typeof template !== 'string' || template === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn fillTemplate(this.cnTranslate(template), this.record || {}).trim()\n\t\t},\n\n\t\t/**\n\t\t * Search after typing rests.\n\t\t *\n\t\t * @param {string} query What was typed.\n\t\t */\n\t\tonSearch(query) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tconst typed = String(query ?? '').trim()\n\t\t\tif (typed === '') {\n\t\t\t\tthis.options = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.timer = setTimeout(() => this.search(typed), SEARCH_DELAY)\n\t\t},\n\n\t\t/**\n\t\t * Search the configured kinds; offer what was typed when allowed.\n\t\t *\n\t\t * @spec openspec/changes/assign-dialog/specs/assign-dialog/spec.md#requirement-a-manifest-configures-an-assign-dialog\n\t\t * @param {string} typed What was typed.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync search(typed) {\n\t\t\tconst seq = ++this.searchSeq\n\t\t\tthis.searching = true\n\t\t\tconst [groups, users] = await Promise.all([\n\t\t\t\tthis.kinds.includes('group') ? searchNextcloudGroups(typed, { limit: 20 }) : [],\n\t\t\t\tthis.kinds.includes('user') ? searchNextcloudUsers(typed, { limit: 20 }) : [],\n\t\t\t])\n\t\t\tif (seq !== this.searchSeq) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst found = [\n\t\t\t\t...groups.map((g) => ({ id: g.id, label: g.label, secondary: g.subline || '', icon: 'AccountGroupOutline', kind: 'group' })),\n\t\t\t\t...users.map((u) => ({ id: u.id, label: u.label, secondary: u.subline || '', avatar: true, kind: 'user' })),\n\t\t\t]\n\t\t\tthis.options = this.assigneeConfig.allowTyped ? withTypedAssignee(found, typed, t('nextcloud-vue', 'Use \"{name}\"', { name: typed })) : found\n\t\t\tthis.searching = false\n\t\t},\n\n\t\t/**\n\t\t * Take the picked row.\n\t\t *\n\t\t * @param {string|null} id The picked id.\n\t\t */\n\t\tonPick(id) {\n\t\t\tthis.picked = id === null ? null : (this.shownOptions.find((o) => String(o.id) === String(id)) || null)\n\t\t},\n\n\t\t/**\n\t\t * The choice as sent: assignee, its kind, the note and the switches.\n\t\t *\n\t\t * @return {object}\n\t\t */\n\t\tchoice() {\n\t\t\tconst out = { [this.assigneeConfig.field || 'assignee']: String(this.picked.id) }\n\t\t\tif (this.assigneeConfig.kindField) {\n\t\t\t\tout[this.assigneeConfig.kindField] = this.picked.kind === 'typed' ? this.kinds[0] : this.picked.kind\n\t\t\t}\n\t\t\tif (this.config.note !== false) {\n\t\t\t\tout[this.noteConfig.field || 'note'] = this.note.trim()\n\t\t\t}\n\t\t\treturn { ...out, ...this.flags }\n\t\t},\n\n\t\t/**\n\t\t * Assign once: send `confirm.request`, or dispatch `confirm.action`,\n\t\t * then show the done text and refresh the page.\n\t\t *\n\t\t * @spec openspec/changes/assign-dialog/specs/assign-dialog/spec.md#requirement-a-manifest-configures-an-assign-dialog\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync confirm() {\n\t\t\tif (!this.canConfirm) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tthis.refusal = ''\n\t\t\tconst choice = this.choice()\n\t\t\ttry {\n\t\t\t\tconst request = this.confirmConfig.request\n\t\t\t\tif (request && request.url) {\n\t\t\t\t\tconst url = interpolateUrlTokens(request.url, this.tokenCtx)\n\t\t\t\t\tconst body = {}\n\t\t\t\t\tfor (const [key, value] of Object.entries(request.body || {})) {\n\t\t\t\t\t\tbody[key] = typeof value === 'string' ? fillTemplate(value, this.record || {}) : value\n\t\t\t\t\t}\n\t\t\t\t\tconst method = String(request.method || 'POST').toLowerCase()\n\t\t\t\t\tawait axios[['post', 'put', 'patch'].includes(method) ? method : 'post'](/^https?:\\/\\//i.test(url) ? url : generateUrl(url), { ...body, ...choice })\n\t\t\t\t} else if (this.confirmConfig.action && typeof this.cnDispatchAction === 'function') {\n\t\t\t\t\tawait this.cnDispatchAction({ ...this.confirmConfig.action, args: [...(this.confirmConfig.action.args || []), choice] })\n\t\t\t\t}\n\t\t\t\tthis.done = true\n\t\t\t\temit(PAGE_REFRESH, {})\n\t\t\t\t/** @event done The assignment ran. Payload: the choice as sent. */\n\t\t\t\tthis.$emit('done', choice)\n\t\t\t} catch (failure) {\n\t\t\t\tconst data = failure && failure.response && failure.response.data\n\t\t\t\tthis.refusal = String((data && (data.message || data.error)) || t('nextcloud-vue', 'It was not handed over.'))\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\n\t\t/** Close the dialog, unless the assignment is running. */\n\t\tclose() {\n\t\t\tif (this.busy) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/** @event close The dialog was closed. */\n\t\t\tthis.$emit('close')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-assign-dialog {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 18px;\n}\n\n.cn-assign-dialog__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-assign-dialog__hint {\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-assign-dialog__toggle {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-large, 8px);\n}\n\n.cn-assign-dialog__toggle--on {\n\tborder: 2px solid var(--color-primary-element);\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-assign-dialog__toggle-title {\n\tfont-weight: 600;\n}\n\n.cn-assign-dialog__toggle-note {\n\tpadding-inline-start: 44px;\n}\n\n.cn-assign-dialog__error {\n\tmargin: 0;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["_createVNode","_createBlock","_createTextVNode","_openBlock","_createElementBlock","_toDisplayString","_Fragment","_renderList"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import script from './CnDecisionListWidget.vue2.js';
2
+ import { render } from './CnDecisionListWidget.vue3.js';
3
+
4
+ script.render = render;
5
+ script.__scopeId = "data-v-5cda9d8e";
6
+ const __sfc_main = script;
7
+
8
+ export { __sfc_main as default };
9
+ //# sourceMappingURL=CnDecisionListWidget.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnDecisionListWidget.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
@@ -0,0 +1,281 @@
1
+ import axios__default from '@nextcloud/axios';
2
+ import { translate } from '@nextcloud/l10n';
3
+ import { generateUrl } from '@nextcloud/router';
4
+ import { NcButton, NcDateTimePickerNative, NcEmptyContent, NcLoadingIcon, NcSelect, NcTextField } from '@nextcloud/vue';
5
+ import { fetchEndpointSource } from '../../composables/useEndpointSource.js';
6
+ import { fillTemplate } from '../../utils/fillTemplate.js';
7
+ import { readPath } from '../../utils/readPath.js';
8
+
9
+ /**
10
+ * A date picker value as `YYYY-MM-DD`, or the value as a string.
11
+ *
12
+ * @param {unknown} value The value.
13
+ * @return {string}
14
+ */
15
+ function dateValue(value) {
16
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
17
+ const pad = (n) => String(n).padStart(2, '0');
18
+ return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`
19
+ }
20
+ return value === null || value === undefined ? '' : String(value)
21
+ }
22
+
23
+ /**
24
+ * CnDecisionListWidget — the `decision-list` widget: a worklist where each
25
+ * row takes one answer from a closed set, a reason, and any fields that
26
+ * answer needs, recorded per row (board DqMijnWerk "archival reviews": a
27
+ * verdict, why, and a new date when retained). The row leaves the list once
28
+ * its answer lands, so the user keeps their place. A failed read shows an
29
+ * error with a retry, never an empty list; a refusal stays on its row in the
30
+ * server's words. hermiq approvals, learniq review queues and integriq
31
+ * approvals use the same widget.
32
+ *
33
+ * `content`:
34
+ * - `source`: an endpointSource for the rows (`responsePath`), or `itemsPath`
35
+ * to read them out of the response.
36
+ * - `idField` (default `id`), `title` and `meta`: templates filled from a row.
37
+ * - `answers`: `[{ value, label, fields? }]`; `fields` are `[{ field, type?:
38
+ * "date"|"text", label?, required? }]` that answer asks for.
39
+ * - `reason`: `{ label?, required?, field? }`, required by default and sent
40
+ * as `field` (default `reason`); `false` drops it.
41
+ * - `submit`: `{ label?, request: { method?, url, body? } }`: `url` and `body`
42
+ * templates filled from the row; the answer goes as `answerField` (default
43
+ * `answer`), with the reason and the answer's fields.
44
+ * - `emptyText`, `errorTitle`, `answerLabel`: texts.
45
+ *
46
+ * Texts go through the host translate.
47
+ */
48
+ var script = {
49
+ name: 'CnDecisionListWidget',
50
+
51
+ components: { NcButton, NcDateTimePickerNative, NcEmptyContent, NcLoadingIcon, NcSelect, NcTextField },
52
+
53
+ inject: {
54
+ /** The host app's translate (CnAppRoot `cnTranslate`). */
55
+ cnTranslate: { default: () => (key) => key },
56
+ },
57
+
58
+ props: {
59
+ /**
60
+ * The widget's configuration (see the component docs).
61
+ *
62
+ * @type {object}
63
+ */
64
+ content: {
65
+ type: Object,
66
+ default: () => ({}),
67
+ },
68
+ },
69
+
70
+ emits: ['decided'],
71
+
72
+ data() {
73
+ return {
74
+ loading: true,
75
+ error: '',
76
+ items: [],
77
+ drafts: {},
78
+ refusals: {},
79
+ busy: {},
80
+ }
81
+ },
82
+
83
+ computed: {
84
+ /** @return {Array<{value: string, label: string, fields?: Array<object>}>} The answers with a value. */
85
+ answers() {
86
+ return (Array.isArray(this.content.answers) ? this.content.answers : []).filter((a) => a && a.value !== undefined && a.value !== '')
87
+ },
88
+
89
+ /** @return {Array<{id: string, label: string}>} The answers as select options. */
90
+ answerOptions() {
91
+ return this.answers.map((a) => ({ id: String(a.value), label: this.text(a.label) || String(a.value) }))
92
+ },
93
+
94
+ /** @return {object|null} The reason config, null when dropped. */
95
+ reasonConfig() {
96
+ if (this.content.reason === false) {
97
+ return null
98
+ }
99
+ return this.content.reason && typeof this.content.reason === 'object' ? this.content.reason : {}
100
+ },
101
+
102
+ /** @return {object} The submit config. */
103
+ submitConfig() {
104
+ return this.content.submit && typeof this.content.submit === 'object' ? this.content.submit : {}
105
+ },
106
+
107
+ /**
108
+ * The rows: id, title and meta filled from each item.
109
+ *
110
+ * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions
111
+ * @return {Array<{id: string, title: string, meta: string, item: object}>}
112
+ */
113
+ rows() {
114
+ const idField = this.content.idField || 'id';
115
+ return this.items.map((item) => {
116
+ const id = String(readPath(item, idField) ?? '');
117
+ return {
118
+ id,
119
+ title: (this.content.title ? fillTemplate(this.cnTranslate(this.content.title), item).trim() : '') || String(item.title || id),
120
+ meta: this.content.meta ? fillTemplate(this.cnTranslate(this.content.meta), item).trim() : '',
121
+ item,
122
+ }
123
+ }).filter((row) => row.id !== '')
124
+ },
125
+ },
126
+
127
+ mounted() {
128
+ this.load();
129
+ },
130
+
131
+ methods: {
132
+ t: translate,
133
+
134
+ /**
135
+ * A config text through the host translate.
136
+ *
137
+ * @param {unknown} value The text.
138
+ * @return {string}
139
+ */
140
+ text(value) {
141
+ return typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''
142
+ },
143
+
144
+ /**
145
+ * Read the rows; a failure is an error with a retry, never an empty list.
146
+ *
147
+ * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions
148
+ * @return {Promise<void>}
149
+ */
150
+ async load() {
151
+ this.loading = true;
152
+ this.error = '';
153
+ try {
154
+ const data = await fetchEndpointSource(this.content.source, {}, { force: true });
155
+ const list = this.content.itemsPath ? readPath(data, this.content.itemsPath) : data;
156
+ this.items = Array.isArray(list) ? list.filter((item) => item && typeof item === 'object') : [];
157
+ } catch (failure) {
158
+ const body = failure && failure.response && failure.response.data;
159
+ this.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure);
160
+ } finally {
161
+ this.loading = false;
162
+ }
163
+ },
164
+
165
+ /**
166
+ * A row's draft.
167
+ *
168
+ * @param {string} id The row id.
169
+ * @return {{answer: string, reason: string, extra: object}}
170
+ */
171
+ draft(id) {
172
+ return this.drafts[id] || { answer: '', reason: '', extra: {} }
173
+ },
174
+
175
+ /**
176
+ * Change a row's draft.
177
+ *
178
+ * @param {string} id The row id.
179
+ * @param {object} change The fields to change.
180
+ */
181
+ patch(id, change) {
182
+ this.drafts = { ...this.drafts, [id]: { ...this.draft(id), ...change } };
183
+ },
184
+
185
+ /**
186
+ * Set one of an answer's fields on a row.
187
+ *
188
+ * @param {string} id The row id.
189
+ * @param {string} field The field.
190
+ * @param {unknown} value The value.
191
+ */
192
+ setExtra(id, field, value) {
193
+ this.patch(id, { extra: { ...this.draft(id).extra, [field]: dateValue(value) } });
194
+ },
195
+
196
+ /**
197
+ * @param {string} id The row id.
198
+ * @return {object|null} The chosen answer as a select option.
199
+ */
200
+ answerOption(id) {
201
+ return this.answerOptions.find((o) => o.id === this.draft(id).answer) || null
202
+ },
203
+
204
+ /**
205
+ * The fields the chosen answer asks for.
206
+ *
207
+ * @param {string} id The row id.
208
+ * @return {Array<object>}
209
+ */
210
+ extraFields(id) {
211
+ const answer = this.answers.find((a) => String(a.value) === this.draft(id).answer);
212
+ return answer && Array.isArray(answer.fields) ? answer.fields.filter((f) => f && f.field) : []
213
+ },
214
+
215
+ /**
216
+ * Whether a row may be sent: an answer, a needed reason, its required fields.
217
+ *
218
+ * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions
219
+ * @param {string} id The row id.
220
+ * @return {boolean}
221
+ */
222
+ canSubmit(id) {
223
+ const draft = this.draft(id);
224
+ if (this.busy[id] || !this.answerOptions.some((o) => o.id === draft.answer)) {
225
+ return false
226
+ }
227
+ if (this.reasonConfig && this.reasonConfig.required !== false && draft.reason.trim() === '') {
228
+ return false
229
+ }
230
+ return this.extraFields(id).every((f) => f.required === false || String(draft.extra[f.field] || '').trim() !== '')
231
+ },
232
+
233
+ /**
234
+ * Record one row's decision and take it off the list.
235
+ *
236
+ * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions
237
+ * @param {{id: string, item: object}} row The row.
238
+ * @return {Promise<void>}
239
+ */
240
+ async submit(row) {
241
+ if (!this.canSubmit(row.id)) {
242
+ return
243
+ }
244
+ const draft = this.draft(row.id);
245
+ const request = this.submitConfig.request || {};
246
+ const body = {};
247
+ for (const [key, value] of Object.entries(request.body || {})) {
248
+ body[key] = typeof value === 'string' ? fillTemplate(value, row.item) : value;
249
+ }
250
+ body[this.content.answerField || 'answer'] = draft.answer;
251
+ if (this.reasonConfig) {
252
+ body[this.reasonConfig.field || 'reason'] = draft.reason.trim();
253
+ }
254
+ for (const field of this.extraFields(row.id)) {
255
+ if (String(draft.extra[field.field] || '') !== '') {
256
+ body[field.field] = draft.extra[field.field];
257
+ }
258
+ }
259
+ this.busy = { ...this.busy, [row.id]: true };
260
+ this.refusals = { ...this.refusals, [row.id]: '' };
261
+ try {
262
+ if (request.url) {
263
+ const url = fillTemplate(request.url, row.item);
264
+ const method = String(request.method || 'POST').toLowerCase();
265
+ await axios__default[['post', 'put', 'patch'].includes(method) ? method : 'post'](/^https?:\/\//i.test(url) ? url : generateUrl(url), body);
266
+ }
267
+ this.items = this.items.filter((item) => item !== row.item);
268
+ /** @event decided A row's decision landed. Payload: `{ id, item, body }`. */
269
+ this.$emit('decided', { id: row.id, item: row.item, body });
270
+ } catch (failure) {
271
+ const data = failure && failure.response && failure.response.data;
272
+ this.refusals = { ...this.refusals, [row.id]: String((data && (data.message || data.error)) || translate('nextcloud-vue', 'This decision was not recorded.')) };
273
+ } finally {
274
+ this.busy = { ...this.busy, [row.id]: false };
275
+ }
276
+ },
277
+ },
278
+ };
279
+
280
+ export { script as default };
281
+ //# sourceMappingURL=CnDecisionListWidget.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnDecisionListWidget.vue2.js","sources":["../../../../src/components/CnDecisionListWidget/CnDecisionListWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-decision-list\" data-testid=\"cn-decision-list\">\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"24\" />\n\t\t<NcEmptyContent v-else-if=\"error\"\n\t\t\t:name=\"text(content.errorTitle) || t('nextcloud-vue', 'This list could not be read')\"\n\t\t\t:description=\"error\"\n\t\t\tdata-testid=\"cn-decision-list-error\">\n\t\t\t<template #action>\n\t\t\t\t<NcButton data-testid=\"cn-decision-list-retry\" @click=\"load\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Try again') }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t</NcEmptyContent>\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-decision-list__empty\" data-testid=\"cn-decision-list-empty\">\n\t\t\t{{ text(content.emptyText) || t('nextcloud-vue', 'Nothing to decide right now.') }}\n\t\t</p>\n\t\t<ul v-else class=\"cn-decision-list__rows\">\n\t\t\t<li v-for=\"row in rows\"\n\t\t\t\t:key=\"row.id\"\n\t\t\t\tclass=\"cn-decision-list__row\"\n\t\t\t\t:data-testid=\"`cn-decision-list-row-${row.id}`\">\n\t\t\t\t<p class=\"cn-decision-list__title\">\n\t\t\t\t\t{{ row.title }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"row.meta\" class=\"cn-decision-list__meta\">\n\t\t\t\t\t{{ row.meta }}\n\t\t\t\t</p>\n\t\t\t\t<div class=\"cn-decision-list__fields\">\n\t\t\t\t\t<NcSelect :modelValue=\"answerOption(row.id)\"\n\t\t\t\t\t\t:options=\"answerOptions\"\n\t\t\t\t\t\t:inputLabel=\"text(content.answerLabel) || t('nextcloud-vue', 'Your answer')\"\n\t\t\t\t\t\t:clearable=\"false\"\n\t\t\t\t\t\t:data-testid=\"`cn-decision-list-answer-${row.id}`\"\n\t\t\t\t\t\t@update:modelValue=\"(v) => patch(row.id, { answer: v ? v.id : '' })\" />\n\t\t\t\t\t<NcTextField v-if=\"reasonConfig\"\n\t\t\t\t\t\t:modelValue=\"draft(row.id).reason\"\n\t\t\t\t\t\t:label=\"text(reasonConfig.label) || t('nextcloud-vue', 'Why?')\"\n\t\t\t\t\t\t:data-testid=\"`cn-decision-list-reason-${row.id}`\"\n\t\t\t\t\t\t@update:modelValue=\"(v) => patch(row.id, { reason: String(v ?? '') })\" />\n\t\t\t\t\t<template v-for=\"field in extraFields(row.id)\" :key=\"field.field\">\n\t\t\t\t\t\t<NcDateTimePickerNative v-if=\"field.type === 'date'\"\n\t\t\t\t\t\t\t:modelValue=\"draft(row.id).extra[field.field] || null\"\n\t\t\t\t\t\t\ttype=\"date\"\n\t\t\t\t\t\t\t:label=\"text(field.label) || field.field\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-decision-list-${field.field}-${row.id}`\"\n\t\t\t\t\t\t\t@update:modelValue=\"(v) => setExtra(row.id, field.field, v)\" />\n\t\t\t\t\t\t<NcTextField v-else\n\t\t\t\t\t\t\t:modelValue=\"draft(row.id).extra[field.field] || ''\"\n\t\t\t\t\t\t\t:label=\"text(field.label) || field.field\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-decision-list-${field.field}-${row.id}`\"\n\t\t\t\t\t\t\t@update:modelValue=\"(v) => setExtra(row.id, field.field, v)\" />\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"refusals[row.id]\" class=\"cn-decision-list__refusal\" role=\"alert\">\n\t\t\t\t\t{{ refusals[row.id] }}\n\t\t\t\t</p>\n\t\t\t\t<NcButton :disabled=\"!canSubmit(row.id)\"\n\t\t\t\t\t:data-testid=\"`cn-decision-list-submit-${row.id}`\"\n\t\t\t\t\t@click=\"submit(row)\">\n\t\t\t\t\t{{ text(submitConfig.label) || t('nextcloud-vue', 'Record this decision') }}\n\t\t\t\t</NcButton>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcDateTimePickerNative, NcEmptyContent, NcLoadingIcon, NcSelect, NcTextField } from '@nextcloud/vue'\nimport { fetchEndpointSource } from '../../composables/useEndpointSource.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * A date picker value as `YYYY-MM-DD`, or the value as a string.\n *\n * @param {unknown} value The value.\n * @return {string}\n */\nfunction dateValue(value) {\n\tif (value instanceof Date && !Number.isNaN(value.getTime())) {\n\t\tconst pad = (n) => String(n).padStart(2, '0')\n\t\treturn `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`\n\t}\n\treturn value === null || value === undefined ? '' : String(value)\n}\n\n/**\n * CnDecisionListWidget — the `decision-list` widget: a worklist where each\n * row takes one answer from a closed set, a reason, and any fields that\n * answer needs, recorded per row (board DqMijnWerk \"archival reviews\": a\n * verdict, why, and a new date when retained). The row leaves the list once\n * its answer lands, so the user keeps their place. A failed read shows an\n * error with a retry, never an empty list; a refusal stays on its row in the\n * server's words. hermiq approvals, learniq review queues and integriq\n * approvals use the same widget.\n *\n * `content`:\n * - `source`: an endpointSource for the rows (`responsePath`), or `itemsPath`\n * to read them out of the response.\n * - `idField` (default `id`), `title` and `meta`: templates filled from a row.\n * - `answers`: `[{ value, label, fields? }]`; `fields` are `[{ field, type?:\n * \"date\"|\"text\", label?, required? }]` that answer asks for.\n * - `reason`: `{ label?, required?, field? }`, required by default and sent\n * as `field` (default `reason`); `false` drops it.\n * - `submit`: `{ label?, request: { method?, url, body? } }`: `url` and `body`\n * templates filled from the row; the answer goes as `answerField` (default\n * `answer`), with the reason and the answer's fields.\n * - `emptyText`, `errorTitle`, `answerLabel`: texts.\n *\n * Texts go through the host translate.\n */\nexport default {\n\tname: 'CnDecisionListWidget',\n\n\tcomponents: { NcButton, NcDateTimePickerNative, NcEmptyContent, NcLoadingIcon, NcSelect, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['decided'],\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\terror: '',\n\t\t\titems: [],\n\t\t\tdrafts: {},\n\t\t\trefusals: {},\n\t\t\tbusy: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {Array<{value: string, label: string, fields?: Array<object>}>} The answers with a value. */\n\t\tanswers() {\n\t\t\treturn (Array.isArray(this.content.answers) ? this.content.answers : []).filter((a) => a && a.value !== undefined && a.value !== '')\n\t\t},\n\n\t\t/** @return {Array<{id: string, label: string}>} The answers as select options. */\n\t\tanswerOptions() {\n\t\t\treturn this.answers.map((a) => ({ id: String(a.value), label: this.text(a.label) || String(a.value) }))\n\t\t},\n\n\t\t/** @return {object|null} The reason config, null when dropped. */\n\t\treasonConfig() {\n\t\t\tif (this.content.reason === false) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.content.reason && typeof this.content.reason === 'object' ? this.content.reason : {}\n\t\t},\n\n\t\t/** @return {object} The submit config. */\n\t\tsubmitConfig() {\n\t\t\treturn this.content.submit && typeof this.content.submit === 'object' ? this.content.submit : {}\n\t\t},\n\n\t\t/**\n\t\t * The rows: id, title and meta filled from each item.\n\t\t *\n\t\t * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions\n\t\t * @return {Array<{id: string, title: string, meta: string, item: object}>}\n\t\t */\n\t\trows() {\n\t\t\tconst idField = this.content.idField || 'id'\n\t\t\treturn this.items.map((item) => {\n\t\t\t\tconst id = String(readPath(item, idField) ?? '')\n\t\t\t\treturn {\n\t\t\t\t\tid,\n\t\t\t\t\ttitle: (this.content.title ? fillTemplate(this.cnTranslate(this.content.title), item).trim() : '') || String(item.title || id),\n\t\t\t\t\tmeta: this.content.meta ? fillTemplate(this.cnTranslate(this.content.meta), item).trim() : '',\n\t\t\t\t\titem,\n\t\t\t\t}\n\t\t\t}).filter((row) => row.id !== '')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * Read the rows; a failure is an error with a retry, never an empty list.\n\t\t *\n\t\t * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst data = await fetchEndpointSource(this.content.source, {}, { force: true })\n\t\t\t\tconst list = this.content.itemsPath ? readPath(data, this.content.itemsPath) : data\n\t\t\t\tthis.items = Array.isArray(list) ? list.filter((item) => item && typeof item === 'object') : []\n\t\t\t} catch (failure) {\n\t\t\t\tconst body = failure && failure.response && failure.response.data\n\t\t\t\tthis.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A row's draft.\n\t\t *\n\t\t * @param {string} id The row id.\n\t\t * @return {{answer: string, reason: string, extra: object}}\n\t\t */\n\t\tdraft(id) {\n\t\t\treturn this.drafts[id] || { answer: '', reason: '', extra: {} }\n\t\t},\n\n\t\t/**\n\t\t * Change a row's draft.\n\t\t *\n\t\t * @param {string} id The row id.\n\t\t * @param {object} change The fields to change.\n\t\t */\n\t\tpatch(id, change) {\n\t\t\tthis.drafts = { ...this.drafts, [id]: { ...this.draft(id), ...change } }\n\t\t},\n\n\t\t/**\n\t\t * Set one of an answer's fields on a row.\n\t\t *\n\t\t * @param {string} id The row id.\n\t\t * @param {string} field The field.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\tsetExtra(id, field, value) {\n\t\t\tthis.patch(id, { extra: { ...this.draft(id).extra, [field]: dateValue(value) } })\n\t\t},\n\n\t\t/**\n\t\t * @param {string} id The row id.\n\t\t * @return {object|null} The chosen answer as a select option.\n\t\t */\n\t\tanswerOption(id) {\n\t\t\treturn this.answerOptions.find((o) => o.id === this.draft(id).answer) || null\n\t\t},\n\n\t\t/**\n\t\t * The fields the chosen answer asks for.\n\t\t *\n\t\t * @param {string} id The row id.\n\t\t * @return {Array<object>}\n\t\t */\n\t\textraFields(id) {\n\t\t\tconst answer = this.answers.find((a) => String(a.value) === this.draft(id).answer)\n\t\t\treturn answer && Array.isArray(answer.fields) ? answer.fields.filter((f) => f && f.field) : []\n\t\t},\n\n\t\t/**\n\t\t * Whether a row may be sent: an answer, a needed reason, its required fields.\n\t\t *\n\t\t * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions\n\t\t * @param {string} id The row id.\n\t\t * @return {boolean}\n\t\t */\n\t\tcanSubmit(id) {\n\t\t\tconst draft = this.draft(id)\n\t\t\tif (this.busy[id] || !this.answerOptions.some((o) => o.id === draft.answer)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (this.reasonConfig && this.reasonConfig.required !== false && draft.reason.trim() === '') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn this.extraFields(id).every((f) => f.required === false || String(draft.extra[f.field] || '').trim() !== '')\n\t\t},\n\n\t\t/**\n\t\t * Record one row's decision and take it off the list.\n\t\t *\n\t\t * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions\n\t\t * @param {{id: string, item: object}} row The row.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync submit(row) {\n\t\t\tif (!this.canSubmit(row.id)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst draft = this.draft(row.id)\n\t\t\tconst request = this.submitConfig.request || {}\n\t\t\tconst body = {}\n\t\t\tfor (const [key, value] of Object.entries(request.body || {})) {\n\t\t\t\tbody[key] = typeof value === 'string' ? fillTemplate(value, row.item) : value\n\t\t\t}\n\t\t\tbody[this.content.answerField || 'answer'] = draft.answer\n\t\t\tif (this.reasonConfig) {\n\t\t\t\tbody[this.reasonConfig.field || 'reason'] = draft.reason.trim()\n\t\t\t}\n\t\t\tfor (const field of this.extraFields(row.id)) {\n\t\t\t\tif (String(draft.extra[field.field] || '') !== '') {\n\t\t\t\t\tbody[field.field] = draft.extra[field.field]\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.busy = { ...this.busy, [row.id]: true }\n\t\t\tthis.refusals = { ...this.refusals, [row.id]: '' }\n\t\t\ttry {\n\t\t\t\tif (request.url) {\n\t\t\t\t\tconst url = fillTemplate(request.url, row.item)\n\t\t\t\t\tconst method = String(request.method || 'POST').toLowerCase()\n\t\t\t\t\tawait axios[['post', 'put', 'patch'].includes(method) ? method : 'post'](/^https?:\\/\\//i.test(url) ? url : generateUrl(url), body)\n\t\t\t\t}\n\t\t\t\tthis.items = this.items.filter((item) => item !== row.item)\n\t\t\t\t/** @event decided A row's decision landed. Payload: `{ id, item, body }`. */\n\t\t\t\tthis.$emit('decided', { id: row.id, item: row.item, body })\n\t\t\t} catch (failure) {\n\t\t\t\tconst data = failure && failure.response && failure.response.data\n\t\t\t\tthis.refusals = { ...this.refusals, [row.id]: String((data && (data.message || data.error)) || t('nextcloud-vue', 'This decision was not recorded.')) }\n\t\t\t} finally {\n\t\t\t\tthis.busy = { ...this.busy, [row.id]: false }\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-decision-list__empty,\n.cn-decision-list__meta {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-decision-list__rows {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-decision-list__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding-block: 10px;\n\tborder-block-end: 1px solid var(--color-border);\n}\n\n.cn-decision-list__title {\n\tmargin: 0;\n\tfont-weight: 600;\n}\n\n.cn-decision-list__fields {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n\tgap: 8px;\n}\n\n.cn-decision-list__refusal {\n\tmargin: 0;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["t","axios"],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ import { resolveComponent, openBlock, createElementBlock, createBlock, withCtx, createVNode, createTextVNode, toDisplayString, Fragment, renderList, createElementVNode, createCommentVNode } from 'vue';
2
+
3
+ const _hoisted_1 = {
4
+ class: "cn-decision-list",
5
+ "data-testid": "cn-decision-list"
6
+ };
7
+ const _hoisted_2 = {
8
+ key: 2,
9
+ class: "cn-decision-list__empty",
10
+ "data-testid": "cn-decision-list-empty"
11
+ };
12
+ const _hoisted_3 = {
13
+ key: 3,
14
+ class: "cn-decision-list__rows"
15
+ };
16
+ const _hoisted_4 = ["data-testid"];
17
+ const _hoisted_5 = { class: "cn-decision-list__title" };
18
+ const _hoisted_6 = {
19
+ key: 0,
20
+ class: "cn-decision-list__meta"
21
+ };
22
+ const _hoisted_7 = { class: "cn-decision-list__fields" };
23
+ const _hoisted_8 = {
24
+ key: 1,
25
+ class: "cn-decision-list__refusal",
26
+ role: "alert"
27
+ };
28
+
29
+ function render(_ctx, _cache, $props, $setup, $data, $options) {
30
+ const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
31
+ const _component_NcButton = resolveComponent("NcButton");
32
+ const _component_NcEmptyContent = resolveComponent("NcEmptyContent");
33
+ const _component_NcSelect = resolveComponent("NcSelect");
34
+ const _component_NcTextField = resolveComponent("NcTextField");
35
+ const _component_NcDateTimePickerNative = resolveComponent("NcDateTimePickerNative");
36
+
37
+ return (openBlock(), createElementBlock("div", _hoisted_1, [
38
+ ($data.loading)
39
+ ? (openBlock(), createBlock(_component_NcLoadingIcon, {
40
+ key: 0,
41
+ size: 24
42
+ }))
43
+ : ($data.error)
44
+ ? (openBlock(), createBlock(_component_NcEmptyContent, {
45
+ key: 1,
46
+ name: $options.text($props.content.errorTitle) || $options.t('nextcloud-vue', 'This list could not be read'),
47
+ description: $data.error,
48
+ "data-testid": "cn-decision-list-error"
49
+ }, {
50
+ action: withCtx(() => [
51
+ createVNode(_component_NcButton, {
52
+ "data-testid": "cn-decision-list-retry",
53
+ onClick: $options.load
54
+ }, {
55
+ default: withCtx(() => [
56
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Try again')), 1 /* TEXT */)
57
+ ]),
58
+ _: 1 /* STABLE */
59
+ }, 8 /* PROPS */, ["onClick"])
60
+ ]),
61
+ _: 1 /* STABLE */
62
+ }, 8 /* PROPS */, ["name", "description"]))
63
+ : ($options.rows.length === 0)
64
+ ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($options.text($props.content.emptyText) || $options.t('nextcloud-vue', 'Nothing to decide right now.')), 1 /* TEXT */))
65
+ : (openBlock(), createElementBlock("ul", _hoisted_3, [
66
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.rows, (row) => {
67
+ return (openBlock(), createElementBlock("li", {
68
+ key: row.id,
69
+ class: "cn-decision-list__row",
70
+ "data-testid": `cn-decision-list-row-${row.id}`
71
+ }, [
72
+ createElementVNode("p", _hoisted_5, toDisplayString(row.title), 1 /* TEXT */),
73
+ (row.meta)
74
+ ? (openBlock(), createElementBlock("p", _hoisted_6, toDisplayString(row.meta), 1 /* TEXT */))
75
+ : createCommentVNode("v-if", true),
76
+ createElementVNode("div", _hoisted_7, [
77
+ createVNode(_component_NcSelect, {
78
+ modelValue: $options.answerOption(row.id),
79
+ options: $options.answerOptions,
80
+ inputLabel: $options.text($props.content.answerLabel) || $options.t('nextcloud-vue', 'Your answer'),
81
+ clearable: false,
82
+ "data-testid": `cn-decision-list-answer-${row.id}`,
83
+ "onUpdate:modelValue": (v) => $options.patch(row.id, { answer: v ? v.id : '' })
84
+ }, null, 8 /* PROPS */, ["modelValue", "options", "inputLabel", "data-testid", "onUpdate:modelValue"]),
85
+ ($options.reasonConfig)
86
+ ? (openBlock(), createBlock(_component_NcTextField, {
87
+ key: 0,
88
+ modelValue: $options.draft(row.id).reason,
89
+ label: $options.text($options.reasonConfig.label) || $options.t('nextcloud-vue', 'Why?'),
90
+ "data-testid": `cn-decision-list-reason-${row.id}`,
91
+ "onUpdate:modelValue": (v) => $options.patch(row.id, { reason: String(v ?? '') })
92
+ }, null, 8 /* PROPS */, ["modelValue", "label", "data-testid", "onUpdate:modelValue"]))
93
+ : createCommentVNode("v-if", true),
94
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.extraFields(row.id), (field) => {
95
+ return (openBlock(), createElementBlock(Fragment, {
96
+ key: field.field
97
+ }, [
98
+ (field.type === 'date')
99
+ ? (openBlock(), createBlock(_component_NcDateTimePickerNative, {
100
+ key: 0,
101
+ modelValue: $options.draft(row.id).extra[field.field] || null,
102
+ type: "date",
103
+ label: $options.text(field.label) || field.field,
104
+ "data-testid": `cn-decision-list-${field.field}-${row.id}`,
105
+ "onUpdate:modelValue": (v) => $options.setExtra(row.id, field.field, v)
106
+ }, null, 8 /* PROPS */, ["modelValue", "label", "data-testid", "onUpdate:modelValue"]))
107
+ : (openBlock(), createBlock(_component_NcTextField, {
108
+ key: 1,
109
+ modelValue: $options.draft(row.id).extra[field.field] || '',
110
+ label: $options.text(field.label) || field.field,
111
+ "data-testid": `cn-decision-list-${field.field}-${row.id}`,
112
+ "onUpdate:modelValue": (v) => $options.setExtra(row.id, field.field, v)
113
+ }, null, 8 /* PROPS */, ["modelValue", "label", "data-testid", "onUpdate:modelValue"]))
114
+ ], 64 /* STABLE_FRAGMENT */))
115
+ }), 128 /* KEYED_FRAGMENT */))
116
+ ]),
117
+ ($data.refusals[row.id])
118
+ ? (openBlock(), createElementBlock("p", _hoisted_8, toDisplayString($data.refusals[row.id]), 1 /* TEXT */))
119
+ : createCommentVNode("v-if", true),
120
+ createVNode(_component_NcButton, {
121
+ disabled: !$options.canSubmit(row.id),
122
+ "data-testid": `cn-decision-list-submit-${row.id}`,
123
+ onClick: $event => ($options.submit(row))
124
+ }, {
125
+ default: withCtx(() => [
126
+ createTextVNode(toDisplayString($options.text($options.submitConfig.label) || $options.t('nextcloud-vue', 'Record this decision')), 1 /* TEXT */)
127
+ ]),
128
+ _: 1 /* STABLE */
129
+ }, 8 /* PROPS */, ["disabled", "data-testid", "onClick"])
130
+ ], 8 /* PROPS */, _hoisted_4))
131
+ }), 128 /* KEYED_FRAGMENT */))
132
+ ]))
133
+ ]))
134
+ }
135
+
136
+ export { render };
137
+ //# sourceMappingURL=CnDecisionListWidget.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnDecisionListWidget.vue3.js","sources":["../../../../src/components/CnDecisionListWidget/CnDecisionListWidget.vue?vue&type=template&id=5cda9d8e&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-decision-list\" data-testid=\"cn-decision-list\">\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"24\" />\n\t\t<NcEmptyContent v-else-if=\"error\"\n\t\t\t:name=\"text(content.errorTitle) || t('nextcloud-vue', 'This list could not be read')\"\n\t\t\t:description=\"error\"\n\t\t\tdata-testid=\"cn-decision-list-error\">\n\t\t\t<template #action>\n\t\t\t\t<NcButton data-testid=\"cn-decision-list-retry\" @click=\"load\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Try again') }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t</NcEmptyContent>\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-decision-list__empty\" data-testid=\"cn-decision-list-empty\">\n\t\t\t{{ text(content.emptyText) || t('nextcloud-vue', 'Nothing to decide right now.') }}\n\t\t</p>\n\t\t<ul v-else class=\"cn-decision-list__rows\">\n\t\t\t<li v-for=\"row in rows\"\n\t\t\t\t:key=\"row.id\"\n\t\t\t\tclass=\"cn-decision-list__row\"\n\t\t\t\t:data-testid=\"`cn-decision-list-row-${row.id}`\">\n\t\t\t\t<p class=\"cn-decision-list__title\">\n\t\t\t\t\t{{ row.title }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"row.meta\" class=\"cn-decision-list__meta\">\n\t\t\t\t\t{{ row.meta }}\n\t\t\t\t</p>\n\t\t\t\t<div class=\"cn-decision-list__fields\">\n\t\t\t\t\t<NcSelect :modelValue=\"answerOption(row.id)\"\n\t\t\t\t\t\t:options=\"answerOptions\"\n\t\t\t\t\t\t:inputLabel=\"text(content.answerLabel) || t('nextcloud-vue', 'Your answer')\"\n\t\t\t\t\t\t:clearable=\"false\"\n\t\t\t\t\t\t:data-testid=\"`cn-decision-list-answer-${row.id}`\"\n\t\t\t\t\t\t@update:modelValue=\"(v) => patch(row.id, { answer: v ? v.id : '' })\" />\n\t\t\t\t\t<NcTextField v-if=\"reasonConfig\"\n\t\t\t\t\t\t:modelValue=\"draft(row.id).reason\"\n\t\t\t\t\t\t:label=\"text(reasonConfig.label) || t('nextcloud-vue', 'Why?')\"\n\t\t\t\t\t\t:data-testid=\"`cn-decision-list-reason-${row.id}`\"\n\t\t\t\t\t\t@update:modelValue=\"(v) => patch(row.id, { reason: String(v ?? '') })\" />\n\t\t\t\t\t<template v-for=\"field in extraFields(row.id)\" :key=\"field.field\">\n\t\t\t\t\t\t<NcDateTimePickerNative v-if=\"field.type === 'date'\"\n\t\t\t\t\t\t\t:modelValue=\"draft(row.id).extra[field.field] || null\"\n\t\t\t\t\t\t\ttype=\"date\"\n\t\t\t\t\t\t\t:label=\"text(field.label) || field.field\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-decision-list-${field.field}-${row.id}`\"\n\t\t\t\t\t\t\t@update:modelValue=\"(v) => setExtra(row.id, field.field, v)\" />\n\t\t\t\t\t\t<NcTextField v-else\n\t\t\t\t\t\t\t:modelValue=\"draft(row.id).extra[field.field] || ''\"\n\t\t\t\t\t\t\t:label=\"text(field.label) || field.field\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-decision-list-${field.field}-${row.id}`\"\n\t\t\t\t\t\t\t@update:modelValue=\"(v) => setExtra(row.id, field.field, v)\" />\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"refusals[row.id]\" class=\"cn-decision-list__refusal\" role=\"alert\">\n\t\t\t\t\t{{ refusals[row.id] }}\n\t\t\t\t</p>\n\t\t\t\t<NcButton :disabled=\"!canSubmit(row.id)\"\n\t\t\t\t\t:data-testid=\"`cn-decision-list-submit-${row.id}`\"\n\t\t\t\t\t@click=\"submit(row)\">\n\t\t\t\t\t{{ text(submitConfig.label) || t('nextcloud-vue', 'Record this decision') }}\n\t\t\t\t</NcButton>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcButton, NcDateTimePickerNative, NcEmptyContent, NcLoadingIcon, NcSelect, NcTextField } from '@nextcloud/vue'\nimport { fetchEndpointSource } from '../../composables/useEndpointSource.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * A date picker value as `YYYY-MM-DD`, or the value as a string.\n *\n * @param {unknown} value The value.\n * @return {string}\n */\nfunction dateValue(value) {\n\tif (value instanceof Date && !Number.isNaN(value.getTime())) {\n\t\tconst pad = (n) => String(n).padStart(2, '0')\n\t\treturn `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`\n\t}\n\treturn value === null || value === undefined ? '' : String(value)\n}\n\n/**\n * CnDecisionListWidget — the `decision-list` widget: a worklist where each\n * row takes one answer from a closed set, a reason, and any fields that\n * answer needs, recorded per row (board DqMijnWerk \"archival reviews\": a\n * verdict, why, and a new date when retained). The row leaves the list once\n * its answer lands, so the user keeps their place. A failed read shows an\n * error with a retry, never an empty list; a refusal stays on its row in the\n * server's words. hermiq approvals, learniq review queues and integriq\n * approvals use the same widget.\n *\n * `content`:\n * - `source`: an endpointSource for the rows (`responsePath`), or `itemsPath`\n * to read them out of the response.\n * - `idField` (default `id`), `title` and `meta`: templates filled from a row.\n * - `answers`: `[{ value, label, fields? }]`; `fields` are `[{ field, type?:\n * \"date\"|\"text\", label?, required? }]` that answer asks for.\n * - `reason`: `{ label?, required?, field? }`, required by default and sent\n * as `field` (default `reason`); `false` drops it.\n * - `submit`: `{ label?, request: { method?, url, body? } }`: `url` and `body`\n * templates filled from the row; the answer goes as `answerField` (default\n * `answer`), with the reason and the answer's fields.\n * - `emptyText`, `errorTitle`, `answerLabel`: texts.\n *\n * Texts go through the host translate.\n */\nexport default {\n\tname: 'CnDecisionListWidget',\n\n\tcomponents: { NcButton, NcDateTimePickerNative, NcEmptyContent, NcLoadingIcon, NcSelect, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate (CnAppRoot `cnTranslate`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\temits: ['decided'],\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\terror: '',\n\t\t\titems: [],\n\t\t\tdrafts: {},\n\t\t\trefusals: {},\n\t\t\tbusy: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {Array<{value: string, label: string, fields?: Array<object>}>} The answers with a value. */\n\t\tanswers() {\n\t\t\treturn (Array.isArray(this.content.answers) ? this.content.answers : []).filter((a) => a && a.value !== undefined && a.value !== '')\n\t\t},\n\n\t\t/** @return {Array<{id: string, label: string}>} The answers as select options. */\n\t\tanswerOptions() {\n\t\t\treturn this.answers.map((a) => ({ id: String(a.value), label: this.text(a.label) || String(a.value) }))\n\t\t},\n\n\t\t/** @return {object|null} The reason config, null when dropped. */\n\t\treasonConfig() {\n\t\t\tif (this.content.reason === false) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn this.content.reason && typeof this.content.reason === 'object' ? this.content.reason : {}\n\t\t},\n\n\t\t/** @return {object} The submit config. */\n\t\tsubmitConfig() {\n\t\t\treturn this.content.submit && typeof this.content.submit === 'object' ? this.content.submit : {}\n\t\t},\n\n\t\t/**\n\t\t * The rows: id, title and meta filled from each item.\n\t\t *\n\t\t * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions\n\t\t * @return {Array<{id: string, title: string, meta: string, item: object}>}\n\t\t */\n\t\trows() {\n\t\t\tconst idField = this.content.idField || 'id'\n\t\t\treturn this.items.map((item) => {\n\t\t\t\tconst id = String(readPath(item, idField) ?? '')\n\t\t\t\treturn {\n\t\t\t\t\tid,\n\t\t\t\t\ttitle: (this.content.title ? fillTemplate(this.cnTranslate(this.content.title), item).trim() : '') || String(item.title || id),\n\t\t\t\t\tmeta: this.content.meta ? fillTemplate(this.cnTranslate(this.content.meta), item).trim() : '',\n\t\t\t\t\titem,\n\t\t\t\t}\n\t\t\t}).filter((row) => row.id !== '')\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * Read the rows; a failure is an error with a retry, never an empty list.\n\t\t *\n\t\t * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst data = await fetchEndpointSource(this.content.source, {}, { force: true })\n\t\t\t\tconst list = this.content.itemsPath ? readPath(data, this.content.itemsPath) : data\n\t\t\t\tthis.items = Array.isArray(list) ? list.filter((item) => item && typeof item === 'object') : []\n\t\t\t} catch (failure) {\n\t\t\t\tconst body = failure && failure.response && failure.response.data\n\t\t\t\tthis.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A row's draft.\n\t\t *\n\t\t * @param {string} id The row id.\n\t\t * @return {{answer: string, reason: string, extra: object}}\n\t\t */\n\t\tdraft(id) {\n\t\t\treturn this.drafts[id] || { answer: '', reason: '', extra: {} }\n\t\t},\n\n\t\t/**\n\t\t * Change a row's draft.\n\t\t *\n\t\t * @param {string} id The row id.\n\t\t * @param {object} change The fields to change.\n\t\t */\n\t\tpatch(id, change) {\n\t\t\tthis.drafts = { ...this.drafts, [id]: { ...this.draft(id), ...change } }\n\t\t},\n\n\t\t/**\n\t\t * Set one of an answer's fields on a row.\n\t\t *\n\t\t * @param {string} id The row id.\n\t\t * @param {string} field The field.\n\t\t * @param {unknown} value The value.\n\t\t */\n\t\tsetExtra(id, field, value) {\n\t\t\tthis.patch(id, { extra: { ...this.draft(id).extra, [field]: dateValue(value) } })\n\t\t},\n\n\t\t/**\n\t\t * @param {string} id The row id.\n\t\t * @return {object|null} The chosen answer as a select option.\n\t\t */\n\t\tanswerOption(id) {\n\t\t\treturn this.answerOptions.find((o) => o.id === this.draft(id).answer) || null\n\t\t},\n\n\t\t/**\n\t\t * The fields the chosen answer asks for.\n\t\t *\n\t\t * @param {string} id The row id.\n\t\t * @return {Array<object>}\n\t\t */\n\t\textraFields(id) {\n\t\t\tconst answer = this.answers.find((a) => String(a.value) === this.draft(id).answer)\n\t\t\treturn answer && Array.isArray(answer.fields) ? answer.fields.filter((f) => f && f.field) : []\n\t\t},\n\n\t\t/**\n\t\t * Whether a row may be sent: an answer, a needed reason, its required fields.\n\t\t *\n\t\t * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions\n\t\t * @param {string} id The row id.\n\t\t * @return {boolean}\n\t\t */\n\t\tcanSubmit(id) {\n\t\t\tconst draft = this.draft(id)\n\t\t\tif (this.busy[id] || !this.answerOptions.some((o) => o.id === draft.answer)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (this.reasonConfig && this.reasonConfig.required !== false && draft.reason.trim() === '') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn this.extraFields(id).every((f) => f.required === false || String(draft.extra[f.field] || '').trim() !== '')\n\t\t},\n\n\t\t/**\n\t\t * Record one row's decision and take it off the list.\n\t\t *\n\t\t * @spec openspec/changes/decision-list-widget/specs/decision-list-widget/spec.md#requirement-a-worklist-of-per-row-decisions\n\t\t * @param {{id: string, item: object}} row The row.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync submit(row) {\n\t\t\tif (!this.canSubmit(row.id)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst draft = this.draft(row.id)\n\t\t\tconst request = this.submitConfig.request || {}\n\t\t\tconst body = {}\n\t\t\tfor (const [key, value] of Object.entries(request.body || {})) {\n\t\t\t\tbody[key] = typeof value === 'string' ? fillTemplate(value, row.item) : value\n\t\t\t}\n\t\t\tbody[this.content.answerField || 'answer'] = draft.answer\n\t\t\tif (this.reasonConfig) {\n\t\t\t\tbody[this.reasonConfig.field || 'reason'] = draft.reason.trim()\n\t\t\t}\n\t\t\tfor (const field of this.extraFields(row.id)) {\n\t\t\t\tif (String(draft.extra[field.field] || '') !== '') {\n\t\t\t\t\tbody[field.field] = draft.extra[field.field]\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.busy = { ...this.busy, [row.id]: true }\n\t\t\tthis.refusals = { ...this.refusals, [row.id]: '' }\n\t\t\ttry {\n\t\t\t\tif (request.url) {\n\t\t\t\t\tconst url = fillTemplate(request.url, row.item)\n\t\t\t\t\tconst method = String(request.method || 'POST').toLowerCase()\n\t\t\t\t\tawait axios[['post', 'put', 'patch'].includes(method) ? method : 'post'](/^https?:\\/\\//i.test(url) ? url : generateUrl(url), body)\n\t\t\t\t}\n\t\t\t\tthis.items = this.items.filter((item) => item !== row.item)\n\t\t\t\t/** @event decided A row's decision landed. Payload: `{ id, item, body }`. */\n\t\t\t\tthis.$emit('decided', { id: row.id, item: row.item, body })\n\t\t\t} catch (failure) {\n\t\t\t\tconst data = failure && failure.response && failure.response.data\n\t\t\t\tthis.refusals = { ...this.refusals, [row.id]: String((data && (data.message || data.error)) || t('nextcloud-vue', 'This decision was not recorded.')) }\n\t\t\t} finally {\n\t\t\t\tthis.busy = { ...this.busy, [row.id]: false }\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-decision-list__empty,\n.cn-decision-list__meta {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-decision-list__rows {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-decision-list__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 6px;\n\tpadding-block: 10px;\n\tborder-block-end: 1px solid var(--color-border);\n}\n\n.cn-decision-list__title {\n\tmargin: 0;\n\tfont-weight: 600;\n}\n\n.cn-decision-list__fields {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n\tgap: 8px;\n}\n\n.cn-decision-list__refusal {\n\tmargin: 0;\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createBlock","_createVNode","_toDisplayString","_createElementVNode","_Fragment","_createTextVNode"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import script from './CnDuplicateWarning.vue2.js';
2
+ import { render } from './CnDuplicateWarning.vue3.js';
3
+
4
+ script.render = render;
5
+ script.__scopeId = "data-v-7cb31ec5";
6
+ const __sfc_main = script;
7
+
8
+ export { __sfc_main as default };
9
+ //# sourceMappingURL=CnDuplicateWarning.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnDuplicateWarning.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}