@conduction/nextcloud-vue 2.119.0 → 2.121.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 (179) hide show
  1. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js +9 -0
  2. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js.map +1 -0
  3. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js +226 -0
  4. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js.map +1 -0
  5. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js +211 -0
  6. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js.map +1 -0
  7. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js +9 -0
  8. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js.map +1 -0
  9. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js +64 -0
  10. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js.map +1 -0
  11. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js +89 -0
  12. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js.map +1 -0
  13. package/dist/esm/components/CnIcon/semanticIcons.js +2 -2
  14. package/dist/esm/components/CnIcon/semanticIcons.js.map +1 -1
  15. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +53 -1
  16. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  17. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +546 -523
  18. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  19. package/dist/esm/components/CnIndexPage/useNamedSource.js +9 -0
  20. package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
  21. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js +9 -0
  22. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js.map +1 -0
  23. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js +257 -0
  24. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js.map +1 -0
  25. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js +266 -0
  26. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js.map +1 -0
  27. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue.js +1 -1
  28. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js +14 -7
  29. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js.map +1 -1
  30. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue3.js.map +1 -1
  31. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js +9 -0
  32. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js.map +1 -0
  33. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js +278 -0
  34. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js.map +1 -0
  35. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js +128 -0
  36. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js.map +1 -0
  37. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js +9 -0
  38. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js.map +1 -0
  39. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js +214 -0
  40. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js.map +1 -0
  41. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js +125 -0
  42. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js.map +1 -0
  43. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js +9 -0
  44. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js.map +1 -0
  45. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js +163 -0
  46. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js.map +1 -0
  47. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js +131 -0
  48. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js.map +1 -0
  49. package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js +127 -0
  50. package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js.map +1 -0
  51. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js +9 -0
  52. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js.map +1 -0
  53. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js +82 -0
  54. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js.map +1 -0
  55. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js +61 -0
  56. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js.map +1 -0
  57. package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js +60 -0
  58. package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js.map +1 -0
  59. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js +10 -0
  60. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js.map +1 -0
  61. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js +226 -0
  62. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js.map +1 -0
  63. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js +126 -0
  64. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js.map +1 -0
  65. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js +10 -0
  66. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js.map +1 -0
  67. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js +944 -0
  68. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js.map +1 -0
  69. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js +276 -0
  70. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js.map +1 -0
  71. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js +9 -0
  72. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js.map +1 -0
  73. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js +354 -0
  74. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js.map +1 -0
  75. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js +392 -0
  76. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js.map +1 -0
  77. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js +9 -0
  78. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js.map +1 -0
  79. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js +180 -0
  80. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js.map +1 -0
  81. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js +208 -0
  82. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js.map +1 -0
  83. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js +9 -0
  84. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js.map +1 -0
  85. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js +244 -0
  86. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js.map +1 -0
  87. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js +76 -0
  88. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js.map +1 -0
  89. package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js +290 -0
  90. package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js.map +1 -0
  91. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js +9 -0
  92. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js.map +1 -0
  93. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js +448 -0
  94. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js.map +1 -0
  95. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js +303 -0
  96. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js.map +1 -0
  97. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +1 -1
  98. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +19 -2
  99. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
  100. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +73 -62
  101. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
  102. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +6 -0
  103. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  104. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +42 -32
  105. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  106. package/dist/esm/composables/indexSources.js +116 -16
  107. package/dist/esm/composables/indexSources.js.map +1 -1
  108. package/dist/esm/index.js +9 -0
  109. package/dist/esm/index.js.map +1 -1
  110. package/dist/esm/l10n/en.json.js +207 -1
  111. package/dist/esm/l10n/en.json.js.map +1 -1
  112. package/dist/esm/l10n/nl.json.js +207 -1
  113. package/dist/esm/l10n/nl.json.js.map +1 -1
  114. package/dist/esm/library-style.js +1 -1
  115. package/dist/esm/nextcloud-vue.css +951 -16
  116. package/dist/esm/utils/aiFeatures.js +245 -0
  117. package/dist/esm/utils/aiFeatures.js.map +1 -0
  118. package/dist/esm/utils/aiSuggestionTracker.js +182 -0
  119. package/dist/esm/utils/aiSuggestionTracker.js.map +1 -0
  120. package/dist/esm/utils/libraryWidgetKeys.js +2 -0
  121. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  122. package/dist/esm/utils/objectPermissions.js +552 -0
  123. package/dist/esm/utils/objectPermissions.js.map +1 -0
  124. package/dist/esm/utils/objectTranslations.js +180 -0
  125. package/dist/esm/utils/objectTranslations.js.map +1 -0
  126. package/dist/esm/utils/stateMachineValidation.js +254 -0
  127. package/dist/esm/utils/stateMachineValidation.js.map +1 -0
  128. package/dist/esm/utils/widgetDispatch.js +3 -1
  129. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  130. package/dist/nextcloud-vue.cjs.js +23038 -14567
  131. package/dist/nextcloud-vue.cjs.js.map +2 -2
  132. package/dist/nextcloud-vue.css +951 -16
  133. package/l10n/en.json +207 -1
  134. package/l10n/nl.json +207 -1
  135. package/package.json +1 -1
  136. package/src/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue +416 -0
  137. package/src/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue +136 -0
  138. package/src/components/CnAiFeaturesWidget/__tests__/CnAiFeaturesWidget.spec.js +213 -0
  139. package/src/components/CnAiFeaturesWidget/index.js +14 -0
  140. package/src/components/CnIndexPage/CnIndexPage.vue +74 -3
  141. package/src/components/CnIndexPage/useNamedSource.js +9 -0
  142. package/src/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue +499 -0
  143. package/src/components/CnObjectPermissionsTab/__tests__/CnObjectPermissionsTab.spec.js +186 -0
  144. package/src/components/CnObjectPermissionsTab/index.js +13 -0
  145. package/src/components/CnObjectSidebar/CnObjectSidebar.vue +8 -1
  146. package/src/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue +424 -0
  147. package/src/components/CnObjectTranslationsWidget/__tests__/CnObjectTranslationsWidget.spec.js +189 -0
  148. package/src/components/CnObjectTranslationsWidget/index.js +13 -0
  149. package/src/components/CnReadinessChecklist/CnReadinessChecklist.vue +335 -0
  150. package/src/components/CnReadinessChecklist/index.js +9 -0
  151. package/src/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue +300 -0
  152. package/src/components/CnSchemaRulesWidget/index.js +10 -0
  153. package/src/components/CnSchemaRulesWidget/schemaRules.js +123 -0
  154. package/src/components/CnSearchIndexStatus/CnSearchIndexStatus.vue +160 -0
  155. package/src/components/CnSearchIndexStatus/index.js +10 -0
  156. package/src/components/CnSearchIndexStatus/searchIndexStatus.js +56 -0
  157. package/src/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue +328 -0
  158. package/src/components/CnShippedConfigurationSection/index.js +9 -0
  159. package/src/components/CnStateMachineEditor/CnStateMachineEditor.vue +1332 -0
  160. package/src/components/CnStateMachineEditor/CnStateMachineFields.vue +791 -0
  161. package/src/components/CnStateMachineEditor/CnStateMachineNode.vue +442 -0
  162. package/src/components/CnStateMachineEditor/CnStateMachinePanel.vue +326 -0
  163. package/src/components/CnStateMachineEditor/index.js +8 -0
  164. package/src/components/CnStateMachineEditor/stateMachineFields.js +287 -0
  165. package/src/components/CnTimelineWidget/CnTimelineManage.vue +827 -0
  166. package/src/components/CnTimelineWidget/CnTimelineWidget.vue +29 -3
  167. package/src/components/CnWidgetGrid/builtInWidgets.js +6 -0
  168. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +10 -0
  169. package/src/components/index.js +8 -0
  170. package/src/composables/indexSources.js +116 -16
  171. package/src/css/index-page.css +7 -0
  172. package/src/index.js +9 -0
  173. package/src/utils/aiFeatures.js +241 -0
  174. package/src/utils/aiSuggestionTracker.js +178 -0
  175. package/src/utils/libraryWidgetKeys.js +2 -0
  176. package/src/utils/objectPermissions.js +548 -0
  177. package/src/utils/objectTranslations.js +176 -0
  178. package/src/utils/stateMachineValidation.js +250 -0
  179. package/src/utils/widgetDispatch.js +3 -1
