@conduction/nextcloud-vue 2.56.0-beta.48 → 2.56.0-beta.49
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.
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +46 -28
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +17 -10
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBrandBar/CnBrandBar.vue2.js.map +1 -1
- package/dist/esm/components/CnBrandBar/CnBrandBar.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +56 -16
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue.js +9 -0
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue.js.map +1 -0
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue2.js +240 -0
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue3.js +65 -0
- package/dist/esm/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnDeviceFramesWidget/index.js +30 -0
- package/dist/esm/components/CnDeviceFramesWidget/index.js.map +1 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +17 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +30 -31
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue.js +6 -0
- package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue.js.map +1 -0
- package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue2.js +90 -0
- package/dist/esm/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue2.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +48 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +20 -2
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue.js +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js +230 -25
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js.map +1 -1
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js +447 -221
- package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +18 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +18 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +356 -114
- package/dist/esm/utils/libraryWidgetKeys.js +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +6964 -5943
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +356 -114
- package/l10n/en.json +18 -1
- package/l10n/nl.json +18 -1
- package/package.json +1 -1
- package/src/components/CnAppRoot/CnAppRoot.vue +20 -0
- package/src/components/CnBrandBar/CnBrandBar.vue +5 -4
- package/src/components/CnDetailPage/CnDetailPage.vue +55 -15
- package/src/components/CnDeviceFramesWidget/CnDeviceFramesWidget.vue +360 -0
- package/src/components/CnDeviceFramesWidget/index.js +29 -0
- package/src/components/CnFactsWidget/CnFactsWidget.vue +46 -2
- package/src/components/CnHeaderSearchLabel/CnHeaderSearchLabel.vue +107 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +81 -1
- package/src/components/CnRegisterMapping/CnRegisterMapping.md +10 -0
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +511 -28
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/index.js +1 -0
- package/src/index.js +1 -0
- package/src/schemas/app-manifest-v2.schema.json +6 -1
- package/src/utils/libraryWidgetKeys.js +1 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
:errorMessage="registersError || ''"
|
|
10
10
|
:onRetry="loadRegisters">
|
|
11
11
|
<!-- Action buttons -->
|
|
12
|
-
<template #actions>
|
|
12
|
+
<template v-if="hasHeaderActions" #actions>
|
|
13
13
|
<NcButton
|
|
14
|
-
v-if="showSaveButton"
|
|
14
|
+
v-if="showSaveButton && !isBoardLook"
|
|
15
15
|
variant="primary"
|
|
16
16
|
:disabled="saving || !hasChanges"
|
|
17
17
|
@click="handleSave">
|
|
@@ -36,7 +36,132 @@
|
|
|
36
36
|
</template>
|
|
37
37
|
|
|
38
38
|
<!-- Group sections -->
|
|
39
|
-
<div
|
|
39
|
+
<div
|
|
40
|
+
v-if="isBoardLook && !registersLoading && !registersError"
|
|
41
|
+
class="cn-register-mapping cn-register-mapping--board"
|
|
42
|
+
data-testid="cn-register-mapping-board">
|
|
43
|
+
<div class="cn-register-mapping__board-group" data-testid="cn-register-mapping-board-group">
|
|
44
|
+
<div class="cn-register-mapping__board-head">
|
|
45
|
+
<h3 class="cn-register-mapping__board-title">
|
|
46
|
+
{{ activeGroup.name }}
|
|
47
|
+
</h3>
|
|
48
|
+
<CnStatusBadge
|
|
49
|
+
:label="groupStatusLabel(activeGroupIdx, true)"
|
|
50
|
+
:variant="groupStatusVariant(activeGroupIdx)"
|
|
51
|
+
data-testid="cn-register-mapping-board-status" />
|
|
52
|
+
</div>
|
|
53
|
+
<p v-if="activeGroup.description" class="cn-register-mapping__group-description">
|
|
54
|
+
{{ activeGroup.description }}
|
|
55
|
+
</p>
|
|
56
|
+
<div class="cn-register-mapping__register-select cn-register-mapping__board-select">
|
|
57
|
+
<NcSelect
|
|
58
|
+
:modelValue="selectedRegister(activeGroupIdx)"
|
|
59
|
+
:options="registerSelectOptions"
|
|
60
|
+
:placeholder="labels.selectRegister"
|
|
61
|
+
:inputLabel="labels.register"
|
|
62
|
+
label="label"
|
|
63
|
+
trackBy="value"
|
|
64
|
+
@update:modelValue="handleRegisterChange(activeGroupIdx, $event)" />
|
|
65
|
+
</div>
|
|
66
|
+
<table v-if="selectedRegister(activeGroupIdx)" class="cn-register-mapping__board-table">
|
|
67
|
+
<thead>
|
|
68
|
+
<tr>
|
|
69
|
+
<th scope="col">
|
|
70
|
+
{{ t('nextcloud-vue', 'Object type') }}
|
|
71
|
+
</th>
|
|
72
|
+
<th scope="col">
|
|
73
|
+
{{ labels.schema }}
|
|
74
|
+
</th>
|
|
75
|
+
</tr>
|
|
76
|
+
</thead>
|
|
77
|
+
<tbody>
|
|
78
|
+
<template v-for="type in boardVisibleTypes" :key="type.slug + '-board'">
|
|
79
|
+
<tr data-testid="cn-register-mapping-board-row">
|
|
80
|
+
<td>{{ type.label }}</td>
|
|
81
|
+
<td>
|
|
82
|
+
<button
|
|
83
|
+
type="button"
|
|
84
|
+
class="cn-register-mapping__board-schema"
|
|
85
|
+
:aria-expanded="String(isExpanded(activeGroupIdx, type.slug))"
|
|
86
|
+
:aria-label="t('nextcloud-vue', 'Change the schema for {type}', { type: type.label })"
|
|
87
|
+
@click="toggleExpand(activeGroupIdx, type.slug)">
|
|
88
|
+
<CheckCircle
|
|
89
|
+
v-if="schemaValue(activeGroupIdx, type)"
|
|
90
|
+
class="cn-register-mapping__board-check"
|
|
91
|
+
:size="16" />
|
|
92
|
+
{{ schemaLabel(activeGroupIdx, type) || t('nextcloud-vue', 'Not set') }}
|
|
93
|
+
</button>
|
|
94
|
+
</td>
|
|
95
|
+
</tr>
|
|
96
|
+
<tr v-if="isExpanded(activeGroupIdx, type.slug)" class="cn-register-mapping__board-detail">
|
|
97
|
+
<td colspan="2">
|
|
98
|
+
<p v-if="type.description" class="cn-register-mapping__type-description">
|
|
99
|
+
{{ type.description }}
|
|
100
|
+
</p>
|
|
101
|
+
<NcSelect
|
|
102
|
+
:modelValue="selectedSchema(activeGroupIdx, type)"
|
|
103
|
+
:options="schemaSelectOptions(activeGroupIdx)"
|
|
104
|
+
:placeholder="labels.selectSchema"
|
|
105
|
+
:inputLabel="labels.schema"
|
|
106
|
+
label="label"
|
|
107
|
+
trackBy="value"
|
|
108
|
+
@update:modelValue="handleSchemaChange(activeGroupIdx, type, $event)" />
|
|
109
|
+
</td>
|
|
110
|
+
</tr>
|
|
111
|
+
</template>
|
|
112
|
+
</tbody>
|
|
113
|
+
</table>
|
|
114
|
+
<p
|
|
115
|
+
v-if="selectedRegister(activeGroupIdx) && boardHiddenTypes.length > 0"
|
|
116
|
+
class="cn-register-mapping__board-more"
|
|
117
|
+
data-testid="cn-register-mapping-board-more">
|
|
118
|
+
{{ boardHiddenSummary }}
|
|
119
|
+
<button type="button" class="cn-register-mapping__board-link" @click="showAllTypes = true">
|
|
120
|
+
{{ t('nextcloud-vue', 'Show all types') }}
|
|
121
|
+
</button>
|
|
122
|
+
</p>
|
|
123
|
+
<NcNoteCard v-if="!selectedRegister(activeGroupIdx)" type="info">
|
|
124
|
+
{{ labels.selectRegister }}
|
|
125
|
+
</NcNoteCard>
|
|
126
|
+
<NcNoteCard
|
|
127
|
+
v-if="selectedRegister(activeGroupIdx) && schemaSelectOptions(activeGroupIdx).length === 0"
|
|
128
|
+
type="warning">
|
|
129
|
+
{{ labels.noSchemas }}
|
|
130
|
+
</NcNoteCard>
|
|
131
|
+
</div>
|
|
132
|
+
<ul v-if="boardOtherGroups.length > 0" class="cn-register-mapping__board-groups">
|
|
133
|
+
<li v-for="item in boardOtherGroups" :key="item.idx">
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
class="cn-register-mapping__board-group-row"
|
|
137
|
+
data-testid="cn-register-mapping-board-group-row"
|
|
138
|
+
@click="openBoardGroup(item.idx)">
|
|
139
|
+
<ChevronRight :size="18" class="cn-register-mapping__board-chevron" />
|
|
140
|
+
<strong class="cn-register-mapping__board-group-name">{{ item.group.name }}</strong>
|
|
141
|
+
<span class="cn-register-mapping__board-group-count">
|
|
142
|
+
{{ n('nextcloud-vue', '%n type', '%n types', item.group.types.length) }}
|
|
143
|
+
</span>
|
|
144
|
+
<CnStatusBadge
|
|
145
|
+
:label="groupStatusLabel(item.idx, false)"
|
|
146
|
+
:variant="groupStatusVariant(item.idx)" />
|
|
147
|
+
</button>
|
|
148
|
+
</li>
|
|
149
|
+
</ul>
|
|
150
|
+
<div v-if="showSaveButton" class="cn-register-mapping__board-save">
|
|
151
|
+
<NcButton
|
|
152
|
+
variant="primary"
|
|
153
|
+
:disabled="saving || !hasChanges"
|
|
154
|
+
data-testid="cn-register-mapping-board-save"
|
|
155
|
+
@click="handleSave">
|
|
156
|
+
<template v-if="saving" #icon>
|
|
157
|
+
<NcLoadingIcon :size="20" />
|
|
158
|
+
</template>
|
|
159
|
+
{{ saving ? t('nextcloud-vue', 'Saving…') : saveButtonText }}
|
|
160
|
+
</NcButton>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div v-else-if="!registersLoading && !registersError" class="cn-register-mapping">
|
|
40
165
|
<div
|
|
41
166
|
v-for="(group, groupIdx) in groups"
|
|
42
167
|
:key="groupIdx"
|
|
@@ -186,14 +311,18 @@
|
|
|
186
311
|
</template>
|
|
187
312
|
|
|
188
313
|
<script>
|
|
189
|
-
import { translate as t } from '@nextcloud/l10n'
|
|
314
|
+
import { translatePlural as n, translate as t } from '@nextcloud/l10n'
|
|
190
315
|
import { NcButton, NcLoadingIcon, NcNoteCard, NcSelect } from '@nextcloud/vue'
|
|
316
|
+
import CheckCircle from 'vue-material-design-icons/CheckCircle.vue'
|
|
191
317
|
import ChevronDown from 'vue-material-design-icons/ChevronDown.vue'
|
|
318
|
+
import ChevronRight from 'vue-material-design-icons/ChevronRight.vue'
|
|
192
319
|
import ChevronUp from 'vue-material-design-icons/ChevronUp.vue'
|
|
193
320
|
import ContentSave from 'vue-material-design-icons/ContentSave.vue'
|
|
194
321
|
import Refresh from 'vue-material-design-icons/Refresh.vue'
|
|
322
|
+
import { normalizeLook } from '../../composables/useLook.js'
|
|
195
323
|
import { buildHeaders, prefixUrl } from '../../utils/headers.js'
|
|
196
324
|
import { CnSettingsSection } from '../CnSettingsSection/index.js'
|
|
325
|
+
import { CnStatusBadge } from '../CnStatusBadge/index.js'
|
|
197
326
|
|
|
198
327
|
let mappingCount = 0
|
|
199
328
|
|
|
@@ -205,6 +334,13 @@ let mappingCount = 0
|
|
|
205
334
|
* Supports multiple register groups (stacked sections) with expandable
|
|
206
335
|
* type rows for manual schema override.
|
|
207
336
|
*
|
|
337
|
+
* Only the registers the configuration maps have their schemas fetched; the
|
|
338
|
+
* register select lists every register without its schemas.
|
|
339
|
+
*
|
|
340
|
+
* In the board look one group is open as a type/schema table under a status
|
|
341
|
+
* chip, the other groups are rows with their type count and status, and the
|
|
342
|
+
* save button closes the section.
|
|
343
|
+
*
|
|
208
344
|
* Single register (Pipelinq)
|
|
209
345
|
* ```vue
|
|
210
346
|
* <CnRegisterMapping
|
|
@@ -238,16 +374,24 @@ export default {
|
|
|
238
374
|
|
|
239
375
|
components: {
|
|
240
376
|
CnSettingsSection,
|
|
377
|
+
CnStatusBadge,
|
|
241
378
|
NcButton,
|
|
242
379
|
NcLoadingIcon,
|
|
243
380
|
NcNoteCard,
|
|
244
381
|
NcSelect,
|
|
245
382
|
ContentSave,
|
|
246
383
|
Refresh,
|
|
384
|
+
CheckCircle,
|
|
247
385
|
ChevronDown,
|
|
386
|
+
ChevronRight,
|
|
248
387
|
ChevronUp,
|
|
249
388
|
},
|
|
250
389
|
|
|
390
|
+
inject: {
|
|
391
|
+
/** The look CnAppRoot or CnAdminSettingsShell provides. */
|
|
392
|
+
cnLook: { default: 'nextcloud' },
|
|
393
|
+
},
|
|
394
|
+
|
|
251
395
|
props: {
|
|
252
396
|
/** Section title */
|
|
253
397
|
name: {
|
|
@@ -335,6 +479,28 @@ export default {
|
|
|
335
479
|
default: null,
|
|
336
480
|
},
|
|
337
481
|
|
|
482
|
+
/**
|
|
483
|
+
* The look the mapping is drawn in: `board` or `nextcloud`. Empty
|
|
484
|
+
* follows the provided `cnLook`.
|
|
485
|
+
*
|
|
486
|
+
* @spec openspec/changes/register-mapping-board-look/specs/register-mapping/spec.md#requirement-the-board-look-draws-one-open-group-and-compact-rows
|
|
487
|
+
*/
|
|
488
|
+
look: {
|
|
489
|
+
type: String,
|
|
490
|
+
default: '',
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Board look: how many types the open group lists before a "show all"
|
|
495
|
+
* line. 0 lists every type.
|
|
496
|
+
*
|
|
497
|
+
* @spec openspec/changes/register-mapping-board-look/specs/register-mapping/spec.md#requirement-the-board-look-draws-one-open-group-and-compact-rows
|
|
498
|
+
*/
|
|
499
|
+
boardRowLimit: {
|
|
500
|
+
type: Number,
|
|
501
|
+
default: 4,
|
|
502
|
+
},
|
|
503
|
+
|
|
338
504
|
/** Auto-match schema titles to type slugs on register change */
|
|
339
505
|
autoMatch: {
|
|
340
506
|
type: Boolean,
|
|
@@ -372,6 +538,12 @@ export default {
|
|
|
372
538
|
expandedRows: {},
|
|
373
539
|
// The groups opened by the reader, by index.
|
|
374
540
|
openGroups: {},
|
|
541
|
+
// Board look: the group drawn open, and whether it lists every type.
|
|
542
|
+
activeGroupIdx: 0,
|
|
543
|
+
// Schema requests by register id, so a register is fetched once.
|
|
544
|
+
schemaRequests: {},
|
|
545
|
+
registersLoaded: false,
|
|
546
|
+
showAllTypes: false,
|
|
375
547
|
uid: ++mappingCount,
|
|
376
548
|
}
|
|
377
549
|
},
|
|
@@ -395,6 +567,79 @@ export default {
|
|
|
395
567
|
return this.collapseGroups === null ? this.groups.length > 1 : this.collapseGroups
|
|
396
568
|
},
|
|
397
569
|
|
|
570
|
+
/**
|
|
571
|
+
* Whether the board look applies.
|
|
572
|
+
*
|
|
573
|
+
* @return {boolean}
|
|
574
|
+
*/
|
|
575
|
+
isBoardLook() {
|
|
576
|
+
return normalizeLook(this.look || this.cnLook) === 'board'
|
|
577
|
+
},
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* The group the board look draws open.
|
|
581
|
+
*
|
|
582
|
+
* @return {object}
|
|
583
|
+
*/
|
|
584
|
+
activeGroup() {
|
|
585
|
+
return this.groups[this.activeGroupIdx] || this.groups[0]
|
|
586
|
+
},
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* The open group's types the board table lists.
|
|
590
|
+
*
|
|
591
|
+
* @return {Array<object>}
|
|
592
|
+
*/
|
|
593
|
+
boardVisibleTypes() {
|
|
594
|
+
const types = this.activeGroup.types
|
|
595
|
+
if (this.showAllTypes || this.boardRowLimit <= 0) {
|
|
596
|
+
return types
|
|
597
|
+
}
|
|
598
|
+
return types.slice(0, this.boardRowLimit)
|
|
599
|
+
},
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* The open group's types below the row limit.
|
|
603
|
+
*
|
|
604
|
+
* @return {Array<object>}
|
|
605
|
+
*/
|
|
606
|
+
boardHiddenTypes() {
|
|
607
|
+
return this.activeGroup.types.slice(this.boardVisibleTypes.length)
|
|
608
|
+
},
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* The line under the board table that counts the types not listed.
|
|
612
|
+
*
|
|
613
|
+
* @return {string}
|
|
614
|
+
*/
|
|
615
|
+
boardHiddenSummary() {
|
|
616
|
+
const count = this.boardHiddenTypes.length
|
|
617
|
+
const allSet = this.boardHiddenTypes.every((type) => !!this.schemaValue(this.activeGroupIdx, type))
|
|
618
|
+
return allSet
|
|
619
|
+
? n('nextcloud-vue', 'And %n more type in this group, set up.', 'And %n more types in this group, all set up.', count)
|
|
620
|
+
: n('nextcloud-vue', 'And %n more type in this group.', 'And %n more types in this group.', count)
|
|
621
|
+
},
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* The groups the board look draws as compact rows, with their index.
|
|
625
|
+
*
|
|
626
|
+
* @return {Array<{group: object, idx: number}>}
|
|
627
|
+
*/
|
|
628
|
+
boardOtherGroups() {
|
|
629
|
+
return this.groups
|
|
630
|
+
.map((group, idx) => ({ group, idx }))
|
|
631
|
+
.filter((item) => item.idx !== this.activeGroupIdx)
|
|
632
|
+
},
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Whether the section header draws buttons; the board look saves at the end.
|
|
636
|
+
*
|
|
637
|
+
* @return {boolean}
|
|
638
|
+
*/
|
|
639
|
+
hasHeaderActions() {
|
|
640
|
+
return (this.showSaveButton && !this.isBoardLook) || this.showReimportButton || !!this.$slots.actions
|
|
641
|
+
},
|
|
642
|
+
|
|
398
643
|
/** Whether local config differs from prop */
|
|
399
644
|
hasChanges() {
|
|
400
645
|
return JSON.stringify(this.localConfig) !== JSON.stringify(this.configuration)
|
|
@@ -405,11 +650,20 @@ export default {
|
|
|
405
650
|
configuration: {
|
|
406
651
|
handler(newVal) {
|
|
407
652
|
this.localConfig = { ...newVal }
|
|
653
|
+
if (this.registersLoaded) {
|
|
654
|
+
this.mappedRegisterIds().forEach((id) => this.loadSchemasForRegister(id))
|
|
655
|
+
}
|
|
408
656
|
},
|
|
409
657
|
|
|
410
658
|
immediate: true,
|
|
411
659
|
deep: true,
|
|
412
660
|
},
|
|
661
|
+
|
|
662
|
+
groups() {
|
|
663
|
+
if (this.activeGroupIdx >= this.groups.length) {
|
|
664
|
+
this.activeGroupIdx = 0
|
|
665
|
+
}
|
|
666
|
+
},
|
|
413
667
|
},
|
|
414
668
|
|
|
415
669
|
async mounted() {
|
|
@@ -418,6 +672,65 @@ export default {
|
|
|
418
672
|
|
|
419
673
|
methods: {
|
|
420
674
|
t,
|
|
675
|
+
n,
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* The status chip text of a group: every type set up, none, or how many.
|
|
679
|
+
*
|
|
680
|
+
* @param {number} groupIdx Group index
|
|
681
|
+
* @param {boolean} open Whether the group is the open one, which says "All types set up"
|
|
682
|
+
* @return {string}
|
|
683
|
+
* @spec openspec/changes/register-mapping-board-look/specs/register-mapping/spec.md#requirement-the-board-look-draws-one-open-group-and-compact-rows
|
|
684
|
+
*/
|
|
685
|
+
groupStatusLabel(groupIdx, open) {
|
|
686
|
+
const configured = this.configuredCount(groupIdx)
|
|
687
|
+
const total = this.groups[groupIdx].types.length
|
|
688
|
+
if (configured === total) {
|
|
689
|
+
return open ? t('nextcloud-vue', 'All types set up') : t('nextcloud-vue', 'Set up')
|
|
690
|
+
}
|
|
691
|
+
if (configured === 0) {
|
|
692
|
+
return t('nextcloud-vue', 'Not set')
|
|
693
|
+
}
|
|
694
|
+
return t('nextcloud-vue', '{configured} of {total} set up', { configured, total })
|
|
695
|
+
},
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* The status chip variant of a group.
|
|
699
|
+
*
|
|
700
|
+
* @param {number} groupIdx Group index
|
|
701
|
+
* @return {'success'|'warning'|'default'}
|
|
702
|
+
*/
|
|
703
|
+
groupStatusVariant(groupIdx) {
|
|
704
|
+
const configured = this.configuredCount(groupIdx)
|
|
705
|
+
if (configured === this.groups[groupIdx].types.length) {
|
|
706
|
+
return 'success'
|
|
707
|
+
}
|
|
708
|
+
return configured === 0 ? 'default' : 'warning'
|
|
709
|
+
},
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Board look: draw another group open.
|
|
713
|
+
*
|
|
714
|
+
* @param {number} groupIdx Group index
|
|
715
|
+
* @return {void}
|
|
716
|
+
* @spec openspec/changes/register-mapping-board-look/specs/register-mapping/spec.md#requirement-the-board-look-draws-one-open-group-and-compact-rows
|
|
717
|
+
*/
|
|
718
|
+
openBoardGroup(groupIdx) {
|
|
719
|
+
this.activeGroupIdx = groupIdx
|
|
720
|
+
this.showAllTypes = false
|
|
721
|
+
},
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* The register ids the configuration maps, one per distinct value.
|
|
725
|
+
*
|
|
726
|
+
* @return {string[]}
|
|
727
|
+
*/
|
|
728
|
+
mappedRegisterIds() {
|
|
729
|
+
const ids = this.groups
|
|
730
|
+
.map((group, idx) => String(this.localConfig[this.registerConfigKey(idx)] || ''))
|
|
731
|
+
.filter(Boolean)
|
|
732
|
+
return [...new Set(ids)]
|
|
733
|
+
},
|
|
421
734
|
|
|
422
735
|
/**
|
|
423
736
|
* Whether a group's body shows.
|
|
@@ -675,17 +988,24 @@ export default {
|
|
|
675
988
|
},
|
|
676
989
|
|
|
677
990
|
/**
|
|
678
|
-
* Fetch
|
|
991
|
+
* Fetch the registers for the select, without their schemas, and the
|
|
992
|
+
* schemas of the registers the configuration maps. Expanding the schemas
|
|
993
|
+
* of every register costs megabytes on a busy instance.
|
|
994
|
+
*
|
|
995
|
+
* @spec openspec/changes/register-mapping-board-look/specs/register-mapping/spec.md#requirement-only-mapped-registers-have-their-schemas-fetched
|
|
679
996
|
*/
|
|
680
997
|
async loadRegisters() {
|
|
681
998
|
this.registersLoading = true
|
|
682
999
|
this.registersError = null
|
|
683
1000
|
|
|
684
1001
|
try {
|
|
685
|
-
const response = await
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
1002
|
+
const [response] = await Promise.all([
|
|
1003
|
+
fetch(prefixUrl('/apps/openregister/api/registers'), {
|
|
1004
|
+
method: 'GET',
|
|
1005
|
+
headers: buildHeaders(),
|
|
1006
|
+
}),
|
|
1007
|
+
...this.mappedRegisterIds().map((id) => this.loadSchemasForRegister(id)),
|
|
1008
|
+
])
|
|
689
1009
|
|
|
690
1010
|
if (!response.ok) {
|
|
691
1011
|
this.registersError = t('nextcloud-vue', 'The registers could not be loaded: {reason}', { reason: response.statusText || response.status })
|
|
@@ -695,19 +1015,7 @@ export default {
|
|
|
695
1015
|
const data = await response.json()
|
|
696
1016
|
const results = data.results || data
|
|
697
1017
|
this.registers = Array.isArray(results) ? results : []
|
|
698
|
-
|
|
699
|
-
// Cache expanded schemas
|
|
700
|
-
for (const reg of this.registers) {
|
|
701
|
-
if (Array.isArray(reg.schemas) && reg.schemas.length > 0) {
|
|
702
|
-
const schemas = reg.schemas.filter((s) => s && typeof s === 'object' && s.id)
|
|
703
|
-
if (schemas.length > 0) {
|
|
704
|
-
this.schemasByRegister = {
|
|
705
|
-
...this.schemasByRegister,
|
|
706
|
-
[String(reg.id)]: schemas,
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
}
|
|
1018
|
+
this.registersLoaded = true
|
|
711
1019
|
} catch (error) {
|
|
712
1020
|
this.registersError = error.message || t('nextcloud-vue', 'The registers could not be loaded: {reason}', { reason: t('nextcloud-vue', 'no connection') })
|
|
713
1021
|
} finally {
|
|
@@ -716,32 +1024,54 @@ export default {
|
|
|
716
1024
|
},
|
|
717
1025
|
|
|
718
1026
|
/**
|
|
719
|
-
* Fetch schemas
|
|
1027
|
+
* Fetch the schemas of one register, through `registers/{id}/schemas`,
|
|
1028
|
+
* or the register with its schemas expanded where that route is missing.
|
|
720
1029
|
*
|
|
721
1030
|
* @param {string} registerId Register ID
|
|
1031
|
+
* @spec openspec/changes/register-mapping-board-look/specs/register-mapping/spec.md#requirement-only-mapped-registers-have-their-schemas-fetched
|
|
722
1032
|
*/
|
|
723
1033
|
async loadSchemasForRegister(registerId) {
|
|
724
1034
|
const id = String(registerId)
|
|
725
1035
|
|
|
726
|
-
// Return cached
|
|
727
1036
|
if (this.schemasByRegister[id]?.length > 0) {
|
|
728
1037
|
return
|
|
729
1038
|
}
|
|
1039
|
+
if (!this.schemaRequests[id]) {
|
|
1040
|
+
this.schemaRequests[id] = this.fetchSchemas(id).finally(() => {
|
|
1041
|
+
delete this.schemaRequests[id]
|
|
1042
|
+
})
|
|
1043
|
+
}
|
|
1044
|
+
await this.schemaRequests[id]
|
|
1045
|
+
},
|
|
730
1046
|
|
|
1047
|
+
/**
|
|
1048
|
+
* One schema request for a register; fills `schemasByRegister`.
|
|
1049
|
+
*
|
|
1050
|
+
* @param {string} id Register ID
|
|
1051
|
+
* @return {Promise<void>}
|
|
1052
|
+
*/
|
|
1053
|
+
async fetchSchemas(id) {
|
|
731
1054
|
try {
|
|
732
|
-
|
|
733
|
-
prefixUrl(`/apps/openregister/api/registers/${encodeURIComponent(id)}
|
|
1055
|
+
let response = await fetch(
|
|
1056
|
+
prefixUrl(`/apps/openregister/api/registers/${encodeURIComponent(id)}/schemas`),
|
|
734
1057
|
{ method: 'GET', headers: buildHeaders() },
|
|
735
1058
|
)
|
|
1059
|
+
if (response.status === 404) {
|
|
1060
|
+
response = await fetch(
|
|
1061
|
+
prefixUrl(`/apps/openregister/api/registers/${encodeURIComponent(id)}?_extend[]=schemas`),
|
|
1062
|
+
{ method: 'GET', headers: buildHeaders() },
|
|
1063
|
+
)
|
|
1064
|
+
}
|
|
736
1065
|
if (!response.ok) {
|
|
737
1066
|
return
|
|
738
1067
|
}
|
|
739
1068
|
|
|
740
1069
|
const data = await response.json()
|
|
741
|
-
const
|
|
1070
|
+
const list = data.results || data.schemas || []
|
|
1071
|
+
const schemas = (Array.isArray(list) ? list : []).filter((s) => s && typeof s === 'object' && s.id)
|
|
742
1072
|
this.schemasByRegister = { ...this.schemasByRegister, [id]: schemas }
|
|
743
1073
|
} catch {
|
|
744
|
-
//
|
|
1074
|
+
// The register stays selected; its schema select stays empty.
|
|
745
1075
|
}
|
|
746
1076
|
},
|
|
747
1077
|
},
|
|
@@ -924,6 +1254,159 @@ export default {
|
|
|
924
1254
|
margin: 0 0 8px 0;
|
|
925
1255
|
}
|
|
926
1256
|
|
|
1257
|
+
/* Board look (PqBeheer): one open group, compact rows for the others. */
|
|
1258
|
+
.cn-register-mapping--board {
|
|
1259
|
+
display: flex;
|
|
1260
|
+
flex-direction: column;
|
|
1261
|
+
gap: 14px;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
.cn-register-mapping__board-group {
|
|
1265
|
+
display: flex;
|
|
1266
|
+
flex-direction: column;
|
|
1267
|
+
gap: 10px;
|
|
1268
|
+
padding: 14px;
|
|
1269
|
+
border: 1px solid var(--color-border);
|
|
1270
|
+
border-radius: var(--border-radius-large);
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
.cn-register-mapping__board-head {
|
|
1274
|
+
display: flex;
|
|
1275
|
+
flex-wrap: wrap;
|
|
1276
|
+
align-items: flex-start;
|
|
1277
|
+
justify-content: space-between;
|
|
1278
|
+
gap: 10px 16px;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
.cn-register-mapping__board-title {
|
|
1282
|
+
margin: 0;
|
|
1283
|
+
font-size: 15px;
|
|
1284
|
+
font-weight: 700;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
.cn-register-mapping__board-select {
|
|
1288
|
+
max-width: 400px;
|
|
1289
|
+
margin-bottom: 0;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
.cn-register-mapping__board-table {
|
|
1293
|
+
width: 100%;
|
|
1294
|
+
border-collapse: collapse;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
.cn-register-mapping__board-table th {
|
|
1298
|
+
padding: 8px 12px 8px 0;
|
|
1299
|
+
border-bottom: 1px solid var(--color-border);
|
|
1300
|
+
font-size: 13px;
|
|
1301
|
+
font-weight: 600;
|
|
1302
|
+
color: var(--color-text-maxcontrast);
|
|
1303
|
+
text-align: start;
|
|
1304
|
+
white-space: nowrap;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
.cn-register-mapping__board-table td {
|
|
1308
|
+
padding: 10px 12px 10px 0;
|
|
1309
|
+
border-bottom: 1px solid var(--color-border);
|
|
1310
|
+
font-size: 14px;
|
|
1311
|
+
vertical-align: middle;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
.cn-register-mapping__board-schema {
|
|
1315
|
+
display: inline-flex;
|
|
1316
|
+
align-items: center;
|
|
1317
|
+
gap: 6px;
|
|
1318
|
+
min-height: 32px;
|
|
1319
|
+
margin: 0;
|
|
1320
|
+
padding: 0;
|
|
1321
|
+
border: 0;
|
|
1322
|
+
background: transparent;
|
|
1323
|
+
color: inherit;
|
|
1324
|
+
font: inherit;
|
|
1325
|
+
text-align: start;
|
|
1326
|
+
cursor: pointer;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
.cn-register-mapping__board-schema:hover {
|
|
1330
|
+
text-decoration: underline;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
.cn-register-mapping__board-schema:focus-visible,
|
|
1334
|
+
.cn-register-mapping__board-link:focus-visible,
|
|
1335
|
+
.cn-register-mapping__board-group-row:focus-visible {
|
|
1336
|
+
outline: 2px solid var(--color-primary-element);
|
|
1337
|
+
outline-offset: 2px;
|
|
1338
|
+
border-radius: var(--border-radius);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
.cn-register-mapping__board-check {
|
|
1342
|
+
color: var(--color-success-text, var(--color-success));
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
.cn-register-mapping__board-more {
|
|
1346
|
+
margin: 0;
|
|
1347
|
+
font-size: 13px;
|
|
1348
|
+
line-height: 1.4;
|
|
1349
|
+
color: var(--color-text-maxcontrast);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
.cn-register-mapping__board-link {
|
|
1353
|
+
margin: 0;
|
|
1354
|
+
padding: 0;
|
|
1355
|
+
border: 0;
|
|
1356
|
+
background: transparent;
|
|
1357
|
+
color: var(--color-primary-element);
|
|
1358
|
+
font: inherit;
|
|
1359
|
+
text-decoration: underline;
|
|
1360
|
+
cursor: pointer;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
.cn-register-mapping__board-groups {
|
|
1364
|
+
display: flex;
|
|
1365
|
+
flex-direction: column;
|
|
1366
|
+
margin: 0;
|
|
1367
|
+
padding: 0;
|
|
1368
|
+
list-style: none;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
.cn-register-mapping__board-group-row {
|
|
1372
|
+
display: flex;
|
|
1373
|
+
align-items: center;
|
|
1374
|
+
gap: 10px;
|
|
1375
|
+
width: 100%;
|
|
1376
|
+
min-height: 44px;
|
|
1377
|
+
margin: 0;
|
|
1378
|
+
padding: 10px 2px;
|
|
1379
|
+
border: 0;
|
|
1380
|
+
border-bottom: 1px solid var(--color-border);
|
|
1381
|
+
background: transparent;
|
|
1382
|
+
color: inherit;
|
|
1383
|
+
font: inherit;
|
|
1384
|
+
font-size: 14px;
|
|
1385
|
+
text-align: start;
|
|
1386
|
+
cursor: pointer;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
.cn-register-mapping__board-group-row:hover {
|
|
1390
|
+
background: var(--color-background-hover);
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
.cn-register-mapping__board-chevron,
|
|
1394
|
+
.cn-register-mapping__board-group-count {
|
|
1395
|
+
color: var(--color-text-maxcontrast);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
.cn-register-mapping__board-group-name {
|
|
1399
|
+
flex: 1;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
.cn-register-mapping__board-save {
|
|
1403
|
+
display: flex;
|
|
1404
|
+
flex-wrap: wrap;
|
|
1405
|
+
align-items: center;
|
|
1406
|
+
justify-content: flex-end;
|
|
1407
|
+
gap: 10px;
|
|
1408
|
+
}
|
|
1409
|
+
|
|
927
1410
|
/* Slide transition */
|
|
928
1411
|
.slide-enter-active,
|
|
929
1412
|
.slide-leave-active {
|
|
@@ -74,6 +74,7 @@ import '../CnFlowRunsWidget/index.js'
|
|
|
74
74
|
import '../CnTasksWidget/index.js'
|
|
75
75
|
import '../CnSectionsWidget/index.js'
|
|
76
76
|
import '../CnFactsWidget/index.js'
|
|
77
|
+
import '../CnDeviceFramesWidget/index.js'
|
|
77
78
|
import '../CnFieldListWidget/index.js'
|
|
78
79
|
import '../CnSettingsFormWidget/index.js'
|
|
79
80
|
import '../CnSwitchRowsWidget/index.js'
|
package/src/components/index.js
CHANGED
|
@@ -220,6 +220,7 @@ export { CnTasksWidget } from './CnTasksWidget/index.js'
|
|
|
220
220
|
export { CnTasksWidgetForm } from './CnTasksWidgetForm/index.js'
|
|
221
221
|
export { CnSectionsWidget } from './CnSectionsWidget/index.js'
|
|
222
222
|
export { CnFactsWidget } from './CnFactsWidget/index.js'
|
|
223
|
+
export { CnDeviceFramesWidget } from './CnDeviceFramesWidget/index.js'
|
|
223
224
|
export { CnFieldListWidget } from './CnFieldListWidget/index.js'
|
|
224
225
|
export { CnSettingsFormWidget } from './CnSettingsFormWidget/index.js'
|
|
225
226
|
export { CnSwitchRowsWidget } from './CnSwitchRowsWidget/index.js'
|