@conduction/nextcloud-vue 2.56.0-beta.52 → 2.56.0-beta.53

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 (84) hide show
  1. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js +12 -0
  2. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +11 -2
  4. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  5. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue.js +1 -1
  6. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
  7. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +1 -1
  8. package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
  9. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  10. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +2 -1
  11. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  12. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +1 -1
  13. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +26 -7
  14. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -1
  15. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +78 -17
  16. package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -1
  17. package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +9 -0
  18. package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js.map +1 -0
  19. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js +265 -0
  20. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -0
  21. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +191 -0
  22. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -0
  23. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js +9 -0
  24. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js.map +1 -0
  25. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js +508 -0
  26. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js.map +1 -0
  27. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js +99 -0
  28. package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js.map +1 -0
  29. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
  30. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +18 -70
  31. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
  32. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js +6 -44
  33. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
  34. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  35. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  36. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js +9 -0
  37. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js.map +1 -0
  38. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js +472 -0
  39. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js.map +1 -0
  40. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js +231 -0
  41. package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js.map +1 -0
  42. package/dist/esm/components/CnWorkingCalendarWidget/index.js +27 -0
  43. package/dist/esm/components/CnWorkingCalendarWidget/index.js.map +1 -0
  44. package/dist/esm/composables/useFlowStore.js +2 -1
  45. package/dist/esm/composables/useFlowStore.js.map +1 -1
  46. package/dist/esm/index.js +3 -0
  47. package/dist/esm/index.js.map +1 -1
  48. package/dist/esm/l10n/en.json.js +95 -1
  49. package/dist/esm/l10n/en.json.js.map +1 -1
  50. package/dist/esm/l10n/nl.json.js +109 -1
  51. package/dist/esm/l10n/nl.json.js.map +1 -1
  52. package/dist/esm/library-style.js +1 -1
  53. package/dist/esm/nextcloud-vue.css +660 -81
  54. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  55. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  56. package/dist/esm/utils/workingCalendarMonth.js +85 -0
  57. package/dist/esm/utils/workingCalendarMonth.js.map +1 -0
  58. package/dist/nextcloud-vue.cjs.js +9355 -7303
  59. package/dist/nextcloud-vue.cjs.js.map +2 -2
  60. package/dist/nextcloud-vue.css +660 -81
  61. package/l10n/en.json +95 -1
  62. package/l10n/nl.json +109 -1
  63. package/package.json +1 -1
  64. package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
  65. package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +1 -0
  66. package/src/components/CnDetailPage/CnDetailPage.vue +1 -0
  67. package/src/components/CnOrderedListField/CnOrderedListField.vue +81 -9
  68. package/src/components/CnSettingsField/CnSettingsField.vue +449 -0
  69. package/src/components/CnSettingsField/index.js +4 -0
  70. package/src/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue +613 -0
  71. package/src/components/CnSettingsFieldsSection/index.js +4 -0
  72. package/src/components/CnSettingsPage/CnSettingsPage.vue +6 -86
  73. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
  74. package/src/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue +725 -0
  75. package/src/components/CnWorkingCalendarWidget/index.js +26 -0
  76. package/src/components/index.js +3 -0
  77. package/src/composables/useFlowStore.js +2 -1
  78. package/src/css/badge.css +32 -16
  79. package/src/css/look-board-detail.css +80 -8
  80. package/src/css/look-board-index.css +52 -8
  81. package/src/css/look-board.css +219 -1
  82. package/src/index.js +3 -0
  83. package/src/utils/libraryWidgetKeys.js +1 -0
  84. package/src/utils/workingCalendarMonth.js +82 -0