@@ -0,0 +1,214 @@
1
+ import axios__default from '@nextcloud/axios';
2
+ import { translate } from '@nextcloud/l10n';
3
+ import { generateUrl } from '@nextcloud/router';
4
+ import { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue';
5
+ import { readPath } from '../../utils/readPath.js';
6
+
7
+ /**
8
+ * The fields an item is read from by default.
9
+ *
10
+ * @type {Readonly<Record<string, string>>}
11
+ */
12
+ const DEFAULT_FIELDS = Object.freeze({
13
+ id: 'id',
14
+ label: 'label',
15
+ explanation: 'explanation',
16
+ met: 'met',
17
+ fixHref: 'fixHref',
18
+ failure: 'failure',
19
+ group: 'group',
20
+ blocking: 'blocking',
21
+ });
22
+
23
+ /**
24
+ * CnReadinessChecklist: a list of things an instance needs, each with its
25
+ * state in words (met, open, unknown), an explanation and a link to the
26
+ * screen that fixes it. Reported, never gated: nothing here blocks the app.
27
+ *
28
+ * Items come from `items`, or are read from an app endpoint (`source`, an app
29
+ * path resolved with `generateUrl`, the list at `itemsPath`) and re-read when
30
+ * the reader comes back to the tab. `fields` maps an item's keys onto the
31
+ * row's (`{ label: 'title', met: 'done', fixHref: 'screen' }`). Items carrying
32
+ * a `group` render under that group's heading (`groups`, in order).
33
+ *
34
+ * @spec openspec/changes/readiness-checklist/specs/readiness-checklist/spec.md
35
+ */
36
+ var script = {
37
+ name: 'CnReadinessChecklist',
38
+
39
+ components: { NcLoadingIcon, NcNoteCard },
40
+
41
+ inject: {
42
+ /** The host app's translate, for texts the page declares. */
43
+ cnTranslate: { default: () => (key) => key },
44
+ },
45
+
46
+ props: {
47
+ /** The items: `{ id, label, explanation?, met (true|false|null), fixHref?, failure?, group?, blocking? }`. */
48
+ items: { type: Array, default: () => [] },
49
+ /** An app path to read the items from (`/apps/<app>/api/setup/status`). */
50
+ source: { type: String, default: '' },
51
+ /** Where the items sit in the answer. */
52
+ itemsPath: { type: String, default: 'items' },
53
+ /** Item key to row key, over the defaults. */
54
+ fields: { type: Object, default: () => ({}) },
55
+ /** Headings for grouped items, in order: `[{ id, title }]`. */
56
+ groups: { type: Array, default: () => [] },
57
+ /** A line above the list. */
58
+ description: { type: String, default: '' },
59
+ /** The words for each state: `{ met, unmet, unknown }`. */
60
+ statusLabels: { type: Object, default: () => ({}) },
61
+ /** Said when there is nothing to check. */
62
+ emptyText: { type: String, default: '' },
63
+ /** Said when the source could not be read. */
64
+ errorText: { type: String, default: '' },
65
+ /** Said before the unmet items marked `blocking` ("Cannot run until this is installed:"). */
66
+ blockingText: { type: String, default: '' },
67
+ },
68
+
69
+ emits: ['loaded'],
70
+
71
+ data() {
72
+ return {
73
+ loading: false,
74
+ loadError: '',
75
+ loadedItems: null,
76
+ }
77
+ },
78
+
79
+ computed: {
80
+ /** @return {Array<object>} The items, normalised. */
81
+ shownItems() {
82
+ const raw = this.loadedItems !== null ? this.loadedItems : this.items;
83
+ const map = { ...DEFAULT_FIELDS, ...(this.fields || {}) };
84
+ return (Array.isArray(raw) ? raw : []).map((row, index) => {
85
+ const read = (key) => readPath(row, map[key]);
86
+ const met = read('met');
87
+ return {
88
+ id: String(read('id') ?? index),
89
+ label: this.text(read('label') ?? read('id') ?? ''),
90
+ explanation: this.text(read('explanation') ?? ''),
91
+ met: met === true || met === false ? met : null,
92
+ fixHref: typeof read('fixHref') === 'string' ? read('fixHref') : '',
93
+ failure: read('failure') ? String(read('failure')) : '',
94
+ group: read('group') ? String(read('group')) : '',
95
+ blocking: read('blocking') === true,
96
+ }
97
+ })
98
+ },
99
+
100
+ /** @return {Array<object>} The unmet blocking items. */
101
+ blockingItems() {
102
+ return this.shownItems.filter((item) => item.blocking && item.met !== true)
103
+ },
104
+
105
+ /** @return {Array<{id: string, title: string, items: Array<object>}>} The groups that hold items, in order. */
106
+ shownGroups() {
107
+ const declared = (Array.isArray(this.groups) ? this.groups : []).map((group) => ({
108
+ id: String(group.id),
109
+ title: this.text(group.title || ''),
110
+ items: this.shownItems.filter((item) => item.group === String(group.id)),
111
+ }));
112
+ const known = new Set(declared.map((group) => group.id));
113
+ const rest = this.shownItems.filter((item) => !known.has(item.group));
114
+ const all = rest.length > 0 ? [{ id: 'items', title: '', items: rest }, ...declared] : declared;
115
+ return all.filter((group) => group.items.length > 0)
116
+ },
117
+ },
118
+
119
+ watch: {
120
+ source() {
121
+ this.reload();
122
+ },
123
+ },
124
+
125
+ mounted() {
126
+ this.reload();
127
+ window.addEventListener('focus', this.onFocus);
128
+ },
129
+
130
+ beforeUnmount() {
131
+ window.removeEventListener('focus', this.onFocus);
132
+ },
133
+
134
+ methods: {
135
+ t: translate,
136
+
137
+ /**
138
+ * A declared text through the host's translate.
139
+ *
140
+ * @param {string} value The text.
141
+ * @return {string} The translated text.
142
+ */
143
+ text(value) {
144
+ return value ? String(this.cnTranslate(String(value))) : ''
145
+ },
146
+
147
+ /**
148
+ * @param {object} item The item.
149
+ * @return {string} `met`, `unmet` or `unknown`.
150
+ */
151
+ stateOf(item) {
152
+ if (item.met === true) {
153
+ return 'met'
154
+ }
155
+ return item.met === false ? 'unmet' : 'unknown'
156
+ },
157
+
158
+ /**
159
+ * The word for an item's state.
160
+ *
161
+ * @param {object} item The item.
162
+ * @return {string} The word.
163
+ */
164
+ stateLabel(item) {
165
+ const state = this.stateOf(item);
166
+ const own = this.statusLabels && this.statusLabels[state];
167
+ if (own) {
168
+ return this.text(own)
169
+ }
170
+ return {
171
+ met: translate('nextcloud-vue', 'Done'),
172
+ unmet: translate('nextcloud-vue', 'Still open'),
173
+ unknown: translate('nextcloud-vue', 'Unknown'),
174
+ }[state]
175
+ },
176
+
177
+ /**
178
+ * Read the items again, from the source. The source asks the instance
179
+ * rather than a stored flag, so a fix made elsewhere shows on return.
180
+ *
181
+ * @return {Promise<void>}
182
+ */
183
+ async reload() {
184
+ if (!this.source) {
185
+ this.loadedItems = null;
186
+ return
187
+ }
188
+ this.loading = this.loadedItems === null;
189
+ this.loadError = '';
190
+ try {
191
+ const { data } = await axios__default.get(generateUrl(this.source));
192
+ const rows = Array.isArray(data) ? data : readPath(data, this.itemsPath);
193
+ this.loadedItems = Array.isArray(rows) ? rows : [];
194
+ /** @event loaded The source answered. Payload: the whole answer. */
195
+ this.$emit('loaded', data);
196
+ } catch {
197
+ this.loadedItems = [];
198
+ this.loadError = this.errorText ? this.text(this.errorText) : translate('nextcloud-vue', 'The checklist could not be read.');
199
+ } finally {
200
+ this.loading = false;
201
+ }
202
+ },
203
+
204
+ /** Re-read when the reader comes back from fixing something. */
205
+ onFocus() {
206
+ if (this.source) {
207
+ this.reload();
208
+ }
209
+ },
210
+ },
211
+ };
212
+
213
+ export { script as default };
214
+ //# sourceMappingURL=CnReadinessChecklist.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnReadinessChecklist.vue2.js","sources":["../../../../src/components/CnReadinessChecklist/CnReadinessChecklist.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-readiness-checklist\" data-testid=\"cn-readiness-checklist\">\n\t\t<p v-if=\"description\" class=\"cn-readiness-checklist__description\">\n\t\t\t{{ description }}\n\t\t</p>\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"32\" :name=\"t('nextcloud-vue', 'Loading')\" />\n\n\t\t<NcNoteCard v-else-if=\"loadError\" type=\"error\" data-testid=\"cn-readiness-checklist-error\">\n\t\t\t{{ loadError }}\n\t\t</NcNoteCard>\n\n\t\t<template v-else>\n\t\t\t<NcNoteCard v-if=\"blockingText && blockingItems.length > 0\"\n\t\t\t\ttype=\"error\"\n\t\t\t\tdata-testid=\"cn-readiness-checklist-blocking\">\n\t\t\t\t{{ blockingText }} {{ blockingItems.map((item) => item.label).join(', ') }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"shownItems.length === 0\" class=\"cn-readiness-checklist__empty\" data-testid=\"cn-readiness-checklist-empty\">\n\t\t\t\t{{ emptyText || t('nextcloud-vue', 'Nothing to check.') }}\n\t\t\t</p>\n\n\t\t\t<section v-for=\"group in shownGroups\"\n\t\t\t\t:key=\"group.id\"\n\t\t\t\tclass=\"cn-readiness-checklist__group\"\n\t\t\t\t:data-testid=\"`cn-readiness-checklist-group-${group.id}`\">\n\t\t\t\t<h4 v-if=\"group.title\" class=\"cn-readiness-checklist__group-title\">\n\t\t\t\t\t{{ group.title }}\n\t\t\t\t</h4>\n\t\t\t\t<ul class=\"cn-readiness-checklist__list\">\n\t\t\t\t\t<li v-for=\"item in group.items\"\n\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\tclass=\"cn-readiness-checklist__item\"\n\t\t\t\t\t\t:data-testid=\"`cn-readiness-item-${item.id}`\">\n\t\t\t\t\t\t<!-- The state is a word, not a colour (WCAG 1.4.1); the\n\t\t\t\t\t\t colour repeats it. -->\n\t\t\t\t\t\t<span class=\"cn-readiness-checklist__state\"\n\t\t\t\t\t\t\t:class=\"`cn-readiness-checklist__state--${stateOf(item)}`\"\n\t\t\t\t\t\t\tdata-testid=\"cn-readiness-item-state\">\n\t\t\t\t\t\t\t{{ stateLabel(item) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-readiness-checklist__body\">\n\t\t\t\t\t\t\t<a v-if=\"item.fixHref\"\n\t\t\t\t\t\t\t\t:href=\"item.fixHref\"\n\t\t\t\t\t\t\t\tclass=\"cn-readiness-checklist__label\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-readiness-item-fix\">{{ item.label }}</a>\n\t\t\t\t\t\t\t<span v-else class=\"cn-readiness-checklist__label\">{{ item.label }}</span>\n\t\t\t\t\t\t\t<span v-if=\"item.explanation\" class=\"cn-readiness-checklist__explanation\">{{ item.explanation }}</span>\n\t\t\t\t\t\t\t<span v-if=\"item.failure\" class=\"cn-readiness-checklist__failure\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'This could not be read: {reason}', { reason: item.failure }) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t</template>\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 { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * The fields an item is read from by default.\n *\n * @type {Readonly<Record<string, string>>}\n */\nconst DEFAULT_FIELDS = Object.freeze({\n\tid: 'id',\n\tlabel: 'label',\n\texplanation: 'explanation',\n\tmet: 'met',\n\tfixHref: 'fixHref',\n\tfailure: 'failure',\n\tgroup: 'group',\n\tblocking: 'blocking',\n})\n\n/**\n * CnReadinessChecklist: a list of things an instance needs, each with its\n * state in words (met, open, unknown), an explanation and a link to the\n * screen that fixes it. Reported, never gated: nothing here blocks the app.\n *\n * Items come from `items`, or are read from an app endpoint (`source`, an app\n * path resolved with `generateUrl`, the list at `itemsPath`) and re-read when\n * the reader comes back to the tab. `fields` maps an item's keys onto the\n * row's (`{ label: 'title', met: 'done', fixHref: 'screen' }`). Items carrying\n * a `group` render under that group's heading (`groups`, in order).\n *\n * @spec openspec/changes/readiness-checklist/specs/readiness-checklist/spec.md\n */\nexport default {\n\tname: 'CnReadinessChecklist',\n\n\tcomponents: { NcLoadingIcon, NcNoteCard },\n\n\tinject: {\n\t\t/** The host app's translate, for texts the page declares. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The items: `{ id, label, explanation?, met (true|false|null), fixHref?, failure?, group?, blocking? }`. */\n\t\titems: { type: Array, default: () => [] },\n\t\t/** An app path to read the items from (`/apps/<app>/api/setup/status`). */\n\t\tsource: { type: String, default: '' },\n\t\t/** Where the items sit in the answer. */\n\t\titemsPath: { type: String, default: 'items' },\n\t\t/** Item key to row key, over the defaults. */\n\t\tfields: { type: Object, default: () => ({}) },\n\t\t/** Headings for grouped items, in order: `[{ id, title }]`. */\n\t\tgroups: { type: Array, default: () => [] },\n\t\t/** A line above the list. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** The words for each state: `{ met, unmet, unknown }`. */\n\t\tstatusLabels: { type: Object, default: () => ({}) },\n\t\t/** Said when there is nothing to check. */\n\t\temptyText: { type: String, default: '' },\n\t\t/** Said when the source could not be read. */\n\t\terrorText: { type: String, default: '' },\n\t\t/** Said before the unmet items marked `blocking` (\"Cannot run until this is installed:\"). */\n\t\tblockingText: { type: String, default: '' },\n\t},\n\n\temits: ['loaded'],\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: false,\n\t\t\tloadError: '',\n\t\t\tloadedItems: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {Array<object>} The items, normalised. */\n\t\tshownItems() {\n\t\t\tconst raw = this.loadedItems !== null ? this.loadedItems : this.items\n\t\t\tconst map = { ...DEFAULT_FIELDS, ...(this.fields || {}) }\n\t\t\treturn (Array.isArray(raw) ? raw : []).map((row, index) => {\n\t\t\t\tconst read = (key) => readPath(row, map[key])\n\t\t\t\tconst met = read('met')\n\t\t\t\treturn {\n\t\t\t\t\tid: String(read('id') ?? index),\n\t\t\t\t\tlabel: this.text(read('label') ?? read('id') ?? ''),\n\t\t\t\t\texplanation: this.text(read('explanation') ?? ''),\n\t\t\t\t\tmet: met === true || met === false ? met : null,\n\t\t\t\t\tfixHref: typeof read('fixHref') === 'string' ? read('fixHref') : '',\n\t\t\t\t\tfailure: read('failure') ? String(read('failure')) : '',\n\t\t\t\t\tgroup: read('group') ? String(read('group')) : '',\n\t\t\t\t\tblocking: read('blocking') === true,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {Array<object>} The unmet blocking items. */\n\t\tblockingItems() {\n\t\t\treturn this.shownItems.filter((item) => item.blocking && item.met !== true)\n\t\t},\n\n\t\t/** @return {Array<{id: string, title: string, items: Array<object>}>} The groups that hold items, in order. */\n\t\tshownGroups() {\n\t\t\tconst declared = (Array.isArray(this.groups) ? this.groups : []).map((group) => ({\n\t\t\t\tid: String(group.id),\n\t\t\t\ttitle: this.text(group.title || ''),\n\t\t\t\titems: this.shownItems.filter((item) => item.group === String(group.id)),\n\t\t\t}))\n\t\t\tconst known = new Set(declared.map((group) => group.id))\n\t\t\tconst rest = this.shownItems.filter((item) => !known.has(item.group))\n\t\t\tconst all = rest.length > 0 ? [{ id: 'items', title: '', items: rest }, ...declared] : declared\n\t\t\treturn all.filter((group) => group.items.length > 0)\n\t\t},\n\t},\n\n\twatch: {\n\t\tsource() {\n\t\t\tthis.reload()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.reload()\n\t\twindow.addEventListener('focus', this.onFocus)\n\t},\n\n\tbeforeUnmount() {\n\t\twindow.removeEventListener('focus', this.onFocus)\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A declared text through the host's translate.\n\t\t *\n\t\t * @param {string} value The text.\n\t\t * @return {string} The translated text.\n\t\t */\n\t\ttext(value) {\n\t\t\treturn value ? String(this.cnTranslate(String(value))) : ''\n\t\t},\n\n\t\t/**\n\t\t * @param {object} item The item.\n\t\t * @return {string} `met`, `unmet` or `unknown`.\n\t\t */\n\t\tstateOf(item) {\n\t\t\tif (item.met === true) {\n\t\t\t\treturn 'met'\n\t\t\t}\n\t\t\treturn item.met === false ? 'unmet' : 'unknown'\n\t\t},\n\n\t\t/**\n\t\t * The word for an item's state.\n\t\t *\n\t\t * @param {object} item The item.\n\t\t * @return {string} The word.\n\t\t */\n\t\tstateLabel(item) {\n\t\t\tconst state = this.stateOf(item)\n\t\t\tconst own = this.statusLabels && this.statusLabels[state]\n\t\t\tif (own) {\n\t\t\t\treturn this.text(own)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tmet: t('nextcloud-vue', 'Done'),\n\t\t\t\tunmet: t('nextcloud-vue', 'Still open'),\n\t\t\t\tunknown: t('nextcloud-vue', 'Unknown'),\n\t\t\t}[state]\n\t\t},\n\n\t\t/**\n\t\t * Read the items again, from the source. The source asks the instance\n\t\t * rather than a stored flag, so a fix made elsewhere shows on return.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync reload() {\n\t\t\tif (!this.source) {\n\t\t\t\tthis.loadedItems = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = this.loadedItems === null\n\t\t\tthis.loadError = ''\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(generateUrl(this.source))\n\t\t\t\tconst rows = Array.isArray(data) ? data : readPath(data, this.itemsPath)\n\t\t\t\tthis.loadedItems = Array.isArray(rows) ? rows : []\n\t\t\t\t/** @event loaded The source answered. Payload: the whole answer. */\n\t\t\t\tthis.$emit('loaded', data)\n\t\t\t} catch {\n\t\t\t\tthis.loadedItems = []\n\t\t\t\tthis.loadError = this.errorText ? this.text(this.errorText) : t('nextcloud-vue', 'The checklist could not be read.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/** Re-read when the reader comes back from fixing something. */\n\t\tonFocus() {\n\t\t\tif (this.source) {\n\t\t\t\tthis.reload()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-readiness-checklist__description,\n.cn-readiness-checklist__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0 0 12px;\n}\n\n.cn-readiness-checklist__group-title {\n\tmargin: 12px 0 4px;\n}\n\n.cn-readiness-checklist__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-readiness-checklist__item {\n\tdisplay: flex;\n\tgap: calc(var(--default-grid-baseline, 4px) * 3);\n\tpadding-block: calc(var(--default-grid-baseline, 4px) * 2);\n\tborder-block-end: 1px solid var(--color-border);\n}\n\n.cn-readiness-checklist__state {\n\tflex: 0 0 auto;\n\tmin-width: 6em;\n\tfont-weight: bold;\n}\n\n.cn-readiness-checklist__state--met {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-readiness-checklist__state--unmet {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-readiness-checklist__state--unknown {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-readiness-checklist__body {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-readiness-checklist__explanation {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-readiness-checklist__failure {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["t","axios"],"mappings":"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"}
@@ -0,0 +1,125 @@
1
+ import { resolveComponent, openBlock, createElementBlock, toDisplayString, createCommentVNode, createBlock, withCtx, createTextVNode, Fragment, renderList, createElementVNode, normalizeClass } from 'vue';
2
+
3
+ const _hoisted_1 = {
4
+ class: "cn-readiness-checklist",
5
+ "data-testid": "cn-readiness-checklist"
6
+ };
7
+ const _hoisted_2 = {
8
+ key: 0,
9
+ class: "cn-readiness-checklist__description"
10
+ };
11
+ const _hoisted_3 = {
12
+ key: 1,
13
+ class: "cn-readiness-checklist__empty",
14
+ "data-testid": "cn-readiness-checklist-empty"
15
+ };
16
+ const _hoisted_4 = ["data-testid"];
17
+ const _hoisted_5 = {
18
+ key: 0,
19
+ class: "cn-readiness-checklist__group-title"
20
+ };
21
+ const _hoisted_6 = { class: "cn-readiness-checklist__list" };
22
+ const _hoisted_7 = ["data-testid"];
23
+ const _hoisted_8 = { class: "cn-readiness-checklist__body" };
24
+ const _hoisted_9 = ["href"];
25
+ const _hoisted_10 = {
26
+ key: 1,
27
+ class: "cn-readiness-checklist__label"
28
+ };
29
+ const _hoisted_11 = {
30
+ key: 2,
31
+ class: "cn-readiness-checklist__explanation"
32
+ };
33
+ const _hoisted_12 = {
34
+ key: 3,
35
+ class: "cn-readiness-checklist__failure"
36
+ };
37
+
38
+ function render(_ctx, _cache, $props, $setup, $data, $options) {
39
+ const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
40
+ const _component_NcNoteCard = resolveComponent("NcNoteCard");
41
+
42
+ return (openBlock(), createElementBlock("div", _hoisted_1, [
43
+ ($props.description)
44
+ ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($props.description), 1 /* TEXT */))
45
+ : createCommentVNode("v-if", true),
46
+ ($data.loading)
47
+ ? (openBlock(), createBlock(_component_NcLoadingIcon, {
48
+ key: 1,
49
+ size: 32,
50
+ name: $options.t('nextcloud-vue', 'Loading')
51
+ }, null, 8 /* PROPS */, ["name"]))
52
+ : ($data.loadError)
53
+ ? (openBlock(), createBlock(_component_NcNoteCard, {
54
+ key: 2,
55
+ type: "error",
56
+ "data-testid": "cn-readiness-checklist-error"
57
+ }, {
58
+ default: withCtx(() => [
59
+ createTextVNode(toDisplayString($data.loadError), 1 /* TEXT */)
60
+ ]),
61
+ _: 1 /* STABLE */
62
+ }))
63
+ : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
64
+ ($props.blockingText && $options.blockingItems.length > 0)
65
+ ? (openBlock(), createBlock(_component_NcNoteCard, {
66
+ key: 0,
67
+ type: "error",
68
+ "data-testid": "cn-readiness-checklist-blocking"
69
+ }, {
70
+ default: withCtx(() => [
71
+ createTextVNode(toDisplayString($props.blockingText) + " " + toDisplayString($options.blockingItems.map((item) => item.label).join(', ')), 1 /* TEXT */)
72
+ ]),
73
+ _: 1 /* STABLE */
74
+ }))
75
+ : createCommentVNode("v-if", true),
76
+ ($options.shownItems.length === 0)
77
+ ? (openBlock(), createElementBlock("p", _hoisted_3, toDisplayString($props.emptyText || $options.t('nextcloud-vue', 'Nothing to check.')), 1 /* TEXT */))
78
+ : createCommentVNode("v-if", true),
79
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.shownGroups, (group) => {
80
+ return (openBlock(), createElementBlock("section", {
81
+ key: group.id,
82
+ class: "cn-readiness-checklist__group",
83
+ "data-testid": `cn-readiness-checklist-group-${group.id}`
84
+ }, [
85
+ (group.title)
86
+ ? (openBlock(), createElementBlock("h4", _hoisted_5, toDisplayString(group.title), 1 /* TEXT */))
87
+ : createCommentVNode("v-if", true),
88
+ createElementVNode("ul", _hoisted_6, [
89
+ (openBlock(true), createElementBlock(Fragment, null, renderList(group.items, (item) => {
90
+ return (openBlock(), createElementBlock("li", {
91
+ key: item.id,
92
+ class: "cn-readiness-checklist__item",
93
+ "data-testid": `cn-readiness-item-${item.id}`
94
+ }, [
95
+ createElementVNode("span", {
96
+ class: normalizeClass(["cn-readiness-checklist__state", `cn-readiness-checklist__state--${$options.stateOf(item)}`]),
97
+ "data-testid": "cn-readiness-item-state"
98
+ }, toDisplayString($options.stateLabel(item)), 3 /* TEXT, CLASS */),
99
+ createElementVNode("span", _hoisted_8, [
100
+ (item.fixHref)
101
+ ? (openBlock(), createElementBlock("a", {
102
+ key: 0,
103
+ href: item.fixHref,
104
+ class: "cn-readiness-checklist__label",
105
+ "data-testid": "cn-readiness-item-fix"
106
+ }, toDisplayString(item.label), 9 /* TEXT, PROPS */, _hoisted_9))
107
+ : (openBlock(), createElementBlock("span", _hoisted_10, toDisplayString(item.label), 1 /* TEXT */)),
108
+ (item.explanation)
109
+ ? (openBlock(), createElementBlock("span", _hoisted_11, toDisplayString(item.explanation), 1 /* TEXT */))
110
+ : createCommentVNode("v-if", true),
111
+ (item.failure)
112
+ ? (openBlock(), createElementBlock("span", _hoisted_12, toDisplayString($options.t('nextcloud-vue', 'This could not be read: {reason}', { reason: item.failure })), 1 /* TEXT */))
113
+ : createCommentVNode("v-if", true)
114
+ ])
115
+ ], 8 /* PROPS */, _hoisted_7))
116
+ }), 128 /* KEYED_FRAGMENT */))
117
+ ])
118
+ ], 8 /* PROPS */, _hoisted_4))
119
+ }), 128 /* KEYED_FRAGMENT */))
120
+ ], 64 /* STABLE_FRAGMENT */))
121
+ ]))
122
+ }
123
+
124
+ export { render };
125
+ //# sourceMappingURL=CnReadinessChecklist.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnReadinessChecklist.vue3.js","sources":["../../../../src/components/CnReadinessChecklist/CnReadinessChecklist.vue?vue&type=template&id=4e10a556&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-readiness-checklist\" data-testid=\"cn-readiness-checklist\">\n\t\t<p v-if=\"description\" class=\"cn-readiness-checklist__description\">\n\t\t\t{{ description }}\n\t\t</p>\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"32\" :name=\"t('nextcloud-vue', 'Loading')\" />\n\n\t\t<NcNoteCard v-else-if=\"loadError\" type=\"error\" data-testid=\"cn-readiness-checklist-error\">\n\t\t\t{{ loadError }}\n\t\t</NcNoteCard>\n\n\t\t<template v-else>\n\t\t\t<NcNoteCard v-if=\"blockingText && blockingItems.length > 0\"\n\t\t\t\ttype=\"error\"\n\t\t\t\tdata-testid=\"cn-readiness-checklist-blocking\">\n\t\t\t\t{{ blockingText }} {{ blockingItems.map((item) => item.label).join(', ') }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<p v-if=\"shownItems.length === 0\" class=\"cn-readiness-checklist__empty\" data-testid=\"cn-readiness-checklist-empty\">\n\t\t\t\t{{ emptyText || t('nextcloud-vue', 'Nothing to check.') }}\n\t\t\t</p>\n\n\t\t\t<section v-for=\"group in shownGroups\"\n\t\t\t\t:key=\"group.id\"\n\t\t\t\tclass=\"cn-readiness-checklist__group\"\n\t\t\t\t:data-testid=\"`cn-readiness-checklist-group-${group.id}`\">\n\t\t\t\t<h4 v-if=\"group.title\" class=\"cn-readiness-checklist__group-title\">\n\t\t\t\t\t{{ group.title }}\n\t\t\t\t</h4>\n\t\t\t\t<ul class=\"cn-readiness-checklist__list\">\n\t\t\t\t\t<li v-for=\"item in group.items\"\n\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\tclass=\"cn-readiness-checklist__item\"\n\t\t\t\t\t\t:data-testid=\"`cn-readiness-item-${item.id}`\">\n\t\t\t\t\t\t<!-- The state is a word, not a colour (WCAG 1.4.1); the\n\t\t\t\t\t\t colour repeats it. -->\n\t\t\t\t\t\t<span class=\"cn-readiness-checklist__state\"\n\t\t\t\t\t\t\t:class=\"`cn-readiness-checklist__state--${stateOf(item)}`\"\n\t\t\t\t\t\t\tdata-testid=\"cn-readiness-item-state\">\n\t\t\t\t\t\t\t{{ stateLabel(item) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span class=\"cn-readiness-checklist__body\">\n\t\t\t\t\t\t\t<a v-if=\"item.fixHref\"\n\t\t\t\t\t\t\t\t:href=\"item.fixHref\"\n\t\t\t\t\t\t\t\tclass=\"cn-readiness-checklist__label\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-readiness-item-fix\">{{ item.label }}</a>\n\t\t\t\t\t\t\t<span v-else class=\"cn-readiness-checklist__label\">{{ item.label }}</span>\n\t\t\t\t\t\t\t<span v-if=\"item.explanation\" class=\"cn-readiness-checklist__explanation\">{{ item.explanation }}</span>\n\t\t\t\t\t\t\t<span v-if=\"item.failure\" class=\"cn-readiness-checklist__failure\">\n\t\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'This could not be read: {reason}', { reason: item.failure }) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</section>\n\t\t</template>\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 { NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { readPath } from '../../utils/readPath.js'\n\n/**\n * The fields an item is read from by default.\n *\n * @type {Readonly<Record<string, string>>}\n */\nconst DEFAULT_FIELDS = Object.freeze({\n\tid: 'id',\n\tlabel: 'label',\n\texplanation: 'explanation',\n\tmet: 'met',\n\tfixHref: 'fixHref',\n\tfailure: 'failure',\n\tgroup: 'group',\n\tblocking: 'blocking',\n})\n\n/**\n * CnReadinessChecklist: a list of things an instance needs, each with its\n * state in words (met, open, unknown), an explanation and a link to the\n * screen that fixes it. Reported, never gated: nothing here blocks the app.\n *\n * Items come from `items`, or are read from an app endpoint (`source`, an app\n * path resolved with `generateUrl`, the list at `itemsPath`) and re-read when\n * the reader comes back to the tab. `fields` maps an item's keys onto the\n * row's (`{ label: 'title', met: 'done', fixHref: 'screen' }`). Items carrying\n * a `group` render under that group's heading (`groups`, in order).\n *\n * @spec openspec/changes/readiness-checklist/specs/readiness-checklist/spec.md\n */\nexport default {\n\tname: 'CnReadinessChecklist',\n\n\tcomponents: { NcLoadingIcon, NcNoteCard },\n\n\tinject: {\n\t\t/** The host app's translate, for texts the page declares. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The items: `{ id, label, explanation?, met (true|false|null), fixHref?, failure?, group?, blocking? }`. */\n\t\titems: { type: Array, default: () => [] },\n\t\t/** An app path to read the items from (`/apps/<app>/api/setup/status`). */\n\t\tsource: { type: String, default: '' },\n\t\t/** Where the items sit in the answer. */\n\t\titemsPath: { type: String, default: 'items' },\n\t\t/** Item key to row key, over the defaults. */\n\t\tfields: { type: Object, default: () => ({}) },\n\t\t/** Headings for grouped items, in order: `[{ id, title }]`. */\n\t\tgroups: { type: Array, default: () => [] },\n\t\t/** A line above the list. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** The words for each state: `{ met, unmet, unknown }`. */\n\t\tstatusLabels: { type: Object, default: () => ({}) },\n\t\t/** Said when there is nothing to check. */\n\t\temptyText: { type: String, default: '' },\n\t\t/** Said when the source could not be read. */\n\t\terrorText: { type: String, default: '' },\n\t\t/** Said before the unmet items marked `blocking` (\"Cannot run until this is installed:\"). */\n\t\tblockingText: { type: String, default: '' },\n\t},\n\n\temits: ['loaded'],\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: false,\n\t\t\tloadError: '',\n\t\t\tloadedItems: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {Array<object>} The items, normalised. */\n\t\tshownItems() {\n\t\t\tconst raw = this.loadedItems !== null ? this.loadedItems : this.items\n\t\t\tconst map = { ...DEFAULT_FIELDS, ...(this.fields || {}) }\n\t\t\treturn (Array.isArray(raw) ? raw : []).map((row, index) => {\n\t\t\t\tconst read = (key) => readPath(row, map[key])\n\t\t\t\tconst met = read('met')\n\t\t\t\treturn {\n\t\t\t\t\tid: String(read('id') ?? index),\n\t\t\t\t\tlabel: this.text(read('label') ?? read('id') ?? ''),\n\t\t\t\t\texplanation: this.text(read('explanation') ?? ''),\n\t\t\t\t\tmet: met === true || met === false ? met : null,\n\t\t\t\t\tfixHref: typeof read('fixHref') === 'string' ? read('fixHref') : '',\n\t\t\t\t\tfailure: read('failure') ? String(read('failure')) : '',\n\t\t\t\t\tgroup: read('group') ? String(read('group')) : '',\n\t\t\t\t\tblocking: read('blocking') === true,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {Array<object>} The unmet blocking items. */\n\t\tblockingItems() {\n\t\t\treturn this.shownItems.filter((item) => item.blocking && item.met !== true)\n\t\t},\n\n\t\t/** @return {Array<{id: string, title: string, items: Array<object>}>} The groups that hold items, in order. */\n\t\tshownGroups() {\n\t\t\tconst declared = (Array.isArray(this.groups) ? this.groups : []).map((group) => ({\n\t\t\t\tid: String(group.id),\n\t\t\t\ttitle: this.text(group.title || ''),\n\t\t\t\titems: this.shownItems.filter((item) => item.group === String(group.id)),\n\t\t\t}))\n\t\t\tconst known = new Set(declared.map((group) => group.id))\n\t\t\tconst rest = this.shownItems.filter((item) => !known.has(item.group))\n\t\t\tconst all = rest.length > 0 ? [{ id: 'items', title: '', items: rest }, ...declared] : declared\n\t\t\treturn all.filter((group) => group.items.length > 0)\n\t\t},\n\t},\n\n\twatch: {\n\t\tsource() {\n\t\t\tthis.reload()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.reload()\n\t\twindow.addEventListener('focus', this.onFocus)\n\t},\n\n\tbeforeUnmount() {\n\t\twindow.removeEventListener('focus', this.onFocus)\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A declared text through the host's translate.\n\t\t *\n\t\t * @param {string} value The text.\n\t\t * @return {string} The translated text.\n\t\t */\n\t\ttext(value) {\n\t\t\treturn value ? String(this.cnTranslate(String(value))) : ''\n\t\t},\n\n\t\t/**\n\t\t * @param {object} item The item.\n\t\t * @return {string} `met`, `unmet` or `unknown`.\n\t\t */\n\t\tstateOf(item) {\n\t\t\tif (item.met === true) {\n\t\t\t\treturn 'met'\n\t\t\t}\n\t\t\treturn item.met === false ? 'unmet' : 'unknown'\n\t\t},\n\n\t\t/**\n\t\t * The word for an item's state.\n\t\t *\n\t\t * @param {object} item The item.\n\t\t * @return {string} The word.\n\t\t */\n\t\tstateLabel(item) {\n\t\t\tconst state = this.stateOf(item)\n\t\t\tconst own = this.statusLabels && this.statusLabels[state]\n\t\t\tif (own) {\n\t\t\t\treturn this.text(own)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tmet: t('nextcloud-vue', 'Done'),\n\t\t\t\tunmet: t('nextcloud-vue', 'Still open'),\n\t\t\t\tunknown: t('nextcloud-vue', 'Unknown'),\n\t\t\t}[state]\n\t\t},\n\n\t\t/**\n\t\t * Read the items again, from the source. The source asks the instance\n\t\t * rather than a stored flag, so a fix made elsewhere shows on return.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync reload() {\n\t\t\tif (!this.source) {\n\t\t\t\tthis.loadedItems = null\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = this.loadedItems === null\n\t\t\tthis.loadError = ''\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(generateUrl(this.source))\n\t\t\t\tconst rows = Array.isArray(data) ? data : readPath(data, this.itemsPath)\n\t\t\t\tthis.loadedItems = Array.isArray(rows) ? rows : []\n\t\t\t\t/** @event loaded The source answered. Payload: the whole answer. */\n\t\t\t\tthis.$emit('loaded', data)\n\t\t\t} catch {\n\t\t\t\tthis.loadedItems = []\n\t\t\t\tthis.loadError = this.errorText ? this.text(this.errorText) : t('nextcloud-vue', 'The checklist could not be read.')\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/** Re-read when the reader comes back from fixing something. */\n\t\tonFocus() {\n\t\t\tif (this.source) {\n\t\t\t\tthis.reload()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-readiness-checklist__description,\n.cn-readiness-checklist__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0 0 12px;\n}\n\n.cn-readiness-checklist__group-title {\n\tmargin: 12px 0 4px;\n}\n\n.cn-readiness-checklist__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-readiness-checklist__item {\n\tdisplay: flex;\n\tgap: calc(var(--default-grid-baseline, 4px) * 3);\n\tpadding-block: calc(var(--default-grid-baseline, 4px) * 2);\n\tborder-block-end: 1px solid var(--color-border);\n}\n\n.cn-readiness-checklist__state {\n\tflex: 0 0 auto;\n\tmin-width: 6em;\n\tfont-weight: bold;\n}\n\n.cn-readiness-checklist__state--met {\n\tcolor: var(--color-success-text, var(--color-success));\n}\n\n.cn-readiness-checklist__state--unmet {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-readiness-checklist__state--unknown {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-readiness-checklist__body {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-readiness-checklist__explanation {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-readiness-checklist__failure {\n\tcolor: var(--color-error-text, var(--color-error));\n}\n</style>\n"],"names":["_createElementBlock","_toDisplayString","_createBlock","_createTextVNode","_openBlock","_createElementVNode","_Fragment","_renderList","_normalizeClass"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import script from './CnSchemaRulesWidget.vue2.js';
2
+ import { render } from './CnSchemaRulesWidget.vue3.js';
3
+
4
+ script.render = render;
5
+ script.__scopeId = "data-v-1d1c1c13";
6
+ const __sfc_main = script;
7
+
8
+ export { __sfc_main as default };
9
+ //# sourceMappingURL=CnSchemaRulesWidget.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnSchemaRulesWidget.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
@@ -0,0 +1,163 @@
1
+ import { translate } from '@nextcloud/l10n';
2
+ import { NcButton, NcLoadingIcon, NcTextField } from '@nextcloud/vue';
3
+ import { kindsByName, fetchRuleVocabulary, fetchSchemaRules, traceOf, evaluateRule } from './schemaRules.js';
4
+
5
+ /**
6
+ * CnSchemaRulesWidget: the rules OpenRegister's engine holds for one schema,
7
+ * in the order it evaluates them, each with its kind (from the engine's own
8
+ * vocabulary), whether it is on and when it last ran, and a dry run against
9
+ * one stored object that writes nothing and shows the trace (verdict, the
10
+ * operand it read and what it found).
11
+ *
12
+ * The inventory is an admin read: a refusal is said in one sentence instead
13
+ * of an empty table that would read as "nothing is checked".
14
+ *
15
+ * @spec openspec/changes/schema-rules-widget/specs/schema-rules-widget/spec.md
16
+ */
17
+ var script = {
18
+ name: 'CnSchemaRulesWidget',
19
+
20
+ components: { NcButton, NcLoadingIcon, NcTextField },
21
+
22
+ inject: {
23
+ /** The host app's translate, for the texts the page declares. */
24
+ cnTranslate: { default: () => (key) => key },
25
+ },
26
+
27
+ props: {
28
+ /** The register slug the trial object lives in. */
29
+ register: { type: String, required: true },
30
+ /** The schema slug whose rules are listed. */
31
+ schema: { type: String, required: true },
32
+ /** The object a trial runs against, pre-filled; the reader can change it. */
33
+ objectId: { type: String, default: '' },
34
+ /** The heading; `null` draws none. */
35
+ title: { type: String, default: '' },
36
+ /** The line under the heading. */
37
+ description: { type: String, default: '' },
38
+ /** Said when the schema has no rules. */
39
+ emptyText: { type: String, default: '' },
40
+ /** The label of the trial object field. */
41
+ objectLabel: { type: String, default: '' },
42
+ /** The placeholder of the trial object field. */
43
+ objectPlaceholder: { type: String, default: '' },
44
+ /** Said when the inventory is refused (403). */
45
+ refusedText: { type: String, default: '' },
46
+ /** Whether to read nothing yet (a record still being created). */
47
+ disabled: { type: Boolean, default: false },
48
+ },
49
+
50
+ data() {
51
+ return {
52
+ loading: false,
53
+ error: '',
54
+ rules: [],
55
+ kinds: {},
56
+ trialObject: this.objectId,
57
+ trying: '',
58
+ trace: null,
59
+ }
60
+ },
61
+
62
+ computed: {
63
+ /** @return {string} What the trial decided, as a sentence. */
64
+ verdictLine() {
65
+ const verdicts = {
66
+ fired: translate('nextcloud-vue', 'The rule fired.'),
67
+ no_match: translate('nextcloud-vue', 'The rule did not match this object.'),
68
+ refused: translate('nextcloud-vue', 'The rule refused the save.'),
69
+ error: translate('nextcloud-vue', 'The rule could not be evaluated.'),
70
+ };
71
+ return verdicts[this.trace && this.trace.verdict] || (this.trace && this.trace.verdict) || ''
72
+ },
73
+ },
74
+
75
+ watch: {
76
+ objectId(value) {
77
+ this.trialObject = value;
78
+ },
79
+
80
+ schema() {
81
+ this.load();
82
+ },
83
+ },
84
+
85
+ mounted() {
86
+ if (!this.disabled) {
87
+ this.load();
88
+ }
89
+ },
90
+
91
+ methods: {
92
+ t: translate,
93
+
94
+ /**
95
+ * @param {string} value A declared text.
96
+ * @return {string} It, translated by the host.
97
+ */
98
+ text(value) {
99
+ return value ? String(this.cnTranslate(String(value))) : ''
100
+ },
101
+
102
+ /**
103
+ * The word for a rule's kind, from the vocabulary, else the kind the
104
+ * rule carries: a blank would read as "this rule has no kind".
105
+ *
106
+ * @param {object} rule An inventory row.
107
+ * @return {string} The label.
108
+ */
109
+ kindLabel(rule) {
110
+ const kind = String((rule && rule.kind) ?? '');
111
+ return String((this.kinds[kind] && this.kinds[kind].description) || kind)
112
+ },
113
+
114
+ /**
115
+ * Read the vocabulary and the inventory.
116
+ *
117
+ * @return {Promise<void>}
118
+ */
119
+ async load() {
120
+ if (!this.schema) {
121
+ return
122
+ }
123
+ this.loading = true;
124
+ this.error = '';
125
+ this.kinds = kindsByName(await fetchRuleVocabulary());
126
+ try {
127
+ this.rules = await fetchSchemaRules(this.schema);
128
+ } catch (e) {
129
+ this.rules = [];
130
+ const refused = Number(e && e.response && e.response.status) === 403;
131
+ this.error = refused
132
+ ? (this.refusedText ? this.text(this.refusedText) : translate('nextcloud-vue', 'Only an administrator can read the rules of this schema.'))
133
+ : translate('nextcloud-vue', 'The rules could not be read. Ask an administrator.');
134
+ }
135
+ this.loading = false;
136
+ },
137
+
138
+ /**
139
+ * Try one rule against the trial object, keyed on the rule's id.
140
+ *
141
+ * @param {object} rule An inventory row.
142
+ * @return {Promise<void>}
143
+ */
144
+ async tryRule(rule) {
145
+ const ruleId = String((rule && rule.id) ?? '');
146
+ const objectId = this.trialObject.trim();
147
+ if (ruleId === '' || objectId === '') {
148
+ return
149
+ }
150
+ this.trying = ruleId;
151
+ this.trace = null;
152
+ try {
153
+ this.trace = traceOf(await evaluateRule(this.schema, ruleId, this.register, objectId));
154
+ } catch {
155
+ this.trace = { verdict: 'error', operand: '', operandValue: '', message: translate('nextcloud-vue', 'That object could not be read.') };
156
+ }
157
+ this.trying = '';
158
+ },
159
+ },
160
+ };
161
+
162
+ export { script as default };
163
+ //# sourceMappingURL=CnSchemaRulesWidget.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnSchemaRulesWidget.vue2.js","sources":["../../../../src/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-schema-rules\" data-testid=\"cn-schema-rules\">\n\t\t<h4 v-if=\"title !== null\">\n\t\t\t{{ title ? text(title) : t('nextcloud-vue', 'Rules on this schema') }}\n\t\t</h4>\n\t\t<p class=\"cn-schema-rules__hint\">\n\t\t\t{{ description ? text(description) : t('nextcloud-vue', 'Everything the platform checks when an object is saved, in the order it checks it.') }}\n\t\t</p>\n\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"32\" :name=\"t('nextcloud-vue', 'Loading')\" />\n\t\t<p v-else-if=\"error\"\n\t\t\tclass=\"cn-schema-rules__error\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-schema-rules-error\">\n\t\t\t{{ error }}\n\t\t</p>\n\t\t<p v-else-if=\"rules.length === 0\" class=\"cn-schema-rules__empty\" data-testid=\"cn-schema-rules-empty\">\n\t\t\t{{ emptyText ? text(emptyText) : t('nextcloud-vue', 'Nothing is checked on this schema yet.') }}\n\t\t</p>\n\t\t<table v-else class=\"cn-schema-rules__table\" data-testid=\"cn-schema-rules-table\">\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Rule') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Kind') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'On') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Last run') }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\">\n\t\t\t\t\t\t<span class=\"hidden-visually\">{{ t('nextcloud-vue', 'Try') }}</span>\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\t\t\t<tbody>\n\t\t\t\t<tr v-for=\"rule in rules\" :key=\"rule.id\" data-testid=\"cn-schema-rules-row\">\n\t\t\t\t\t<th scope=\"row\">\n\t\t\t\t\t\t{{ rule.label || rule.key || rule.id }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<td data-testid=\"cn-schema-rules-kind\">\n\t\t\t\t\t\t{{ kindLabel(rule) }}\n\t\t\t\t\t</td>\n\t\t\t\t\t<td>{{ rule.enabled === false ? t('nextcloud-vue', 'Off') : t('nextcloud-vue', 'On') }}</td>\n\t\t\t\t\t<td>{{ rule.lastRun || t('nextcloud-vue', 'Never') }}</td>\n\t\t\t\t\t<td>\n\t\t\t\t\t\t<NcButton variant=\"tertiary\"\n\t\t\t\t\t\t\t:disabled=\"trying === rule.id || trialObject.trim() === ''\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-schema-rules-try-${rule.id}`\"\n\t\t\t\t\t\t\t@click=\"tryRule(rule)\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Try it') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\n\t\t<div class=\"cn-schema-rules__trial\">\n\t\t\t<NcTextField :modelValue=\"trialObject\"\n\t\t\t\t:label=\"objectLabel ? text(objectLabel) : t('nextcloud-vue', 'Object to try a rule against')\"\n\t\t\t\t:placeholder=\"objectPlaceholder ? text(objectPlaceholder) : t('nextcloud-vue', 'Paste an object id')\"\n\t\t\t\tdata-testid=\"cn-schema-rules-object-id\"\n\t\t\t\t@update:modelValue=\"(v) => (trialObject = v)\" />\n\t\t\t<p class=\"cn-schema-rules__hint\">\n\t\t\t\t{{ t('nextcloud-vue', 'A trial writes nothing. It reads the object as it stands and says what the rule would have decided.') }}\n\t\t\t</p>\n\t\t\t<div v-if=\"trace\" class=\"cn-schema-rules__trace\" data-testid=\"cn-schema-rules-trace\">\n\t\t\t\t<p class=\"cn-schema-rules__verdict\">\n\t\t\t\t\t{{ verdictLine }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"trace.operand\" data-testid=\"cn-schema-rules-operand\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'It read {operand} and found {value}.', { operand: trace.operand, value: trace.operandValue || t('nextcloud-vue', 'nothing') }) }}\n\t\t\t\t</p>\n\t\t\t\t<p v-if=\"trace.message\">\n\t\t\t\t\t{{ trace.message }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcTextField } from '@nextcloud/vue'\nimport { evaluateRule, fetchRuleVocabulary, fetchSchemaRules, kindsByName, traceOf } from './schemaRules.js'\n\n/**\n * CnSchemaRulesWidget: the rules OpenRegister's engine holds for one schema,\n * in the order it evaluates them, each with its kind (from the engine's own\n * vocabulary), whether it is on and when it last ran, and a dry run against\n * one stored object that writes nothing and shows the trace (verdict, the\n * operand it read and what it found).\n *\n * The inventory is an admin read: a refusal is said in one sentence instead\n * of an empty table that would read as \"nothing is checked\".\n *\n * @spec openspec/changes/schema-rules-widget/specs/schema-rules-widget/spec.md\n */\nexport default {\n\tname: 'CnSchemaRulesWidget',\n\n\tcomponents: { NcButton, NcLoadingIcon, NcTextField },\n\n\tinject: {\n\t\t/** The host app's translate, for the texts the page declares. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The register slug the trial object lives in. */\n\t\tregister: { type: String, required: true },\n\t\t/** The schema slug whose rules are listed. */\n\t\tschema: { type: String, required: true },\n\t\t/** The object a trial runs against, pre-filled; the reader can change it. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** The heading; `null` draws none. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** The line under the heading. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** Said when the schema has no rules. */\n\t\temptyText: { type: String, default: '' },\n\t\t/** The label of the trial object field. */\n\t\tobjectLabel: { type: String, default: '' },\n\t\t/** The placeholder of the trial object field. */\n\t\tobjectPlaceholder: { type: String, default: '' },\n\t\t/** Said when the inventory is refused (403). */\n\t\trefusedText: { type: String, default: '' },\n\t\t/** Whether to read nothing yet (a record still being created). */\n\t\tdisabled: { type: Boolean, default: false },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: false,\n\t\t\terror: '',\n\t\t\trules: [],\n\t\t\tkinds: {},\n\t\t\ttrialObject: this.objectId,\n\t\t\ttrying: '',\n\t\t\ttrace: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} What the trial decided, as a sentence. */\n\t\tverdictLine() {\n\t\t\tconst verdicts = {\n\t\t\t\tfired: t('nextcloud-vue', 'The rule fired.'),\n\t\t\t\tno_match: t('nextcloud-vue', 'The rule did not match this object.'),\n\t\t\t\trefused: t('nextcloud-vue', 'The rule refused the save.'),\n\t\t\t\terror: t('nextcloud-vue', 'The rule could not be evaluated.'),\n\t\t\t}\n\t\t\treturn verdicts[this.trace && this.trace.verdict] || (this.trace && this.trace.verdict) || ''\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId(value) {\n\t\t\tthis.trialObject = value\n\t\t},\n\n\t\tschema() {\n\t\t\tthis.load()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tif (!this.disabled) {\n\t\t\tthis.load()\n\t\t}\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * @param {string} value A declared text.\n\t\t * @return {string} It, translated by the host.\n\t\t */\n\t\ttext(value) {\n\t\t\treturn value ? String(this.cnTranslate(String(value))) : ''\n\t\t},\n\n\t\t/**\n\t\t * The word for a rule's kind, from the vocabulary, else the kind the\n\t\t * rule carries: a blank would read as \"this rule has no kind\".\n\t\t *\n\t\t * @param {object} rule An inventory row.\n\t\t * @return {string} The label.\n\t\t */\n\t\tkindLabel(rule) {\n\t\t\tconst kind = String((rule && rule.kind) ?? '')\n\t\t\treturn String((this.kinds[kind] && this.kinds[kind].description) || kind)\n\t\t},\n\n\t\t/**\n\t\t * Read the vocabulary and the inventory.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tif (!this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = ''\n\t\t\tthis.kinds = kindsByName(await fetchRuleVocabulary())\n\t\t\ttry {\n\t\t\t\tthis.rules = await fetchSchemaRules(this.schema)\n\t\t\t} catch (e) {\n\t\t\t\tthis.rules = []\n\t\t\t\tconst refused = Number(e && e.response && e.response.status) === 403\n\t\t\t\tthis.error = refused\n\t\t\t\t\t? (this.refusedText ? this.text(this.refusedText) : t('nextcloud-vue', 'Only an administrator can read the rules of this schema.'))\n\t\t\t\t\t: t('nextcloud-vue', 'The rules could not be read. Ask an administrator.')\n\t\t\t}\n\t\t\tthis.loading = false\n\t\t},\n\n\t\t/**\n\t\t * Try one rule against the trial object, keyed on the rule's id.\n\t\t *\n\t\t * @param {object} rule An inventory row.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync tryRule(rule) {\n\t\t\tconst ruleId = String((rule && rule.id) ?? '')\n\t\t\tconst objectId = this.trialObject.trim()\n\t\t\tif (ruleId === '' || objectId === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.trying = ruleId\n\t\t\tthis.trace = null\n\t\t\ttry {\n\t\t\t\tthis.trace = traceOf(await evaluateRule(this.schema, ruleId, this.register, objectId))\n\t\t\t} catch {\n\t\t\t\tthis.trace = { verdict: 'error', operand: '', operandValue: '', message: t('nextcloud-vue', 'That object could not be read.') }\n\t\t\t}\n\t\t\tthis.trying = ''\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-schema-rules__hint {\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 4px 0 12px;\n}\n\n.cn-schema-rules__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-schema-rules__table th,\n.cn-schema-rules__table td {\n\ttext-align: start;\n\tpadding: 6px 8px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-schema-rules__trial {\n\tmargin-top: 16px;\n\tborder-top: 1px solid var(--color-border);\n\tpadding-top: 12px;\n}\n\n.cn-schema-rules__trace {\n\tmargin-top: 8px;\n\tpadding: 8px 12px;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbackground-color: var(--color-background-hover);\n}\n\n.cn-schema-rules__trace p {\n\tmargin: 0;\n}\n\n.cn-schema-rules__verdict {\n\tfont-weight: bold;\n}\n\n.cn-schema-rules__error {\n\tcolor: var(--color-error-text);\n}\n\n.cn-schema-rules__empty {\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["t"],"mappings":"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"}