@conduction/nextcloud-vue 2.108.0 → 2.109.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.
- package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue.js +10 -0
- package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue.js.map +1 -0
- package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue2.js +305 -0
- package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue2.js.map +1 -0
- package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue3.js +38 -0
- package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue3.js.map +1 -0
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js +10 -0
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js.map +1 -0
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +398 -0
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -0
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js +155 -0
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -0
- package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue.js +9 -0
- package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue.js.map +1 -0
- package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue2.js +281 -0
- package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue3.js +137 -0
- package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue.js +9 -0
- package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue.js.map +1 -0
- package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue2.js +225 -0
- package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue2.js.map +1 -0
- package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue3.js +110 -0
- package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue3.js.map +1 -0
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +19 -3
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +30 -0
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +513 -481
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js +9 -0
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js.map +1 -0
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js +181 -0
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js.map +1 -0
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js +50 -0
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js.map +1 -0
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +102 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -6
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue.js +9 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue.js.map +1 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue2.js +190 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue3.js +91 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js +9 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js.map +1 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js +64 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js.map +1 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js +49 -0
- package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js.map +1 -0
- package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue.js +9 -0
- package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue.js.map +1 -0
- package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue2.js +382 -0
- package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue2.js.map +1 -0
- package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue3.js +285 -0
- package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue3.js.map +1 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +9 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js.map +1 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js +277 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +183 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +16 -14
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +53 -22
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue.js +9 -0
- package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue.js.map +1 -0
- package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue2.js +246 -0
- package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue2.js.map +1 -0
- package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue3.js +197 -0
- package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue3.js.map +1 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +106 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +105 -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 +495 -61
- package/dist/esm/utils/actionsDispatcher.js +6 -1
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/builtInModals.js +3 -1
- package/dist/esm/utils/builtInModals.js.map +1 -1
- package/dist/esm/utils/duplicateWarning.js +62 -0
- package/dist/esm/utils/duplicateWarning.js.map +1 -0
- package/dist/esm/utils/errors.js +4 -0
- package/dist/esm/utils/errors.js.map +1 -1
- package/dist/esm/utils/libraryWidgetKeys.js +4 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/openDuplicateWarning.js +39 -0
- package/dist/esm/utils/openDuplicateWarning.js.map +1 -0
- package/dist/esm/utils/optionsFrom.js +69 -2
- package/dist/esm/utils/optionsFrom.js.map +1 -1
- package/dist/esm/utils/planTree.js +57 -0
- package/dist/esm/utils/planTree.js.map +1 -0
- package/dist/esm/utils/redactionSegments.js +53 -0
- package/dist/esm/utils/redactionSegments.js.map +1 -0
- package/dist/esm/utils/registerNcDashboardWidgets.js +61 -0
- package/dist/esm/utils/registerNcDashboardWidgets.js.map +1 -0
- package/dist/esm/utils/searchRefusal.js +62 -0
- package/dist/esm/utils/searchRefusal.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +17409 -12832
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +495 -61
- package/l10n/en.json +106 -1
- package/l10n/nl.json +105 -1
- package/package.json +1 -1
- package/src/components/CnAddressSearch/CnAddressSearch.vue +343 -0
- package/src/components/CnAddressSearch/index.js +12 -0
- package/src/components/CnAssignDialog/CnAssignDialog.vue +525 -0
- package/src/components/CnAssignDialog/index.js +12 -0
- package/src/components/CnDecisionListWidget/CnDecisionListWidget.vue +388 -0
- package/src/components/CnDecisionListWidget/index.js +12 -0
- package/src/components/CnDuplicateWarning/CnDuplicateWarning.vue +318 -0
- package/src/components/CnDuplicateWarning/index.js +12 -0
- package/src/components/CnFormDialog/CnFormDialog.vue +19 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +52 -0
- package/src/components/CnNcDashboardList/CnNcDashboardList.vue +218 -0
- package/src/components/CnNcDashboardList/index.js +12 -0
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +107 -6
- package/src/components/CnPlanWidget/CnPlanWidget.vue +246 -0
- package/src/components/CnPlanWidget/CnPlanWidgetRow.vue +117 -0
- package/src/components/CnPlanWidget/index.js +12 -0
- package/src/components/CnRedactionEditor/CnRedactionEditor.vue +655 -0
- package/src/components/CnRedactionEditor/index.js +13 -0
- package/src/components/CnRetentionWidget/CnRetentionWidget.vue +413 -0
- package/src/components/CnRetentionWidget/index.js +12 -0
- package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +31 -0
- package/src/components/CnWorkQueue/CnWorkQueue.vue +456 -0
- package/src/components/CnWorkQueue/index.js +12 -0
- package/src/components/index.js +9 -0
- package/src/css/index-page.css +24 -0
- package/src/index.js +12 -0
- package/src/schemas/app-manifest-v2.schema.json +119 -3
- package/src/utils/actionsDispatcher.js +6 -1
- package/src/utils/builtInModals.js +3 -1
- package/src/utils/duplicateWarning.js +59 -0
- package/src/utils/errors.js +4 -0
- package/src/utils/libraryWidgetKeys.js +4 -0
- package/src/utils/openDuplicateWarning.js +35 -0
- package/src/utils/optionsFrom.js +68 -1
- package/src/utils/planTree.js +54 -0
- package/src/utils/redactionSegments.js +50 -0
- package/src/utils/registerNcDashboardWidgets.js +57 -0
- package/src/utils/searchRefusal.js +59 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
- SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
- SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
-->
|
|
5
|
+
<template>
|
|
6
|
+
<div class="cn-work-queue" data-testid="cn-work-queue">
|
|
7
|
+
<div v-if="title || $slots.actions" class="cn-work-queue__head">
|
|
8
|
+
<h2 v-if="title" class="cn-work-queue__title">
|
|
9
|
+
{{ title }}
|
|
10
|
+
</h2>
|
|
11
|
+
<div class="cn-work-queue__actions">
|
|
12
|
+
<!-- @slot Buttons beside the title ("Plan an item"). -->
|
|
13
|
+
<slot name="actions" />
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<NcNoteCard v-for="source in unavailable"
|
|
18
|
+
:key="source.source"
|
|
19
|
+
type="warning"
|
|
20
|
+
:data-testid="`cn-work-queue-unavailable-${source.source}`">
|
|
21
|
+
{{ t('nextcloud-vue', '{source} could not be read, so this list is incomplete.', { source: source.label || source.source }) }}
|
|
22
|
+
</NcNoteCard>
|
|
23
|
+
|
|
24
|
+
<NcLoadingIcon v-if="loading" :size="32" />
|
|
25
|
+
<NcEmptyContent v-else-if="error"
|
|
26
|
+
:name="t('nextcloud-vue', 'This queue could not be read')"
|
|
27
|
+
:description="error"
|
|
28
|
+
data-testid="cn-work-queue-error">
|
|
29
|
+
<template #action>
|
|
30
|
+
<NcButton @click="load">
|
|
31
|
+
{{ t('nextcloud-vue', 'Try again') }}
|
|
32
|
+
</NcButton>
|
|
33
|
+
</template>
|
|
34
|
+
</NcEmptyContent>
|
|
35
|
+
<template v-else>
|
|
36
|
+
<p v-if="shownGroups.length === 0" class="cn-work-queue__muted" data-testid="cn-work-queue-empty">
|
|
37
|
+
{{ text(content.emptyText) || t('nextcloud-vue', 'Nothing is waiting on you.') }}
|
|
38
|
+
</p>
|
|
39
|
+
<section v-for="group in shownGroups"
|
|
40
|
+
:key="group.key"
|
|
41
|
+
class="cn-work-queue__group"
|
|
42
|
+
:data-testid="`cn-work-queue-group-${group.key}`">
|
|
43
|
+
<div class="cn-work-queue__group-head">
|
|
44
|
+
<h3 class="cn-work-queue__group-title">
|
|
45
|
+
{{ group.label }}
|
|
46
|
+
<span class="cn-work-queue__count">{{ group.items.length }}</span>
|
|
47
|
+
</h3>
|
|
48
|
+
<NcButton v-if="content.hide"
|
|
49
|
+
variant="secondary"
|
|
50
|
+
:aria-label="t('nextcloud-vue', 'Hide this group until tomorrow')"
|
|
51
|
+
:data-testid="`cn-work-queue-hide-${group.key}`"
|
|
52
|
+
@click="setHidden(group.key, true)">
|
|
53
|
+
{{ t('nextcloud-vue', 'Hide until tomorrow') }}
|
|
54
|
+
</NcButton>
|
|
55
|
+
</div>
|
|
56
|
+
<p v-if="group.closesWhen" class="cn-work-queue__muted">
|
|
57
|
+
{{ group.closesWhen }}
|
|
58
|
+
</p>
|
|
59
|
+
<ul class="cn-work-queue__items">
|
|
60
|
+
<li v-for="item in group.items"
|
|
61
|
+
:key="item.id"
|
|
62
|
+
class="cn-work-queue__item"
|
|
63
|
+
:class="{ 'cn-work-queue__item--late': item.deadlineTier === 'overdue' }"
|
|
64
|
+
:data-testid="`cn-work-queue-item-${item.id}`">
|
|
65
|
+
<span class="cn-work-queue__avatar" aria-hidden="true">{{ initials(item.title) }}</span>
|
|
66
|
+
<span class="cn-work-queue__body">
|
|
67
|
+
<a :href="hrefOf(item) || '#'" class="cn-work-queue__link" @click="onOpen(item, $event)">{{ item.title }}</a>
|
|
68
|
+
<span v-if="item.covered || waitingText(item)" class="cn-work-queue__sub">
|
|
69
|
+
<span v-if="item.covered">{{ t('nextcloud-vue', 'Covering for {person}', { person: String(item.coveredFor || '') }) }}</span>
|
|
70
|
+
<span v-if="waitingText(item)" :data-testid="`cn-work-queue-waiting-${item.id}`">{{ waitingText(item) }}</span>
|
|
71
|
+
</span>
|
|
72
|
+
</span>
|
|
73
|
+
<span v-if="item.dueAt" class="cn-work-queue__muted">{{ item.dueAt }}</span>
|
|
74
|
+
</li>
|
|
75
|
+
</ul>
|
|
76
|
+
</section>
|
|
77
|
+
</template>
|
|
78
|
+
|
|
79
|
+
<section v-if="content.hide && hidden.length" class="cn-work-queue__group" data-testid="cn-work-queue-hidden">
|
|
80
|
+
<h3 class="cn-work-queue__group-title">
|
|
81
|
+
{{ t('nextcloud-vue', 'Hidden until tomorrow') }}
|
|
82
|
+
</h3>
|
|
83
|
+
<NcButton v-for="key in hidden"
|
|
84
|
+
:key="key"
|
|
85
|
+
variant="tertiary"
|
|
86
|
+
:data-testid="`cn-work-queue-show-${key}`"
|
|
87
|
+
@click="setHidden(key, false)">
|
|
88
|
+
{{ t('nextcloud-vue', 'Show {group} again', { group: labelOf(key) }) }}
|
|
89
|
+
</NcButton>
|
|
90
|
+
</section>
|
|
91
|
+
</div>
|
|
92
|
+
</template>
|
|
93
|
+
|
|
94
|
+
<script>
|
|
95
|
+
import axios from '@nextcloud/axios'
|
|
96
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
97
|
+
import { generateUrl } from '@nextcloud/router'
|
|
98
|
+
import { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'
|
|
99
|
+
import { fetchEndpointSource } from '../../composables/useEndpointSource.js'
|
|
100
|
+
import { fillTemplate } from '../../utils/fillTemplate.js'
|
|
101
|
+
import { openRowTarget } from '../../utils/linkNavigation.js'
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* CnWorkQueue — one list of everything waiting on the reader: tasks,
|
|
105
|
+
* consultations, mentions, calendar items, work they cover for a colleague,
|
|
106
|
+
* from as many sources as the server declares. The component names no
|
|
107
|
+
* source: the server answers the groups (`key`, `label`, `closesWhen`,
|
|
108
|
+
* `items`), so a new kind of work needs no change here. A source that could
|
|
109
|
+
* not be read is named at the top rather than folded into "nothing is
|
|
110
|
+
* waiting". A group can be hidden until tomorrow, through the server, so the
|
|
111
|
+
* choice follows the reader to another device. dossiq's personal queue is
|
|
112
|
+
* the first; pipelinq's queue has the same shape.
|
|
113
|
+
*
|
|
114
|
+
* `content`:
|
|
115
|
+
* - `source`: an endpointSource answering `{ groups, hiddenGroups?, unavailable? }`.
|
|
116
|
+
* An item is `{ id, title, dueAt?, deadlineTier?, covered?, coveredFor?,
|
|
117
|
+
* waiting?: { on, days, chases }, route?, href? }`.
|
|
118
|
+
* - `hide`, `show`: url templates with `{group}` that hide a group until
|
|
119
|
+
* tomorrow and show it again, each answering `{ hiddenGroups }`; without
|
|
120
|
+
* `hide` no group can be hidden.
|
|
121
|
+
* - `itemHref`: a template filled from an item, when items carry no `route`/`href`.
|
|
122
|
+
* - `waitingParties`: words for `waiting.on` (`{ applicant: "the applicant" }`).
|
|
123
|
+
* - `emptyText`: through the host translate.
|
|
124
|
+
*/
|
|
125
|
+
export default {
|
|
126
|
+
name: 'CnWorkQueue',
|
|
127
|
+
|
|
128
|
+
components: { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard },
|
|
129
|
+
|
|
130
|
+
inject: {
|
|
131
|
+
/** The host app's translate (CnAppRoot `cnTranslate`). */
|
|
132
|
+
cnTranslate: { default: () => (key) => key },
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
props: {
|
|
136
|
+
/**
|
|
137
|
+
* The queue's configuration (see the component docs).
|
|
138
|
+
*
|
|
139
|
+
* @type {object}
|
|
140
|
+
*/
|
|
141
|
+
content: {
|
|
142
|
+
type: Object,
|
|
143
|
+
default: () => ({}),
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
/** The heading above the queue; none when empty. */
|
|
147
|
+
title: {
|
|
148
|
+
type: String,
|
|
149
|
+
default: '',
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
emits: ['open'],
|
|
154
|
+
|
|
155
|
+
data() {
|
|
156
|
+
return {
|
|
157
|
+
loading: true,
|
|
158
|
+
error: '',
|
|
159
|
+
groups: [],
|
|
160
|
+
hidden: [],
|
|
161
|
+
unavailable: [],
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
computed: {
|
|
166
|
+
/**
|
|
167
|
+
* The groups not hidden today.
|
|
168
|
+
*
|
|
169
|
+
* @spec openspec/changes/work-queue/specs/work-queue/spec.md#requirement-one-queue-over-every-source
|
|
170
|
+
* @return {Array<object>}
|
|
171
|
+
*/
|
|
172
|
+
shownGroups() {
|
|
173
|
+
const hidden = new Set(this.hidden.map(String))
|
|
174
|
+
return this.groups.filter((group) => !hidden.has(String(group.key)))
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
mounted() {
|
|
179
|
+
this.load()
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
methods: {
|
|
183
|
+
t,
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* A config text through the host translate.
|
|
187
|
+
*
|
|
188
|
+
* @param {unknown} value The text.
|
|
189
|
+
* @return {string}
|
|
190
|
+
*/
|
|
191
|
+
text(value) {
|
|
192
|
+
return typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Read the queue; a failed read is an error with a retry.
|
|
197
|
+
*
|
|
198
|
+
* @spec openspec/changes/work-queue/specs/work-queue/spec.md#requirement-one-queue-over-every-source
|
|
199
|
+
* @return {Promise<void>}
|
|
200
|
+
*/
|
|
201
|
+
async load() {
|
|
202
|
+
this.loading = true
|
|
203
|
+
this.error = ''
|
|
204
|
+
try {
|
|
205
|
+
const data = (await fetchEndpointSource(this.content.source, {}, { fresh: true })) || {}
|
|
206
|
+
this.groups = (Array.isArray(data.groups) ? data.groups : [])
|
|
207
|
+
.filter((g) => g && g.key !== undefined)
|
|
208
|
+
.map((g) => ({ ...g, items: Array.isArray(g.items) ? g.items : [] }))
|
|
209
|
+
this.hidden = Array.isArray(data.hiddenGroups) ? data.hiddenGroups.map(String) : []
|
|
210
|
+
this.unavailable = Array.isArray(data.unavailable) ? data.unavailable.filter((s) => s && s.source) : []
|
|
211
|
+
} catch (failure) {
|
|
212
|
+
const body = failure && failure.response && failure.response.data
|
|
213
|
+
this.error = String((body && (body.message || body.error)) || (failure && failure.message) || failure)
|
|
214
|
+
} finally {
|
|
215
|
+
this.loading = false
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Hide a group until tomorrow, or show it again, through the server.
|
|
221
|
+
*
|
|
222
|
+
* @spec openspec/changes/work-queue/specs/work-queue/spec.md#requirement-one-queue-over-every-source
|
|
223
|
+
* @param {string} key The group key.
|
|
224
|
+
* @param {boolean} hide Hide (true) or show (false).
|
|
225
|
+
* @return {Promise<void>}
|
|
226
|
+
*/
|
|
227
|
+
async setHidden(key, hide) {
|
|
228
|
+
const template = hide ? this.content.hide : this.content.show
|
|
229
|
+
if (!template) {
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const { data } = await axios.post(generateUrl(fillTemplate(template, { group: encodeURIComponent(key) })), {})
|
|
234
|
+
this.hidden = Array.isArray(data && data.hiddenGroups) ? data.hiddenGroups.map(String) : this.hidden
|
|
235
|
+
} catch {
|
|
236
|
+
// The group stays as it was; the server decides.
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A group's label by key, for a hidden group.
|
|
242
|
+
*
|
|
243
|
+
* @param {string} key The key.
|
|
244
|
+
* @return {string}
|
|
245
|
+
*/
|
|
246
|
+
labelOf(key) {
|
|
247
|
+
const group = this.groups.find((g) => String(g.key) === String(key))
|
|
248
|
+
return group && group.label ? group.label : String(key)
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* An item's page as an href.
|
|
253
|
+
*
|
|
254
|
+
* @param {object} item The item.
|
|
255
|
+
* @return {string}
|
|
256
|
+
*/
|
|
257
|
+
hrefOf(item) {
|
|
258
|
+
if (item.route && this.$router) {
|
|
259
|
+
try {
|
|
260
|
+
return this.$router.resolve(item.route).href
|
|
261
|
+
} catch {
|
|
262
|
+
return ''
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (item.href) {
|
|
266
|
+
return String(item.href)
|
|
267
|
+
}
|
|
268
|
+
return this.content.itemHref ? generateUrl(fillTemplate(this.content.itemHref, item)) : ''
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Open an item: its route through the router, else its href like a link, else tell the host.
|
|
273
|
+
*
|
|
274
|
+
* @param {object} item The item.
|
|
275
|
+
* @param {MouseEvent} event The click.
|
|
276
|
+
*/
|
|
277
|
+
onOpen(item, event) {
|
|
278
|
+
if (item.route && this.$router && event && !(event.ctrlKey || event.metaKey || event.shiftKey || event.button === 1)) {
|
|
279
|
+
event.preventDefault()
|
|
280
|
+
this.$router.push(item.route).catch(() => {})
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
const href = this.hrefOf(item)
|
|
284
|
+
if (href) {
|
|
285
|
+
if (event) {
|
|
286
|
+
event.preventDefault()
|
|
287
|
+
}
|
|
288
|
+
openRowTarget(event, href)
|
|
289
|
+
return
|
|
290
|
+
}
|
|
291
|
+
if (event) {
|
|
292
|
+
event.preventDefault()
|
|
293
|
+
}
|
|
294
|
+
/** @event open An item without a route or href was opened. Payload: the item. */
|
|
295
|
+
this.$emit('open', item)
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Who the item waits on, for how long and how often chased.
|
|
300
|
+
*
|
|
301
|
+
* @param {object} item The item.
|
|
302
|
+
* @return {string}
|
|
303
|
+
*/
|
|
304
|
+
waitingText(item) {
|
|
305
|
+
const waiting = item && item.waiting
|
|
306
|
+
if (!waiting || typeof waiting !== 'object' || !waiting.on) {
|
|
307
|
+
return ''
|
|
308
|
+
}
|
|
309
|
+
const parties = {
|
|
310
|
+
applicant: t('nextcloud-vue', 'the applicant'),
|
|
311
|
+
thirdParty: t('nextcloud-vue', 'a third party'),
|
|
312
|
+
us: t('nextcloud-vue', 'us'),
|
|
313
|
+
}
|
|
314
|
+
const own = this.content.waitingParties && this.content.waitingParties[waiting.on]
|
|
315
|
+
const party = own ? this.cnTranslate(own) : (parties[waiting.on] || String(waiting.on))
|
|
316
|
+
const days = Number.isFinite(waiting.days) ? waiting.days : 0
|
|
317
|
+
const chases = Number.isFinite(waiting.chases) ? waiting.chases : 0
|
|
318
|
+
if (chases === 0) {
|
|
319
|
+
return t('nextcloud-vue', 'Waiting on {party} for {days} days', { party, days })
|
|
320
|
+
}
|
|
321
|
+
if (chases === 1) {
|
|
322
|
+
return t('nextcloud-vue', 'Waiting on {party} for {days} days, chased once', { party, days })
|
|
323
|
+
}
|
|
324
|
+
return t('nextcloud-vue', 'Waiting on {party} for {days} days, chased {chases} times', { party, days, chases })
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Up to two initials of a title.
|
|
329
|
+
*
|
|
330
|
+
* @param {string} title The title.
|
|
331
|
+
* @return {string}
|
|
332
|
+
*/
|
|
333
|
+
initials(title) {
|
|
334
|
+
return String(title ?? '').trim().split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0]).join('').toUpperCase()
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
}
|
|
338
|
+
</script>
|
|
339
|
+
|
|
340
|
+
<style scoped>
|
|
341
|
+
.cn-work-queue {
|
|
342
|
+
display: flex;
|
|
343
|
+
flex-direction: column;
|
|
344
|
+
gap: 24px;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.cn-work-queue__head,
|
|
348
|
+
.cn-work-queue__group-head {
|
|
349
|
+
display: flex;
|
|
350
|
+
flex-wrap: wrap;
|
|
351
|
+
align-items: center;
|
|
352
|
+
justify-content: space-between;
|
|
353
|
+
gap: 12px;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.cn-work-queue__title {
|
|
357
|
+
margin: 0;
|
|
358
|
+
font-size: 28px;
|
|
359
|
+
font-weight: 700;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.cn-work-queue__actions {
|
|
363
|
+
display: flex;
|
|
364
|
+
gap: 8px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.cn-work-queue__group {
|
|
368
|
+
padding: 20px 24px;
|
|
369
|
+
border: 1px solid var(--color-border);
|
|
370
|
+
border-radius: var(--border-radius-large);
|
|
371
|
+
background: var(--color-main-background);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.cn-work-queue__group-title {
|
|
375
|
+
display: flex;
|
|
376
|
+
align-items: center;
|
|
377
|
+
gap: 8px;
|
|
378
|
+
margin: 0;
|
|
379
|
+
font-size: 18px;
|
|
380
|
+
font-weight: 700;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.cn-work-queue__count {
|
|
384
|
+
padding: 0 8px;
|
|
385
|
+
border-radius: 10px;
|
|
386
|
+
background: var(--color-background-dark);
|
|
387
|
+
font-size: 12px;
|
|
388
|
+
font-weight: 600;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.cn-work-queue__muted {
|
|
392
|
+
margin: 4px 0 8px;
|
|
393
|
+
color: var(--color-text-maxcontrast);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.cn-work-queue__items {
|
|
397
|
+
margin: 0;
|
|
398
|
+
padding: 0;
|
|
399
|
+
list-style: none;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.cn-work-queue__item {
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: center;
|
|
405
|
+
gap: 16px;
|
|
406
|
+
padding: 12px 0;
|
|
407
|
+
border-block-end: 1px solid var(--color-border);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.cn-work-queue__item:last-child {
|
|
411
|
+
border-block-end: none;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.cn-work-queue__avatar {
|
|
415
|
+
display: inline-flex;
|
|
416
|
+
flex: none;
|
|
417
|
+
align-items: center;
|
|
418
|
+
justify-content: center;
|
|
419
|
+
width: 32px;
|
|
420
|
+
height: 32px;
|
|
421
|
+
border-radius: 50%;
|
|
422
|
+
background: var(--color-background-dark);
|
|
423
|
+
color: var(--color-text-maxcontrast);
|
|
424
|
+
font-size: 12px;
|
|
425
|
+
font-weight: 700;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.cn-work-queue__body {
|
|
429
|
+
display: flex;
|
|
430
|
+
flex: 1 1 auto;
|
|
431
|
+
flex-direction: column;
|
|
432
|
+
min-width: 0;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.cn-work-queue__link {
|
|
436
|
+
color: var(--color-main-text);
|
|
437
|
+
font-weight: 700;
|
|
438
|
+
text-decoration: none;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.cn-work-queue__link:hover {
|
|
442
|
+
text-decoration: underline;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.cn-work-queue__item--late .cn-work-queue__link {
|
|
446
|
+
color: var(--color-error);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.cn-work-queue__sub {
|
|
450
|
+
display: flex;
|
|
451
|
+
flex-wrap: wrap;
|
|
452
|
+
gap: 12px;
|
|
453
|
+
color: var(--color-text-maxcontrast);
|
|
454
|
+
font-size: 0.9em;
|
|
455
|
+
}
|
|
456
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*
|
|
5
|
+
* CnWorkQueue — one list of everything waiting on the reader, in the groups
|
|
6
|
+
* the server declares.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import CnWorkQueue from './CnWorkQueue.vue'
|
|
10
|
+
|
|
11
|
+
export { CnWorkQueue }
|
|
12
|
+
export default CnWorkQueue
|
package/src/components/index.js
CHANGED
|
@@ -59,6 +59,7 @@ export { CnMassExportDialog } from './CnMassExportDialog/index.js'
|
|
|
59
59
|
export { CnMassImportDialog } from './CnMassImportDialog/index.js'
|
|
60
60
|
export { CnExportWizard } from './CnExportWizard/index.js'
|
|
61
61
|
export { CnWizardDialog } from './CnWizardDialog/index.js'
|
|
62
|
+
export { CnWorkQueue } from './CnWorkQueue/index.js'
|
|
62
63
|
export { CnSetupWizard } from './CnSetupWizard/index.js'
|
|
63
64
|
export { CnWalkthrough } from './CnWalkthrough/index.js'
|
|
64
65
|
export { CnRichSubmitDialog } from './CnRichSubmitDialog/index.js'
|
|
@@ -84,6 +85,7 @@ export {
|
|
|
84
85
|
export { CnPageHeader } from './CnPageHeader/index.js'
|
|
85
86
|
export { CnBreadcrumbs } from './CnBreadcrumbs/index.js'
|
|
86
87
|
export { CnActionDialog } from './CnActionDialog/index.js'
|
|
88
|
+
export { CnAssignDialog } from './CnAssignDialog/index.js'
|
|
87
89
|
export { CnActionsBar } from './CnActionsBar/index.js'
|
|
88
90
|
export { CnActionsMenu } from './CnActionsMenu/index.js'
|
|
89
91
|
export { CnActionButtons } from './CnActionButtons/index.js'
|
|
@@ -131,9 +133,12 @@ export { default as CnContactsTab } from '../integrations/builtin/contacts/CnCon
|
|
|
131
133
|
export { CnContactPicker } from './CnContactPicker/index.js'
|
|
132
134
|
export { CnContactCreate } from './CnContactCreate/index.js'
|
|
133
135
|
export { CnResourceSelect } from './CnResourceSelect/index.js'
|
|
136
|
+
export { CnRetentionWidget } from './CnRetentionWidget/index.js'
|
|
134
137
|
export { CnSearchSelect } from './CnSearchSelect/index.js'
|
|
135
138
|
export { CnPartyPicker } from './CnPartyPicker/index.js'
|
|
139
|
+
export { CnAddressSearch } from './CnAddressSearch/index.js'
|
|
136
140
|
export { CnNoticeWidget } from './CnNoticeWidget/index.js'
|
|
141
|
+
export { CnDecisionListWidget } from './CnDecisionListWidget/index.js'
|
|
137
142
|
export { CnPropertySourceField } from './CnPropertySourceField/index.js'
|
|
138
143
|
export { CnDurationField } from './CnDurationField/index.js'
|
|
139
144
|
export { CnFavouriteToggle } from './CnFavouriteToggle/index.js'
|
|
@@ -193,6 +198,7 @@ export { CnDividerWidgetForm } from './CnDividerWidgetForm/index.js'
|
|
|
193
198
|
export { CnFilesWidget } from './CnFilesWidget/index.js'
|
|
194
199
|
export { CnFilesWidgetForm } from './CnFilesWidgetForm/index.js'
|
|
195
200
|
export { CnPeopleWidget } from './CnPeopleWidget/index.js'
|
|
201
|
+
export { CnPlanWidget } from './CnPlanWidget/index.js'
|
|
196
202
|
export { CnPeopleWidgetForm } from './CnPeopleWidgetForm/index.js'
|
|
197
203
|
export { CnQuicklinksWidget } from './CnQuicklinksWidget/index.js'
|
|
198
204
|
export { CnQuicklinksWidgetForm } from './CnQuicklinksWidgetForm/index.js'
|
|
@@ -209,6 +215,7 @@ export { CnCalendarWidgetForm } from './CnCalendarWidgetForm/index.js'
|
|
|
209
215
|
export { CnSpendAnalyticsWidget } from './CnSpendAnalyticsWidget/index.js'
|
|
210
216
|
export { CnSpendAnalyticsWidgetForm } from './CnSpendAnalyticsWidgetForm/index.js'
|
|
211
217
|
export { CnNcWidgetWidget } from './CnNcWidgetWidget/index.js'
|
|
218
|
+
export { CnNcDashboardList } from './CnNcDashboardList/index.js'
|
|
212
219
|
export { CnNcDashboardWidgetForm } from './CnNcDashboardWidgetForm/index.js'
|
|
213
220
|
export { CnDashTileWidget } from './CnDashTileWidget/index.js'
|
|
214
221
|
export { CnDashTileWidgetForm } from './CnDashTileWidgetForm/index.js'
|
|
@@ -278,6 +285,7 @@ export { CnObjectDataWidget } from './CnObjectDataWidget/index.js'
|
|
|
278
285
|
export { CnObjectMetadataWidget } from './CnObjectMetadataWidget/index.js'
|
|
279
286
|
export { CnObjectMetadataModal } from './CnObjectMetadataModal/index.js'
|
|
280
287
|
export { CnRelatedObjectsWidget } from './CnRelatedObjectsWidget/index.js'
|
|
288
|
+
export { CnRedactionEditor } from './CnRedactionEditor/index.js'
|
|
281
289
|
export { CnObjectGeoWidget } from './CnObjectGeoWidget/index.js'
|
|
282
290
|
export { CnLogsPage } from './CnLogsPage/index.js'
|
|
283
291
|
export { CnSettingsPage } from './CnSettingsPage/index.js'
|
|
@@ -435,3 +443,4 @@ export { CnTeamWorkloadWidget } from './CnTeamWorkloadWidget/index.js'
|
|
|
435
443
|
export { CnStackedBarWidgetForm } from './CnStackedBarWidgetForm/index.js'
|
|
436
444
|
export { CnSegmentedControl } from './CnSegmentedControl/index.js'
|
|
437
445
|
export { CnBrandStripe } from './CnBrandStripe/index.js'
|
|
446
|
+
export { CnDuplicateWarning } from './CnDuplicateWarning/index.js'
|
package/src/css/index-page.css
CHANGED
|
@@ -236,6 +236,30 @@
|
|
|
236
236
|
text-align: center;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
.cn-index-page__search-refusal {
|
|
240
|
+
padding: calc(3 * var(--default-grid-baseline)) 0;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.cn-index-page__search-refusal-headline {
|
|
244
|
+
font-weight: bold;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.cn-index-page__search-refusal-term {
|
|
248
|
+
font-family: var(--font-face-monospace, monospace);
|
|
249
|
+
overflow-wrap: anywhere;
|
|
250
|
+
white-space: pre-wrap;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.cn-index-page__search-refusal-term mark {
|
|
254
|
+
background-color: var(--color-warning);
|
|
255
|
+
color: var(--color-main-text);
|
|
256
|
+
font-weight: bold;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.cn-index-page__search-refusal-muted {
|
|
260
|
+
color: var(--color-text-maxcontrast);
|
|
261
|
+
}
|
|
262
|
+
|
|
239
263
|
/* Explicit `0` rather than dropping the declaration: this element carries both
|
|
240
264
|
`.cn-pagination` and `.cn-index-page__pagination`, and `.cn-pagination` sets
|
|
241
265
|
`margin-top: calc(8 * baseline)` at the same (0,1,0) specificity. Only the
|
package/src/index.js
CHANGED
|
@@ -35,6 +35,7 @@ export {
|
|
|
35
35
|
CnActionDialog,
|
|
36
36
|
CnActionsBar,
|
|
37
37
|
CnActionsMenu,
|
|
38
|
+
CnAddressSearch,
|
|
38
39
|
CnAddWidgetModal,
|
|
39
40
|
CnAdminActionCard,
|
|
40
41
|
CnAdminSettingsShell,
|
|
@@ -43,6 +44,7 @@ export {
|
|
|
43
44
|
CnAppLoading,
|
|
44
45
|
CnAppNav,
|
|
45
46
|
CnAppRoot,
|
|
47
|
+
CnAssignDialog,
|
|
46
48
|
CnAuditTrailCard,
|
|
47
49
|
CnAuditTrailWidget,
|
|
48
50
|
CnBannerWidget,
|
|
@@ -91,6 +93,7 @@ export {
|
|
|
91
93
|
CnDataTable,
|
|
92
94
|
CnDateAxisView,
|
|
93
95
|
CnDateRangePicker,
|
|
96
|
+
CnDecisionListWidget,
|
|
94
97
|
CnDeckCardCreate,
|
|
95
98
|
CnDeckCardPicker,
|
|
96
99
|
CnDeleteDialog,
|
|
@@ -108,6 +111,7 @@ export {
|
|
|
108
111
|
CnDividerWidgetForm,
|
|
109
112
|
CnDocumentReviewList,
|
|
110
113
|
CnDocumentReviewWidget,
|
|
114
|
+
CnDuplicateWarning,
|
|
111
115
|
CnDurationField,
|
|
112
116
|
CnEditActionsModal,
|
|
113
117
|
CnEditDataModal,
|
|
@@ -211,6 +215,7 @@ export {
|
|
|
211
215
|
CnMenuWidgetForm,
|
|
212
216
|
CnMetadataTab,
|
|
213
217
|
CnNavCardGrid,
|
|
218
|
+
CnNcDashboardList,
|
|
214
219
|
CnNcDashboardWidgetForm,
|
|
215
220
|
CnNcWidgetGridPicker,
|
|
216
221
|
CnNcWidgetWidget,
|
|
@@ -249,6 +254,7 @@ export {
|
|
|
249
254
|
CnPartyPicker,
|
|
250
255
|
CnPeopleWidget,
|
|
251
256
|
CnPeopleWidgetForm,
|
|
257
|
+
CnPlanWidget,
|
|
252
258
|
CnPresenceAvatars,
|
|
253
259
|
CnProcessSteps,
|
|
254
260
|
CnProgressBar,
|
|
@@ -260,6 +266,7 @@ export {
|
|
|
260
266
|
CnQuicklinksWidgetForm,
|
|
261
267
|
CnRadioCardsWidget,
|
|
262
268
|
CnRecordListWidget,
|
|
269
|
+
CnRedactionEditor,
|
|
263
270
|
CnReferencePreview,
|
|
264
271
|
CnRegisterMapping,
|
|
265
272
|
CnRegisterSchemaSelect,
|
|
@@ -269,6 +276,7 @@ export {
|
|
|
269
276
|
CnRelationLinkModal,
|
|
270
277
|
CnRelationshipGraph,
|
|
271
278
|
CnResourceSelect,
|
|
279
|
+
CnRetentionWidget,
|
|
272
280
|
CnRichSubmitDialog,
|
|
273
281
|
CnRoadmapItem,
|
|
274
282
|
CnRoadmapTab,
|
|
@@ -355,6 +363,7 @@ export {
|
|
|
355
363
|
CnWidgetWrapper,
|
|
356
364
|
CnWizardDialog,
|
|
357
365
|
CnWorkingCalendarWidget,
|
|
366
|
+
CnWorkQueue,
|
|
358
367
|
CnWorkspaceFilterWidget,
|
|
359
368
|
DEFAULT_DATE_RANGE_PRESETS,
|
|
360
369
|
defaultPageTypes,
|
|
@@ -618,6 +627,9 @@ export { followLinkClick, isModifiedClick, isNewTabHandled, isRowMiddleClick, op
|
|
|
618
627
|
export { resolveImageUrl } from './utils/index.js'
|
|
619
628
|
export { resolveDateVariant } from './utils/index.js'
|
|
620
629
|
export { actionLink, dispatchAction } from './utils/actionsDispatcher.js'
|
|
630
|
+
export { candidateWithoutBlanks, duplicateAffordances } from './utils/duplicateWarning.js'
|
|
631
|
+
export { openDuplicateWarning } from './utils/openDuplicateWarning.js'
|
|
632
|
+
export { registerNcDashboardWidgets } from './utils/registerNcDashboardWidgets.js'
|
|
621
633
|
// Nested-modal stacking. `CnAppRoot` installs this itself; apps that do not
|
|
622
634
|
// mount `CnAppRoot` should call `installModalStack()` once from `main.js`, or
|
|
623
635
|
// two open dialogs tie on z-index and the lower one intercepts the clicks.
|