@@ -0,0 +1,508 @@
1
+ import axios__default from '@nextcloud/axios';
2
+ import { translate } from '@nextcloud/l10n';
3
+ import { NcButton, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue';
4
+ import { prefixUrl } from '../../utils/headers.js';
5
+ import { readPath } from '../../utils/readPath.js';
6
+ import { serverErrorMessage } from '../../utils/serverErrorMessage.js';
7
+ import __sfc_main from '../CnSettingsField/CnSettingsField.vue.js';
8
+ import __sfc_main$1 from '../CnSettingsSection/CnSettingsSection.vue.js';
9
+
10
+ /**
11
+ * Up to two initials of a name, for an image field without an image.
12
+ *
13
+ * @param {unknown} name The name.
14
+ * @return {string}
15
+ */
16
+ function initialsOf(name) {
17
+ return String(name || '').trim().split(/\s+/).filter(Boolean).slice(0, 2).map((word) => word[0].toUpperCase()).join('')
18
+ }
19
+
20
+ /**
21
+ * CnSettingsFieldsSection - a settings section declared as fields: it reads
22
+ * the app's settings, draws the fields in one to three columns, and saves
23
+ * them with ONE Save button for the section.
24
+ *
25
+ * It reads `GET url` (default `/apps/<appId>/api/settings`) and writes the
26
+ * section's fields with `PUT saveUrl` (default the same url). The Save button
27
+ * is on while something changed ("Unsaved changes"); a save checks the
28
+ * fields first (required, pattern, min, max) and marks each failing field,
29
+ * then says it saved or why it did not. A `secret` field never shows the
30
+ * stored value: it says a value is stored, and is sent only when something
31
+ * was typed.
32
+ *
33
+ * It renders inside CnAdminSettingsShell and CnSettingsPage, in the default
34
+ * and the board look.
35
+ *
36
+ * ```vue
37
+ * <CnSettingsFieldsSection
38
+ * app-id="myapp"
39
+ * name="Connection"
40
+ * :columns="2"
41
+ * :fields="[
42
+ * { key: 'baseUrl', label: 'Server address', required: true, pattern: '^https://', patternMessage: 'Starts with https://' },
43
+ * { key: 'apiKey', label: 'API key', type: 'secret' },
44
+ * { key: 'mode', label: 'Mode', type: 'select', options: [{ value: 'live', label: 'Live' }, { value: 'test', label: 'Test' }] },
45
+ * { key: 'enabled', label: 'Enabled', type: 'switch', hint: 'Off stops every call.' },
46
+ * ]" />
47
+ * ```
48
+ */
49
+ var script = {
50
+ name: 'CnSettingsFieldsSection',
51
+
52
+ components: { CnSettingsField: __sfc_main, CnSettingsSection: __sfc_main$1, NcButton, NcLoadingIcon, NcNoteCard },
53
+
54
+ inject: {
55
+ /** The app's translate function; identity outside CnAppRoot. */
56
+ cnTranslate: { default: () => (key) => key },
57
+ },
58
+
59
+ props: {
60
+ /** The section title. */
61
+ name: {
62
+ type: String,
63
+ required: true,
64
+ },
65
+
66
+ /** The line under the title. */
67
+ description: {
68
+ type: String,
69
+ default: '',
70
+ },
71
+
72
+ /** A documentation link beside the title. */
73
+ docUrl: {
74
+ type: String,
75
+ default: '',
76
+ },
77
+
78
+ /** Span the whole settings grid in the board look. */
79
+ wide: {
80
+ type: Boolean,
81
+ default: false,
82
+ },
83
+
84
+ /**
85
+ * The fields: `[{ key, label, type?, hint?, placeholder?, required?,
86
+ * pattern?, patternMessage?, min?, max?, rows?, options?, optionsUrl?,
87
+ * optionsPath?, optionLabel?, optionValue?, default?, readOnly?, wide?,
88
+ * url?, valuePath?, saveUrl?, saveMethod?, saveKey?, uploadUrl?,
89
+ * uploadField?, uploadParams?, uploadValuePath?, initials?, initialsFrom? }]`.
90
+ * `type` is `text` (default), `textarea`, `number`, `switch`, `select`,
91
+ * `secret` or `image`. `key` names the setting; a dotted key reads a
92
+ * nested value and is written as written. A field with its own `url`
93
+ * is read there (at `valuePath`, default its key) and saved to its
94
+ * `saveUrl` (default that `url`) with `saveMethod` (default POST) under
95
+ * `saveKey` (default its key), so one section can hold the settings of
96
+ * several apps. An `image` uploads at once to `uploadUrl`. Labels,
97
+ * hints, placeholders and messages go through the app's translate
98
+ * function.
99
+ *
100
+ * @type {Array<object>}
101
+ * @spec openspec/changes/settings-fields-section/specs/settings-fields-section/spec.md#requirement-a-section-declared-as-fields-reads-and-saves-the-apps-settings
102
+ */
103
+ fields: {
104
+ type: Array,
105
+ required: true,
106
+ },
107
+
108
+ /** Columns of the field grid: 1, 2 or 3. A narrow card stacks them. */
109
+ columns: {
110
+ type: Number,
111
+ default: 1,
112
+ validator: (value) => [1, 2, 3].includes(value),
113
+ },
114
+
115
+ /** The app id, for the default url `/apps/<appId>/api/settings`. */
116
+ appId: {
117
+ type: String,
118
+ default: '',
119
+ },
120
+
121
+ /** Where the settings are read (`GET`); app-absolute or a full URL. */
122
+ url: {
123
+ type: String,
124
+ default: '',
125
+ },
126
+
127
+ /** Where the section saves; defaults to `url`. */
128
+ saveUrl: {
129
+ type: String,
130
+ default: '',
131
+ },
132
+
133
+ /** The method of the save request. */
134
+ saveMethod: {
135
+ type: String,
136
+ default: 'PUT',
137
+ validator: (value) => ['PUT', 'POST', 'PATCH'].includes(String(value).toUpperCase()),
138
+ },
139
+
140
+ /** A dotted path to the values in the GET answer; empty reads the answer itself. */
141
+ valuesPath: {
142
+ type: String,
143
+ default: '',
144
+ },
145
+
146
+ /** The Save button's label. Defaults to "Save". */
147
+ saveLabel: {
148
+ type: String,
149
+ default: '',
150
+ },
151
+ },
152
+
153
+ emits: ['saved', 'save-error'],
154
+
155
+ data() {
156
+ return {
157
+ form: {},
158
+ baseline: {},
159
+ storedSecrets: {},
160
+ // Field key to the message of a read target that failed.
161
+ unreadable: {},
162
+ errors: {},
163
+ loading: true,
164
+ loadError: '',
165
+ saving: false,
166
+ notice: null,
167
+ }
168
+ },
169
+
170
+ computed: {
171
+ /** @return {number} The column count, 1 to 3. */
172
+ columnCount() {
173
+ return [1, 2, 3].includes(this.columns) ? this.columns : 1
174
+ },
175
+
176
+ /** @return {string} Where the settings are read: `url`, else the app's settings endpoint. */
177
+ readTarget() {
178
+ return this.url || (this.appId ? `/apps/${this.appId}/api/settings` : '')
179
+ },
180
+
181
+ /** @return {string} Where the section saves: `saveUrl`, else the read target. */
182
+ writeTarget() {
183
+ return this.saveUrl || this.readTarget
184
+ },
185
+
186
+ /**
187
+ * The declared fields with their text translated and a secret's stored state.
188
+ *
189
+ * @return {Array<object>}
190
+ */
191
+ resolvedFields() {
192
+ return (Array.isArray(this.fields) ? this.fields : [])
193
+ .filter((field) => field && typeof field.key === 'string' && field.key !== '')
194
+ .map((field) => ({
195
+ ...field,
196
+ label: this.tr(field.label || field.key),
197
+ hint: this.tr(field.hint || ''),
198
+ placeholder: this.tr(field.placeholder || ''),
199
+ secretSet: field.type === 'secret' ? !!this.storedSecrets[field.key] : undefined,
200
+ initials: field.type === 'image' ? initialsOf(field.initialsFrom ? this.form[field.initialsFrom] : field.initials) : undefined,
201
+ readOnly: field.readOnly || !!this.unreadable[field.key],
202
+ }))
203
+ },
204
+
205
+ /**
206
+ * Whether something changed since the last load or save.
207
+ *
208
+ * @return {boolean}
209
+ * @spec openspec/changes/settings-fields-section/specs/settings-fields-section/spec.md#requirement-one-save-per-section-with-a-dirty-state
210
+ */
211
+ dirty() {
212
+ return this.resolvedFields.some((field) => field.type !== 'image' && !this.same(this.form[field.key], this.baseline[field.key]))
213
+ },
214
+
215
+ /** @return {string} The Save button's label. */
216
+ resolvedSaveLabel() {
217
+ return this.saveLabel ? this.tr(this.saveLabel) : translate('nextcloud-vue', 'Save')
218
+ },
219
+ },
220
+
221
+ mounted() {
222
+ this.load();
223
+ },
224
+
225
+ methods: {
226
+ t: translate,
227
+
228
+ /**
229
+ * @param {string} value A declared string.
230
+ * @return {string} It through the app's translate function.
231
+ */
232
+ tr(value) {
233
+ return typeof value === 'string' && value !== '' ? this.cnTranslate(value) : (value || '')
234
+ },
235
+
236
+ /**
237
+ * @param {unknown} a A value.
238
+ * @param {unknown} b Another.
239
+ * @return {boolean} Whether they count as the same setting.
240
+ */
241
+ same(a, b) {
242
+ const empty = (v) => v === null || v === undefined || v === '';
243
+ if (empty(a) && empty(b)) {
244
+ return true
245
+ }
246
+ return JSON.stringify(a) === JSON.stringify(b)
247
+ },
248
+
249
+ /**
250
+ * Change a field's value; its error goes until the next save.
251
+ *
252
+ * @param {object} field The field.
253
+ * @param {unknown} value The value.
254
+ * @return {void}
255
+ */
256
+ setValue(field, value) {
257
+ this.form = { ...this.form, [field.key]: value };
258
+ if (this.errors[field.key]) {
259
+ const { [field.key]: _gone, ...rest } = this.errors;
260
+ this.errors = rest;
261
+ }
262
+ this.notice = null;
263
+ },
264
+
265
+ /**
266
+ * Read the settings and fill the fields; a secret's value is never put
267
+ * in the form, only whether one is stored.
268
+ *
269
+ * @return {Promise<void>}
270
+ */
271
+ async load() {
272
+ this.loading = true;
273
+ this.loadError = '';
274
+ try {
275
+ // One GET per distinct read target; the section's own target must
276
+ // answer, a field's own target may fail on its own.
277
+ const targets = [...new Set(this.fields
278
+ .filter((field) => field && field.key)
279
+ .map((field) => this.readUrlOf(field))
280
+ .filter(Boolean))];
281
+ const answers = {};
282
+ const failures = {};
283
+ await Promise.all(targets.map(async (target) => {
284
+ try {
285
+ const response = await axios__default.get(prefixUrl(target));
286
+ answers[target] = response?.data ?? {};
287
+ } catch (error) {
288
+ failures[target] = error;
289
+ }
290
+ }));
291
+ if (this.readTarget && failures[this.readTarget]) {
292
+ throw failures[this.readTarget]
293
+ }
294
+ const form = {};
295
+ const stored = {};
296
+ const unreadable = {};
297
+ for (const field of this.fields || []) {
298
+ if (!field || !field.key) {
299
+ continue
300
+ }
301
+ const target = this.readUrlOf(field);
302
+ if (target && failures[target]) {
303
+ unreadable[field.key] = translate('nextcloud-vue', 'This value could not be loaded: {reason}', { reason: serverErrorMessage(failures[target]) });
304
+ }
305
+ const raw = this.readValue(field, target ? answers[target] : null);
306
+ if (field.type === 'secret') {
307
+ stored[field.key] = raw !== null && raw !== undefined && raw !== '' && raw !== false;
308
+ form[field.key] = '';
309
+ continue
310
+ }
311
+ form[field.key] = raw === undefined ? (field.default ?? (field.type === 'switch' ? false : '')) : raw;
312
+ }
313
+ this.form = form;
314
+ this.baseline = { ...form };
315
+ this.storedSecrets = stored;
316
+ this.unreadable = unreadable;
317
+ this.errors = {};
318
+ } catch (error) {
319
+ this.loadError = translate('nextcloud-vue', 'The settings could not be loaded: {reason}', { reason: serverErrorMessage(error) });
320
+ } finally {
321
+ this.loading = false;
322
+ }
323
+ },
324
+
325
+ /**
326
+ * Where a field is read: its own `url`, else the section's.
327
+ *
328
+ * @param {object} field The field.
329
+ * @return {string}
330
+ * @spec openspec/changes/settings-fields-section/specs/settings-fields-section/spec.md#requirement-a-field-can-read-and-save-elsewhere
331
+ */
332
+ readUrlOf(field) {
333
+ return field.url || this.readTarget
334
+ },
335
+
336
+ /**
337
+ * A field's value in its read answer: at its `valuePath` (a field with its
338
+ * own `url`), else at the section's `valuesPath`, then its `key`.
339
+ *
340
+ * @param {object} field The field.
341
+ * @param {object|null} data The answer of the field's read target.
342
+ * @return {unknown}
343
+ */
344
+ readValue(field, data) {
345
+ if (!data || typeof data !== 'object') {
346
+ return undefined
347
+ }
348
+ if (field.url) {
349
+ return readPath(data, field.valuePath || field.key)
350
+ }
351
+ const values = (this.valuesPath ? readPath(data, this.valuesPath) : data) || {};
352
+ return Object.hasOwn(values, field.key) ? values[field.key] : readPath(values, field.key)
353
+ },
354
+
355
+ /**
356
+ * Check the fields: required, pattern, and a number's min and max.
357
+ *
358
+ * @return {object} Field key to message; empty when all pass.
359
+ * @spec openspec/changes/settings-fields-section/specs/settings-fields-section/spec.md#requirement-a-failing-field-says-why
360
+ */
361
+ validate() {
362
+ const errors = {};
363
+ for (const field of this.resolvedFields) {
364
+ const value = this.form[field.key];
365
+ const empty = value === null || value === undefined || value === '';
366
+ if (field.type === 'switch' || field.type === 'image' || this.unreadable[field.key]) {
367
+ continue
368
+ }
369
+ if (field.required && empty && !(field.type === 'secret' && field.secretSet)) {
370
+ errors[field.key] = translate('nextcloud-vue', '{field} is required.', { field: field.label });
371
+ continue
372
+ }
373
+ if (empty) {
374
+ continue
375
+ }
376
+ if (field.pattern && !new RegExp(field.pattern).test(String(value))) {
377
+ errors[field.key] = field.patternMessage ? this.tr(field.patternMessage) : translate('nextcloud-vue', '{field} is not in the right form.', { field: field.label });
378
+ continue
379
+ }
380
+ if (field.type === 'number') {
381
+ const number = Number(value);
382
+ if (Number.isNaN(number)) {
383
+ errors[field.key] = translate('nextcloud-vue', '{field} must be a number.', { field: field.label });
384
+ } else if (field.min !== undefined && number < field.min) {
385
+ errors[field.key] = translate('nextcloud-vue', '{field} must be at least {min}.', { field: field.label, min: field.min });
386
+ } else if (field.max !== undefined && number > field.max) {
387
+ errors[field.key] = translate('nextcloud-vue', '{field} must be at most {max}.', { field: field.label, max: field.max });
388
+ }
389
+ }
390
+ }
391
+ return errors
392
+ },
393
+
394
+ /**
395
+ * Save the section's fields in one request, after checking them. A
396
+ * secret is sent only when something was typed.
397
+ *
398
+ * @return {Promise<void>}
399
+ * @spec openspec/changes/settings-fields-section/specs/settings-fields-section/spec.md#requirement-one-save-per-section-with-a-dirty-state
400
+ */
401
+ async save() {
402
+ if (this.saving) {
403
+ return
404
+ }
405
+ this.errors = this.validate();
406
+ const failing = Object.keys(this.errors);
407
+ if (failing.length > 0) {
408
+ this.notice = { type: 'error', text: translate('nextcloud-vue', 'Check the marked fields.') };
409
+ this.$nextTick(() => {
410
+ const el = this.$el?.querySelector?.('[aria-invalid="true"], .cn-settings-field__error');
411
+ const control = el?.closest?.('.cn-settings-field')?.querySelector?.('input, textarea, [role="combobox"]');
412
+ control?.focus?.();
413
+ });
414
+ return
415
+ }
416
+ // The changed fields, one request per save target, each carrying its
417
+ // own fields under their `saveKey`. A secret goes only when typed; an
418
+ // image is uploaded on its own; a field that could not be read is left out.
419
+ const groups = new Map();
420
+ for (const field of this.resolvedFields) {
421
+ const value = this.form[field.key];
422
+ if (field.type === 'image' || this.unreadable[field.key] || this.same(value, this.baseline[field.key])) {
423
+ continue
424
+ }
425
+ if (field.type === 'secret' && (value === '' || value === null || value === undefined)) {
426
+ continue
427
+ }
428
+ const url = field.saveUrl || (field.url ? field.url : this.writeTarget);
429
+ const method = String(field.saveMethod || (field.saveUrl || field.url ? 'POST' : this.saveMethod)).toLowerCase();
430
+ const id = `${method} ${url}`;
431
+ if (!groups.has(id)) {
432
+ groups.set(id, { url, method, fields: [], data: {} });
433
+ }
434
+ const group = groups.get(id);
435
+ group.fields.push(field);
436
+ group.data[field.saveKey || field.key] = value;
437
+ }
438
+ this.saving = true;
439
+ this.notice = null;
440
+ const failed = [];
441
+ const sent = {};
442
+ for (const group of groups.values()) {
443
+ try {
444
+ await axios__default.request({ url: prefixUrl(group.url), method: group.method, data: group.data });
445
+ Object.assign(sent, group.data);
446
+ this.acceptSaved(group.fields);
447
+ } catch (error) {
448
+ failed.push({ group, error });
449
+ }
450
+ }
451
+ this.saving = false;
452
+ if (failed.length === 0) {
453
+ this.notice = { type: 'success', text: translate('nextcloud-vue', 'Saved.') };
454
+ /**
455
+ * @event saved The section saved. Payload: what was sent, by save key.
456
+ */
457
+ this.$emit('saved', sent);
458
+ return
459
+ }
460
+ const reasons = failed.map(({ group, error }) => (groups.size > 1
461
+ ? translate('nextcloud-vue', '{fields}: {reason}', { fields: group.fields.map((field) => field.label).join(', '), reason: serverErrorMessage(error) })
462
+ : serverErrorMessage(error)));
463
+ this.notice = { type: 'error', text: translate('nextcloud-vue', 'The settings could not be saved: {reason}', { reason: reasons.join('; ') }) };
464
+ /**
465
+ * @event save-error A save request failed. Payload: the first error.
466
+ */
467
+ this.$emit('save-error', failed[0].error);
468
+ },
469
+
470
+ /**
471
+ * Take saved fields as the new baseline; a saved secret reads as stored.
472
+ *
473
+ * @param {Array<object>} fields The fields that saved.
474
+ * @return {void}
475
+ */
476
+ acceptSaved(fields) {
477
+ const form = { ...this.form };
478
+ const baseline = { ...this.baseline };
479
+ const stored = { ...this.storedSecrets };
480
+ for (const field of fields) {
481
+ if (field.type === 'secret') {
482
+ stored[field.key] = true;
483
+ form[field.key] = '';
484
+ }
485
+ baseline[field.key] = form[field.key];
486
+ }
487
+ this.form = form;
488
+ this.baseline = baseline;
489
+ this.storedSecrets = stored;
490
+ },
491
+
492
+ /**
493
+ * An image field uploaded a file: its new value is the baseline too.
494
+ *
495
+ * @param {object} field The field.
496
+ * @param {string} value The new image url.
497
+ * @return {void}
498
+ */
499
+ onUploaded(field, value) {
500
+ this.form = { ...this.form, [field.key]: value };
501
+ this.baseline = { ...this.baseline, [field.key]: value };
502
+ this.notice = { type: 'success', text: translate('nextcloud-vue', 'Saved.') };
503
+ },
504
+ },
505
+ };
506
+
507
+ export { script as default };
508
+ //# sourceMappingURL=CnSettingsFieldsSection.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnSettingsFieldsSection.vue2.js","sources":["../../../../src/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<CnSettingsSection\n\t\t:name=\"name\"\n\t\t:description=\"description\"\n\t\t:docUrl=\"docUrl\"\n\t\t:wide=\"wide\"\n\t\t:loading=\"loading\"\n\t\t:error=\"loadError !== ''\"\n\t\t:errorMessage=\"loadError\"\n\t\t:onRetry=\"load\"\n\t\tdata-testid=\"cn-settings-fields-section\">\n\t\t<form v-if=\"!loading && loadError === ''\"\n\t\t\tclass=\"cn-settings-fields\"\n\t\t\tnovalidate\n\t\t\t@submit.prevent=\"save\">\n\t\t\t<div class=\"cn-settings-fields__grid\" :class=\"`cn-settings-fields__grid--${columnCount}`\">\n\t\t\t\t<CnSettingsField\n\t\t\t\t\tv-for=\"field in resolvedFields\"\n\t\t\t\t\t:key=\"field.key\"\n\t\t\t\t\tclass=\"cn-settings-fields__field\"\n\t\t\t\t\t:class=\"{ 'cn-settings-fields__field--wide': field.wide }\"\n\t\t\t\t\t:field=\"field\"\n\t\t\t\t\t:modelValue=\"form[field.key]\"\n\t\t\t\t\t:error=\"errors[field.key] || unreadable[field.key] || ''\"\n\t\t\t\t\t:hint=\"field.hint\"\n\t\t\t\t\t@update:modelValue=\"setValue(field, $event)\"\n\t\t\t\t\t@uploaded=\"onUploaded(field, $event)\" />\n\t\t\t</div>\n\n\t\t\t<NcNoteCard v-if=\"notice\"\n\t\t\t\t:type=\"notice.type\"\n\t\t\t\t:role=\"notice.type === 'error' ? 'alert' : 'status'\"\n\t\t\t\tdata-testid=\"cn-settings-fields-notice\">\n\t\t\t\t{{ notice.text }}\n\t\t\t</NcNoteCard>\n\n\t\t\t<div class=\"cn-settings-section__save cn-settings-fields__save\">\n\t\t\t\t<span v-if=\"dirty\" class=\"cn-settings-fields__dirty\" data-testid=\"cn-settings-fields-dirty\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Unsaved changes') }}\n\t\t\t\t</span>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t:disabled=\"saving || !dirty\"\n\t\t\t\t\tdata-testid=\"cn-settings-fields-save\">\n\t\t\t\t\t<template v-if=\"saving\" #icon>\n\t\t\t\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ resolvedSaveLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</form>\n\t</CnSettingsSection>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { serverErrorMessage } from '../../utils/serverErrorMessage.js'\nimport { CnSettingsField } from '../CnSettingsField/index.js'\nimport { CnSettingsSection } from '../CnSettingsSection/index.js'\n\n/**\n * Up to two initials of a name, for an image field without an image.\n *\n * @param {unknown} name The name.\n * @return {string}\n */\nfunction initialsOf(name) {\n\treturn String(name || '').trim().split(/\\s+/).filter(Boolean).slice(0, 2).map((word) => word[0].toUpperCase()).join('')\n}\n\n/**\n * CnSettingsFieldsSection - a settings section declared as fields: it reads\n * the app's settings, draws the fields in one to three columns, and saves\n * them with ONE Save button for the section.\n *\n * It reads `GET url` (default `/apps/<appId>/api/settings`) and writes the\n * section's fields with `PUT saveUrl` (default the same url). The Save button\n * is on while something changed (\"Unsaved changes\"); a save checks the\n * fields first (required, pattern, min, max) and marks each failing field,\n * then says it saved or why it did not. A `secret` field never shows the\n * stored value: it says a value is stored, and is sent only when something\n * was typed.\n *\n * It renders inside CnAdminSettingsShell and CnSettingsPage, in the default\n * and the board look.\n *\n * ```vue\n * <CnSettingsFieldsSection\n * app-id=\"myapp\"\n * name=\"Connection\"\n * :columns=\"2\"\n * :fields=\"[\n * { key: 'baseUrl', label: 'Server address', required: true, pattern: '^https://', patternMessage: 'Starts with https://' },\n * { key: 'apiKey', label: 'API key', type: 'secret' },\n * { key: 'mode', label: 'Mode', type: 'select', options: [{ value: 'live', label: 'Live' }, { value: 'test', label: 'Test' }] },\n * { key: 'enabled', label: 'Enabled', type: 'switch', hint: 'Off stops every call.' },\n * ]\" />\n * ```\n */\nexport default {\n\tname: 'CnSettingsFieldsSection',\n\n\tcomponents: { CnSettingsField, CnSettingsSection, NcButton, NcLoadingIcon, NcNoteCard },\n\n\tinject: {\n\t\t/** The app's translate function; identity outside CnAppRoot. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The section title. */\n\t\tname: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** The line under the title. */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** A documentation link beside the title. */\n\t\tdocUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Span the whole settings grid in the board look. */\n\t\twide: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * The fields: `[{ key, label, type?, hint?, placeholder?, required?,\n\t\t * pattern?, patternMessage?, min?, max?, rows?, options?, optionsUrl?,\n\t\t * optionsPath?, optionLabel?, optionValue?, default?, readOnly?, wide?,\n\t\t * url?, valuePath?, saveUrl?, saveMethod?, saveKey?, uploadUrl?,\n\t\t * uploadField?, uploadParams?, uploadValuePath?, initials?, initialsFrom? }]`.\n\t\t * `type` is `text` (default), `textarea`, `number`, `switch`, `select`,\n\t\t * `secret` or `image`. `key` names the setting; a dotted key reads a\n\t\t * nested value and is written as written. A field with its own `url`\n\t\t * is read there (at `valuePath`, default its key) and saved to its\n\t\t * `saveUrl` (default that `url`) with `saveMethod` (default POST) under\n\t\t * `saveKey` (default its key), so one section can hold the settings of\n\t\t * several apps. An `image` uploads at once to `uploadUrl`. Labels,\n\t\t * hints, placeholders and messages go through the app's translate\n\t\t * function.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t * @spec openspec/changes/settings-fields-section/specs/settings-fields-section/spec.md#requirement-a-section-declared-as-fields-reads-and-saves-the-apps-settings\n\t\t */\n\t\tfields: {\n\t\t\ttype: Array,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Columns of the field grid: 1, 2 or 3. A narrow card stacks them. */\n\t\tcolumns: {\n\t\t\ttype: Number,\n\t\t\tdefault: 1,\n\t\t\tvalidator: (value) => [1, 2, 3].includes(value),\n\t\t},\n\n\t\t/** The app id, for the default url `/apps/<appId>/api/settings`. */\n\t\tappId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Where the settings are read (`GET`); app-absolute or a full URL. */\n\t\turl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Where the section saves; defaults to `url`. */\n\t\tsaveUrl: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The method of the save request. */\n\t\tsaveMethod: {\n\t\t\ttype: String,\n\t\t\tdefault: 'PUT',\n\t\t\tvalidator: (value) => ['PUT', 'POST', 'PATCH'].includes(String(value).toUpperCase()),\n\t\t},\n\n\t\t/** A dotted path to the values in the GET answer; empty reads the answer itself. */\n\t\tvaluesPath: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The Save button's label. Defaults to \"Save\". */\n\t\tsaveLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['saved', 'save-error'],\n\n\tdata() {\n\t\treturn {\n\t\t\tform: {},\n\t\t\tbaseline: {},\n\t\t\tstoredSecrets: {},\n\t\t\t// Field key to the message of a read target that failed.\n\t\t\tunreadable: {},\n\t\t\terrors: {},\n\t\t\tloading: true,\n\t\t\tloadError: '',\n\t\t\tsaving: false,\n\t\t\tnotice: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {number} The column count, 1 to 3. */\n\t\tcolumnCount() {\n\t\t\treturn [1, 2, 3].includes(this.columns) ? this.columns : 1\n\t\t},\n\n\t\t/** @return {string} Where the settings are read: `url`, else the app's settings endpoint. */\n\t\treadTarget() {\n\t\t\treturn this.url || (this.appId ? `/apps/${this.appId}/api/settings` : '')\n\t\t},\n\n\t\t/** @return {string} Where the section saves: `saveUrl`, else the read target. */\n\t\twriteTarget() {\n\t\t\treturn this.saveUrl || this.readTarget\n\t\t},\n\n\t\t/**\n\t\t * The declared fields with their text translated and a secret's stored state.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tresolvedFields() {\n\t\t\treturn (Array.isArray(this.fields) ? this.fields : [])\n\t\t\t\t.filter((field) => field && typeof field.key === 'string' && field.key !== '')\n\t\t\t\t.map((field) => ({\n\t\t\t\t\t...field,\n\t\t\t\t\tlabel: this.tr(field.label || field.key),\n\t\t\t\t\thint: this.tr(field.hint || ''),\n\t\t\t\t\tplaceholder: this.tr(field.placeholder || ''),\n\t\t\t\t\tsecretSet: field.type === 'secret' ? !!this.storedSecrets[field.key] : undefined,\n\t\t\t\t\tinitials: field.type === 'image' ? initialsOf(field.initialsFrom ? this.form[field.initialsFrom] : field.initials) : undefined,\n\t\t\t\t\treadOnly: field.readOnly || !!this.unreadable[field.key],\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Whether something changed since the last load or save.\n\t\t *\n\t\t * @return {boolean}\n\t\t * @spec openspec/changes/settings-fields-section/specs/settings-fields-section/spec.md#requirement-one-save-per-section-with-a-dirty-state\n\t\t */\n\t\tdirty() {\n\t\t\treturn this.resolvedFields.some((field) => field.type !== 'image' && !this.same(this.form[field.key], this.baseline[field.key]))\n\t\t},\n\n\t\t/** @return {string} The Save button's label. */\n\t\tresolvedSaveLabel() {\n\t\t\treturn this.saveLabel ? this.tr(this.saveLabel) : t('nextcloud-vue', 'Save')\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 * @param {string} value A declared string.\n\t\t * @return {string} It through the app's translate function.\n\t\t */\n\t\ttr(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : (value || '')\n\t\t},\n\n\t\t/**\n\t\t * @param {unknown} a A value.\n\t\t * @param {unknown} b Another.\n\t\t * @return {boolean} Whether they count as the same setting.\n\t\t */\n\t\tsame(a, b) {\n\t\t\tconst empty = (v) => v === null || v === undefined || v === ''\n\t\t\tif (empty(a) && empty(b)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn JSON.stringify(a) === JSON.stringify(b)\n\t\t},\n\n\t\t/**\n\t\t * Change a field's value; its error goes until the next save.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {unknown} value The value.\n\t\t * @return {void}\n\t\t */\n\t\tsetValue(field, value) {\n\t\t\tthis.form = { ...this.form, [field.key]: value }\n\t\t\tif (this.errors[field.key]) {\n\t\t\t\tconst { [field.key]: _gone, ...rest } = this.errors\n\t\t\t\tthis.errors = rest\n\t\t\t}\n\t\t\tthis.notice = null\n\t\t},\n\n\t\t/**\n\t\t * Read the settings and fill the fields; a secret's value is never put\n\t\t * in the form, only whether one is stored.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tthis.loadError = ''\n\t\t\ttry {\n\t\t\t\t// One GET per distinct read target; the section's own target must\n\t\t\t\t// answer, a field's own target may fail on its own.\n\t\t\t\tconst targets = [...new Set(this.fields\n\t\t\t\t\t.filter((field) => field && field.key)\n\t\t\t\t\t.map((field) => this.readUrlOf(field))\n\t\t\t\t\t.filter(Boolean))]\n\t\t\t\tconst answers = {}\n\t\t\t\tconst failures = {}\n\t\t\t\tawait Promise.all(targets.map(async (target) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst response = await axios.get(prefixUrl(target))\n\t\t\t\t\t\tanswers[target] = response?.data ?? {}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tfailures[target] = error\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t\t\tif (this.readTarget && failures[this.readTarget]) {\n\t\t\t\t\tthrow failures[this.readTarget]\n\t\t\t\t}\n\t\t\t\tconst form = {}\n\t\t\t\tconst stored = {}\n\t\t\t\tconst unreadable = {}\n\t\t\t\tfor (const field of this.fields || []) {\n\t\t\t\t\tif (!field || !field.key) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tconst target = this.readUrlOf(field)\n\t\t\t\t\tif (target && failures[target]) {\n\t\t\t\t\t\tunreadable[field.key] = t('nextcloud-vue', 'This value could not be loaded: {reason}', { reason: serverErrorMessage(failures[target]) })\n\t\t\t\t\t}\n\t\t\t\t\tconst raw = this.readValue(field, target ? answers[target] : null)\n\t\t\t\t\tif (field.type === 'secret') {\n\t\t\t\t\t\tstored[field.key] = raw !== null && raw !== undefined && raw !== '' && raw !== false\n\t\t\t\t\t\tform[field.key] = ''\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tform[field.key] = raw === undefined ? (field.default ?? (field.type === 'switch' ? false : '')) : raw\n\t\t\t\t}\n\t\t\t\tthis.form = form\n\t\t\t\tthis.baseline = { ...form }\n\t\t\t\tthis.storedSecrets = stored\n\t\t\t\tthis.unreadable = unreadable\n\t\t\t\tthis.errors = {}\n\t\t\t} catch (error) {\n\t\t\t\tthis.loadError = t('nextcloud-vue', 'The settings could not be loaded: {reason}', { reason: serverErrorMessage(error) })\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 * Where a field is read: its own `url`, else the section's.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @return {string}\n\t\t * @spec openspec/changes/settings-fields-section/specs/settings-fields-section/spec.md#requirement-a-field-can-read-and-save-elsewhere\n\t\t */\n\t\treadUrlOf(field) {\n\t\t\treturn field.url || this.readTarget\n\t\t},\n\n\t\t/**\n\t\t * A field's value in its read answer: at its `valuePath` (a field with its\n\t\t * own `url`), else at the section's `valuesPath`, then its `key`.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {object|null} data The answer of the field's read target.\n\t\t * @return {unknown}\n\t\t */\n\t\treadValue(field, data) {\n\t\t\tif (!data || typeof data !== 'object') {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t\tif (field.url) {\n\t\t\t\treturn readPath(data, field.valuePath || field.key)\n\t\t\t}\n\t\t\tconst values = (this.valuesPath ? readPath(data, this.valuesPath) : data) || {}\n\t\t\treturn Object.hasOwn(values, field.key) ? values[field.key] : readPath(values, field.key)\n\t\t},\n\n\t\t/**\n\t\t * Check the fields: required, pattern, and a number's min and max.\n\t\t *\n\t\t * @return {object} Field key to message; empty when all pass.\n\t\t * @spec openspec/changes/settings-fields-section/specs/settings-fields-section/spec.md#requirement-a-failing-field-says-why\n\t\t */\n\t\tvalidate() {\n\t\t\tconst errors = {}\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tconst value = this.form[field.key]\n\t\t\t\tconst empty = value === null || value === undefined || value === ''\n\t\t\t\tif (field.type === 'switch' || field.type === 'image' || this.unreadable[field.key]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (field.required && empty && !(field.type === 'secret' && field.secretSet)) {\n\t\t\t\t\terrors[field.key] = t('nextcloud-vue', '{field} is required.', { field: field.label })\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (empty) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (field.pattern && !new RegExp(field.pattern).test(String(value))) {\n\t\t\t\t\terrors[field.key] = field.patternMessage ? this.tr(field.patternMessage) : t('nextcloud-vue', '{field} is not in the right form.', { field: field.label })\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (field.type === 'number') {\n\t\t\t\t\tconst number = Number(value)\n\t\t\t\t\tif (Number.isNaN(number)) {\n\t\t\t\t\t\terrors[field.key] = t('nextcloud-vue', '{field} must be a number.', { field: field.label })\n\t\t\t\t\t} else if (field.min !== undefined && number < field.min) {\n\t\t\t\t\t\terrors[field.key] = t('nextcloud-vue', '{field} must be at least {min}.', { field: field.label, min: field.min })\n\t\t\t\t\t} else if (field.max !== undefined && number > field.max) {\n\t\t\t\t\t\terrors[field.key] = t('nextcloud-vue', '{field} must be at most {max}.', { field: field.label, max: field.max })\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn errors\n\t\t},\n\n\t\t/**\n\t\t * Save the section's fields in one request, after checking them. A\n\t\t * secret is sent only when something was typed.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/settings-fields-section/specs/settings-fields-section/spec.md#requirement-one-save-per-section-with-a-dirty-state\n\t\t */\n\t\tasync save() {\n\t\t\tif (this.saving) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.errors = this.validate()\n\t\t\tconst failing = Object.keys(this.errors)\n\t\t\tif (failing.length > 0) {\n\t\t\t\tthis.notice = { type: 'error', text: t('nextcloud-vue', 'Check the marked fields.') }\n\t\t\t\tthis.$nextTick(() => {\n\t\t\t\t\tconst el = this.$el?.querySelector?.('[aria-invalid=\"true\"], .cn-settings-field__error')\n\t\t\t\t\tconst control = el?.closest?.('.cn-settings-field')?.querySelector?.('input, textarea, [role=\"combobox\"]')\n\t\t\t\t\tcontrol?.focus?.()\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// The changed fields, one request per save target, each carrying its\n\t\t\t// own fields under their `saveKey`. A secret goes only when typed; an\n\t\t\t// image is uploaded on its own; a field that could not be read is left out.\n\t\t\tconst groups = new Map()\n\t\t\tfor (const field of this.resolvedFields) {\n\t\t\t\tconst value = this.form[field.key]\n\t\t\t\tif (field.type === 'image' || this.unreadable[field.key] || this.same(value, this.baseline[field.key])) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif (field.type === 'secret' && (value === '' || value === null || value === undefined)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst url = field.saveUrl || (field.url ? field.url : this.writeTarget)\n\t\t\t\tconst method = String(field.saveMethod || (field.saveUrl || field.url ? 'POST' : this.saveMethod)).toLowerCase()\n\t\t\t\tconst id = `${method} ${url}`\n\t\t\t\tif (!groups.has(id)) {\n\t\t\t\t\tgroups.set(id, { url, method, fields: [], data: {} })\n\t\t\t\t}\n\t\t\t\tconst group = groups.get(id)\n\t\t\t\tgroup.fields.push(field)\n\t\t\t\tgroup.data[field.saveKey || field.key] = value\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tthis.notice = null\n\t\t\tconst failed = []\n\t\t\tconst sent = {}\n\t\t\tfor (const group of groups.values()) {\n\t\t\t\ttry {\n\t\t\t\t\tawait axios.request({ url: prefixUrl(group.url), method: group.method, data: group.data })\n\t\t\t\t\tObject.assign(sent, group.data)\n\t\t\t\t\tthis.acceptSaved(group.fields)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tfailed.push({ group, error })\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.saving = false\n\t\t\tif (failed.length === 0) {\n\t\t\t\tthis.notice = { type: 'success', text: t('nextcloud-vue', 'Saved.') }\n\t\t\t\t/**\n\t\t\t\t * @event saved The section saved. Payload: what was sent, by save key.\n\t\t\t\t */\n\t\t\t\tthis.$emit('saved', sent)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst reasons = failed.map(({ group, error }) => (groups.size > 1\n\t\t\t\t? t('nextcloud-vue', '{fields}: {reason}', { fields: group.fields.map((field) => field.label).join(', '), reason: serverErrorMessage(error) })\n\t\t\t\t: serverErrorMessage(error)))\n\t\t\tthis.notice = { type: 'error', text: t('nextcloud-vue', 'The settings could not be saved: {reason}', { reason: reasons.join('; ') }) }\n\t\t\t/**\n\t\t\t * @event save-error A save request failed. Payload: the first error.\n\t\t\t */\n\t\t\tthis.$emit('save-error', failed[0].error)\n\t\t},\n\n\t\t/**\n\t\t * Take saved fields as the new baseline; a saved secret reads as stored.\n\t\t *\n\t\t * @param {Array<object>} fields The fields that saved.\n\t\t * @return {void}\n\t\t */\n\t\tacceptSaved(fields) {\n\t\t\tconst form = { ...this.form }\n\t\t\tconst baseline = { ...this.baseline }\n\t\t\tconst stored = { ...this.storedSecrets }\n\t\t\tfor (const field of fields) {\n\t\t\t\tif (field.type === 'secret') {\n\t\t\t\t\tstored[field.key] = true\n\t\t\t\t\tform[field.key] = ''\n\t\t\t\t}\n\t\t\t\tbaseline[field.key] = form[field.key]\n\t\t\t}\n\t\t\tthis.form = form\n\t\t\tthis.baseline = baseline\n\t\t\tthis.storedSecrets = stored\n\t\t},\n\n\t\t/**\n\t\t * An image field uploaded a file: its new value is the baseline too.\n\t\t *\n\t\t * @param {object} field The field.\n\t\t * @param {string} value The new image url.\n\t\t * @return {void}\n\t\t */\n\t\tonUploaded(field, value) {\n\t\t\tthis.form = { ...this.form, [field.key]: value }\n\t\t\tthis.baseline = { ...this.baseline, [field.key]: value }\n\t\t\tthis.notice = { type: 'success', text: t('nextcloud-vue', 'Saved.') }\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-settings-fields {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 16px;\n\tcontainer-type: inline-size;\n}\n\n.cn-settings-fields__grid {\n\tdisplay: grid;\n\tgrid-template-columns: minmax(0, 1fr);\n\tgap: 16px 20px;\n}\n\n/* Two or three columns where the card is wide enough; stacked otherwise. */\n@container (min-width: 520px) {\n\t.cn-settings-fields__grid--2,\n\t.cn-settings-fields__grid--3 {\n\t\tgrid-template-columns: repeat(2, minmax(0, 1fr));\n\t}\n}\n\n@container (min-width: 780px) {\n\t.cn-settings-fields__grid--3 {\n\t\tgrid-template-columns: repeat(3, minmax(0, 1fr));\n\t}\n}\n\n.cn-settings-fields__field--wide {\n\tgrid-column: 1 / -1;\n}\n\n.cn-settings-fields__save {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tjustify-content: flex-end;\n\tgap: 12px;\n}\n\n.cn-settings-fields__dirty {\n\tfont-size: 14px;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["CnSettingsField","CnSettingsSection","t","axios"],"mappings":"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"}