@conduction/nextcloud-vue 2.115.0 → 2.116.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/CnActionButtons/CnActionButtons.vue.js +1 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js +92 -0
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +2 -2
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
- package/dist/esm/components/CnChecklistWidget/CnChecklistWidget.vue.js +9 -0
- package/dist/esm/components/CnChecklistWidget/CnChecklistWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChecklistWidget/CnChecklistWidget.vue2.js +145 -0
- package/dist/esm/components/CnChecklistWidget/CnChecklistWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChecklistWidget/CnChecklistWidget.vue3.js +58 -0
- package/dist/esm/components/CnChecklistWidget/CnChecklistWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChecklistWidget/index.js +28 -0
- package/dist/esm/components/CnChecklistWidget/index.js.map +1 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +25 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +376 -51
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +364 -285
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +11 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +6 -4
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js +1 -1
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js +48 -1
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js +11 -2
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue.js +1 -1
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue2.js +11 -3
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue3.js +8 -3
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +55 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +94 -4
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +248 -239
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue.js +9 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue2.js +282 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue2.js.map +1 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue3.js +202 -0
- package/dist/esm/components/CnRecordListWidget/CnRecordNoteRows.vue3.js.map +1 -0
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +10 -5
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.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/composables/indexSources.js +64 -0
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/composables/useTaskInboxStore.js +85 -0
- package/dist/esm/composables/useTaskInboxStore.js.map +1 -1
- package/dist/esm/dialogs/CnAccessPurposeDialog.vue.js +9 -0
- package/dist/esm/dialogs/CnAccessPurposeDialog.vue.js.map +1 -0
- package/dist/esm/dialogs/CnAccessPurposeDialog.vue2.js +107 -0
- package/dist/esm/dialogs/CnAccessPurposeDialog.vue2.js.map +1 -0
- package/dist/esm/dialogs/CnAccessPurposeDialog.vue3.js +110 -0
- package/dist/esm/dialogs/CnAccessPurposeDialog.vue3.js.map +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +19 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +23 -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 +1054 -911
- package/dist/esm/utils/actionsDispatcher.js +10 -5
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/detailActionModel.js +6 -2
- package/dist/esm/utils/detailActionModel.js.map +1 -1
- package/dist/esm/utils/libraryWidgetKeys.js +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/widgetDispatch.js +1 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +17078 -15224
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1054 -911
- package/l10n/en.json +19 -1
- package/l10n/nl.json +23 -1
- package/package.json +1 -1
- package/src/components/CnActionButtons/CnActionButtons.vue +93 -1
- package/src/components/CnChecklistWidget/CnChecklistWidget.vue +206 -0
- package/src/components/CnChecklistWidget/index.js +27 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +26 -1
- package/src/components/CnDetailPage/CnDetailPage.md +2 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +377 -10
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +15 -2
- package/src/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue +59 -1
- package/src/components/CnMacrosWidget/CnMacrosWidget.vue +16 -6
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +55 -2
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +101 -3
- package/src/components/CnRecordListWidget/CnRecordNoteRows.vue +508 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +10 -5
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/index.js +1 -0
- package/src/composables/indexSources.js +64 -0
- package/src/composables/useTaskInboxStore.js +85 -0
- package/src/dialogs/CnAccessPurposeDialog.vue +183 -0
- package/src/index.js +1 -0
- package/src/schemas/app-manifest-v2.schema.json +49 -3
- package/src/utils/actionsDispatcher.js +10 -5
- package/src/utils/detailActionModel.js +6 -2
- package/src/utils/libraryWidgetKeys.js +1 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/widgetDispatch.js +1 -1
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { emit } from '@nextcloud/event-bus';
|
|
2
|
+
import { translate } from '@nextcloud/l10n';
|
|
3
|
+
import { NcButton, NcTextField } from '@nextcloud/vue';
|
|
4
|
+
import __sfc_main from '../CnIcon/CnIcon.vue.js';
|
|
5
|
+
import __sfc_main$1 from '../CnLeafMountHost/CnLeafMountHost.vue.js';
|
|
6
|
+
import __sfc_main$2 from '../CnStatusBadge/CnStatusBadge.vue.js';
|
|
7
|
+
import { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js';
|
|
8
|
+
import { isAppInstalled } from '../../utils/appInstalled.js';
|
|
9
|
+
import { prefixUrl } from '../../utils/headers.js';
|
|
10
|
+
import { readPath } from '../../utils/readPath.js';
|
|
11
|
+
|
|
12
|
+
/** The bus channel every endpoint-bound widget on the page refetches on. */
|
|
13
|
+
const PAGE_REFRESH_CHANNEL = 'cn:page:refresh';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The rows of a record list drawn as notes to record (`presentation:
|
|
17
|
+
* "notes"`, DqDagAfsluiten "Nog open van vandaag"): per row its pill, title,
|
|
18
|
+
* meta and due text, a field for what happened, an optional integration leaf
|
|
19
|
+
* (humaniq's hours) and a button that records the note on the row's own
|
|
20
|
+
* notes endpoint. What is typed and not yet recorded stays in this browser
|
|
21
|
+
* (session storage) and is counted as the "ready to record" figure; one
|
|
22
|
+
* button records every filled row. After a note is recorded the page's
|
|
23
|
+
* endpoint widgets refetch, so the row moves to whatever lists the server
|
|
24
|
+
* keeps of recorded work.
|
|
25
|
+
*
|
|
26
|
+
* @spec openspec/changes/record-list-notes/specs/record-list-notes/spec.md#requirement-a-record-list-can-collect-and-record-a-note-per-row
|
|
27
|
+
*/
|
|
28
|
+
var script = {
|
|
29
|
+
name: 'CnRecordNoteRows',
|
|
30
|
+
|
|
31
|
+
components: { CnIcon: __sfc_main, CnLeafMountHost: __sfc_main$1, CnStatusBadge: __sfc_main$2, NcButton, NcTextField },
|
|
32
|
+
|
|
33
|
+
props: {
|
|
34
|
+
/** The list's rows as the record list builds them (`key`, `row`, `tag`, `attrs`, `title`, `meta`, `pill`, `due`). */
|
|
35
|
+
items: {
|
|
36
|
+
type: Array,
|
|
37
|
+
default: () => [],
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* `noteAction`, already translated: `{ urlField?, url?, bodyField?,
|
|
42
|
+
* label, fieldLabel, placeholder?, readyLabel, draftsKey?, errorText? }`.
|
|
43
|
+
*/
|
|
44
|
+
action: {
|
|
45
|
+
type: Object,
|
|
46
|
+
required: true,
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/** `recordAll`, already translated: `{ label, title?, text?, icon?, cancel?: { label, to } }`, or null. */
|
|
50
|
+
recordAll: {
|
|
51
|
+
type: Object,
|
|
52
|
+
default: null,
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/** `rowLeaf`: `{ id, app?, register?, objectIdField, schemaField?, schema?, unavailableText? }`, or null. */
|
|
56
|
+
leaf: {
|
|
57
|
+
type: Object,
|
|
58
|
+
default: null,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
setup() {
|
|
63
|
+
const { getById } = useIntegrationRegistry();
|
|
64
|
+
return { getProvider: getById }
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
data() {
|
|
68
|
+
return { drafts: this.readDrafts(), busy: false, error: '' }
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
computed: {
|
|
72
|
+
/** @return {string} The session storage key of this list's drafts. */
|
|
73
|
+
draftsKey() {
|
|
74
|
+
return `cn-record-list-notes:${this.action.draftsKey || 'default'}`
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/** @return {number} The rows with a note typed and not yet recorded. */
|
|
78
|
+
readyCount() {
|
|
79
|
+
return this.items.filter((item) => this.hasDraft(item.key)).length
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
/** @return {string} The label of the ready figure. */
|
|
83
|
+
readyLabel() {
|
|
84
|
+
return this.action.readyLabel || translate('nextcloud-vue', 'filled in, not recorded yet')
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/** @return {string} */
|
|
88
|
+
recordLabel() {
|
|
89
|
+
return this.action.label || translate('nextcloud-vue', 'Record')
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/** @return {string} */
|
|
93
|
+
fieldLabel() {
|
|
94
|
+
return this.action.fieldLabel || translate('nextcloud-vue', 'Note')
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
/** @return {string} */
|
|
98
|
+
placeholder() {
|
|
99
|
+
return this.action.placeholder || ''
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/** @return {object|null} The leaf's integration provider, when one is registered. */
|
|
103
|
+
leafProvider() {
|
|
104
|
+
return this.leaf && this.leaf.id ? (this.getProvider(this.leaf.id) || null) : null
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
/** @return {boolean} Whether the leaf mounts itself (`renderMode: "mount"`). */
|
|
108
|
+
leafIsMount() {
|
|
109
|
+
const provider = this.leafProvider;
|
|
110
|
+
return Boolean(provider) && provider.renderMode === 'mount' && typeof provider.mount === 'function'
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
/** @return {object|null} The leaf's component, in component mode. */
|
|
114
|
+
leafComponent() {
|
|
115
|
+
const provider = this.leafProvider;
|
|
116
|
+
return provider ? (provider.tab || provider.widget || null) : null
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Whether the leaf's app is installed but the leaf is not available, so
|
|
121
|
+
* the row says so instead of silently showing no field.
|
|
122
|
+
*
|
|
123
|
+
* @return {boolean}
|
|
124
|
+
*/
|
|
125
|
+
leafUnavailable() {
|
|
126
|
+
return Boolean(this.leaf && this.leaf.app && !this.leafProvider && isAppInstalled(this.leaf.app))
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
/** @return {string} */
|
|
130
|
+
leafUnavailableText() {
|
|
131
|
+
return this.leaf?.unavailableText || translate('nextcloud-vue', 'This field is not available here.')
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
watch: {
|
|
136
|
+
drafts: {
|
|
137
|
+
deep: true,
|
|
138
|
+
handler() {
|
|
139
|
+
this.saveDrafts();
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
methods: {
|
|
145
|
+
/**
|
|
146
|
+
* The drafts this browser session kept, by row key.
|
|
147
|
+
*
|
|
148
|
+
* @return {Object<string, string>}
|
|
149
|
+
*/
|
|
150
|
+
readDrafts() {
|
|
151
|
+
try {
|
|
152
|
+
const raw = window.sessionStorage.getItem(`cn-record-list-notes:${this.action?.draftsKey || 'default'}`);
|
|
153
|
+
const parsed = raw ? JSON.parse(raw) : {};
|
|
154
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}
|
|
155
|
+
} catch {
|
|
156
|
+
return {}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/** @return {void} */
|
|
161
|
+
saveDrafts() {
|
|
162
|
+
try {
|
|
163
|
+
window.sessionStorage.setItem(this.draftsKey, JSON.stringify(this.drafts));
|
|
164
|
+
} catch {
|
|
165
|
+
// Without session storage the drafts live as long as the page.
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The colour of a due text: a term that ended or ends today in the
|
|
171
|
+
* error colour, one still to come in the warning colour.
|
|
172
|
+
*
|
|
173
|
+
* @param {{tone: string}} due The due text.
|
|
174
|
+
* @return {string} `error`, `warning` or `muted`.
|
|
175
|
+
*/
|
|
176
|
+
dueToneOf(due) {
|
|
177
|
+
if (due.tone === 'danger' || due.tone === 'today') {
|
|
178
|
+
return 'error'
|
|
179
|
+
}
|
|
180
|
+
return due.tone === 'upcoming' ? 'warning' : 'muted'
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @param {string} key The row key.
|
|
185
|
+
* @param {string} value The typed note.
|
|
186
|
+
* @return {void}
|
|
187
|
+
*/
|
|
188
|
+
setDraft(key, value) {
|
|
189
|
+
this.drafts = { ...this.drafts, [key]: String(value ?? '') };
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @param {string} key The row key.
|
|
194
|
+
* @return {boolean} Whether the row has a note typed.
|
|
195
|
+
*/
|
|
196
|
+
hasDraft(key) {
|
|
197
|
+
return String(this.drafts[key] ?? '').trim() !== ''
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The props the row's leaf gets: the row's object, register and schema.
|
|
202
|
+
*
|
|
203
|
+
* @param {object} row The row.
|
|
204
|
+
* @return {object}
|
|
205
|
+
*/
|
|
206
|
+
leafProps(row) {
|
|
207
|
+
const leaf = this.leaf || {};
|
|
208
|
+
return {
|
|
209
|
+
objectId: String(readPath(row, leaf.objectIdField || 'id') ?? ''),
|
|
210
|
+
register: leaf.register || '',
|
|
211
|
+
schema: leaf.schemaField ? String(readPath(row, leaf.schemaField) ?? '') : (leaf.schema || ''),
|
|
212
|
+
surface: 'row',
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* The row's notes endpoint: the row's `urlField`, else `url` with
|
|
218
|
+
* `{field}` filled from the row.
|
|
219
|
+
*
|
|
220
|
+
* @param {object} row The row.
|
|
221
|
+
* @return {string} The URL, or ''.
|
|
222
|
+
*/
|
|
223
|
+
urlOf(row) {
|
|
224
|
+
if (this.action.urlField) {
|
|
225
|
+
return String(readPath(row, this.action.urlField) ?? '')
|
|
226
|
+
}
|
|
227
|
+
const template = String(this.action.url || '');
|
|
228
|
+
return template.replace(/\{([A-Za-z0-9_.]+)\}/g, (_, path) => encodeURIComponent(String(readPath(row, path) ?? '')))
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Record one row's note, then let the page refetch.
|
|
233
|
+
*
|
|
234
|
+
* @param {object} item The row item.
|
|
235
|
+
* @param {boolean} [refresh] Whether to refresh the page after it.
|
|
236
|
+
* @return {Promise<boolean>} Whether it was recorded.
|
|
237
|
+
*/
|
|
238
|
+
async record(item, refresh = true) {
|
|
239
|
+
const text = String(this.drafts[item.key] ?? '').trim();
|
|
240
|
+
const url = this.urlOf(item.row);
|
|
241
|
+
if (text === '' || url === '') {
|
|
242
|
+
return false
|
|
243
|
+
}
|
|
244
|
+
this.busy = true;
|
|
245
|
+
this.error = '';
|
|
246
|
+
try {
|
|
247
|
+
const { default: axios } = await import('@nextcloud/axios');
|
|
248
|
+
await axios.post(prefixUrl(url), { [this.action.bodyField || 'message']: text });
|
|
249
|
+
const { [item.key]: done, ...rest } = this.drafts;
|
|
250
|
+
this.drafts = rest;
|
|
251
|
+
if (refresh) {
|
|
252
|
+
emit(PAGE_REFRESH_CHANNEL, {});
|
|
253
|
+
}
|
|
254
|
+
return true
|
|
255
|
+
} catch {
|
|
256
|
+
this.error = this.action.errorText || translate('nextcloud-vue', 'The note could not be recorded. It is kept here; try again.');
|
|
257
|
+
return false
|
|
258
|
+
} finally {
|
|
259
|
+
this.busy = false;
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Record every row with a note, one after another, then refresh once.
|
|
265
|
+
*
|
|
266
|
+
* @return {Promise<void>}
|
|
267
|
+
*/
|
|
268
|
+
async recordEvery() {
|
|
269
|
+
const filled = this.items.filter((item) => this.hasDraft(item.key));
|
|
270
|
+
let any = false;
|
|
271
|
+
for (const item of filled) {
|
|
272
|
+
any = (await this.record(item, false)) || any;
|
|
273
|
+
}
|
|
274
|
+
if (any) {
|
|
275
|
+
emit(PAGE_REFRESH_CHANNEL, {});
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export { script as default };
|
|
282
|
+
//# sourceMappingURL=CnRecordNoteRows.vue2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnRecordNoteRows.vue2.js","sources":["../../../../src/components/CnRecordListWidget/CnRecordNoteRows.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-record-notes\" data-testid=\"cn-record-list-notes\">\n\t\t<p class=\"cn-record-notes__ready\" data-testid=\"cn-record-list-notes-ready\">\n\t\t\t<span class=\"cn-record-notes__ready-figure\">{{ readyCount }}</span>\n\t\t\t<span class=\"cn-record-notes__ready-label\">{{ readyLabel }}</span>\n\t\t</p>\n\t\t<div\n\t\t\tv-for=\"item in items\"\n\t\t\t:key=\"item.key\"\n\t\t\tclass=\"cn-record-notes__row\"\n\t\t\tdata-testid=\"cn-record-list-item\">\n\t\t\t<div class=\"cn-record-notes__head\">\n\t\t\t\t<CnStatusBadge\n\t\t\t\t\tv-if=\"item.pill\"\n\t\t\t\t\t:label=\"item.pill.label\"\n\t\t\t\t\t:variant=\"item.pill.variant\"\n\t\t\t\t\tsize=\"small\" />\n\t\t\t\t<component\n\t\t\t\t\t:is=\"item.tag\"\n\t\t\t\t\tv-if=\"item.title\"\n\t\t\t\t\tclass=\"cn-record-notes__title\"\n\t\t\t\t\t:class=\"{ 'cn-record-notes__title--link': item.tag !== 'div' }\"\n\t\t\t\t\tv-bind=\"item.attrs\">\n\t\t\t\t\t{{ item.title }}\n\t\t\t\t</component>\n\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-notes__meta\">{{ item.meta }}</span>\n\t\t\t\t<span class=\"cn-record-notes__spacer\" />\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"item.due\"\n\t\t\t\t\tclass=\"cn-record-notes__due\"\n\t\t\t\t\t:class=\"`cn-record-notes__due--${dueToneOf(item.due)}`\"\n\t\t\t\t\tdata-testid=\"cn-record-list-due\">{{ item.due.text }}</span>\n\t\t\t</div>\n\t\t\t<div class=\"cn-record-notes__fields\">\n\t\t\t\t<NcTextField\n\t\t\t\t\tclass=\"cn-record-notes__field\"\n\t\t\t\t\t:modelValue=\"drafts[item.key] || ''\"\n\t\t\t\t\t:label=\"fieldLabel\"\n\t\t\t\t\t:labelOutside=\"false\"\n\t\t\t\t\t:placeholder=\"placeholder\"\n\t\t\t\t\t:disabled=\"busy\"\n\t\t\t\t\t:data-testid=\"`cn-record-list-note-${item.key}`\"\n\t\t\t\t\t@update:modelValue=\"setDraft(item.key, $event)\" />\n\t\t\t\t<div v-if=\"leaf && leafProvider\" class=\"cn-record-notes__leaf\" :data-testid=\"`cn-record-list-leaf-${item.key}`\">\n\t\t\t\t\t<CnLeafMountHost\n\t\t\t\t\t\tv-if=\"leafIsMount\"\n\t\t\t\t\t\t:provider=\"leafProvider\"\n\t\t\t\t\t\t:mountProps=\"leafProps(item.row)\" />\n\t\t\t\t\t<component :is=\"leafComponent\" v-else-if=\"leafComponent\" v-bind=\"leafProps(item.row)\" />\n\t\t\t\t</div>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t:disabled=\"busy || !hasDraft(item.key)\"\n\t\t\t\t\t:data-testid=\"`cn-record-list-record-${item.key}`\"\n\t\t\t\t\t@click=\"record(item)\">\n\t\t\t\t\t{{ recordLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<p\n\t\t\t\tv-if=\"leafUnavailable\"\n\t\t\t\tclass=\"cn-record-notes__leaf-missing\"\n\t\t\t\t:data-testid=\"`cn-record-list-leaf-missing-${item.key}`\">\n\t\t\t\t{{ leafUnavailableText }}\n\t\t\t</p>\n\t\t</div>\n\t\t<p v-if=\"error\"\n\t\t\tclass=\"cn-record-notes__error\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-record-list-notes-error\">\n\t\t\t{{ error }}\n\t\t</p>\n\t\t<div v-if=\"recordAll\" class=\"cn-record-notes__all\" data-testid=\"cn-record-list-record-all\">\n\t\t\t<div class=\"cn-record-notes__all-text\">\n\t\t\t\t<h3 v-if=\"recordAll.title\" class=\"cn-record-notes__all-title\">\n\t\t\t\t\t{{ recordAll.title }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"recordAll.text\" class=\"cn-record-notes__all-sentence\">\n\t\t\t\t\t{{ recordAll.text }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t\t<div class=\"cn-record-notes__all-actions\">\n\t\t\t\t<NcButton v-if=\"recordAll.cancel\" variant=\"secondary\" :to=\"recordAll.cancel.to\">\n\t\t\t\t\t{{ recordAll.cancel.label }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"busy || readyCount === 0\"\n\t\t\t\t\tdata-testid=\"cn-record-list-record-all-button\"\n\t\t\t\t\t@click=\"recordEvery\">\n\t\t\t\t\t<template v-if=\"recordAll.icon\" #icon>\n\t\t\t\t\t\t<CnIcon :name=\"recordAll.icon\" :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ recordAll.label }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcTextField } from '@nextcloud/vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\nimport CnLeafMountHost from '../CnLeafMountHost/CnLeafMountHost.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js'\nimport { isAppInstalled } from '../../utils/appInstalled.js'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { readPath } from '../../utils/readPath.js'\n\n/** The bus channel every endpoint-bound widget on the page refetches on. */\nconst PAGE_REFRESH_CHANNEL = 'cn:page:refresh'\n\n/**\n * The rows of a record list drawn as notes to record (`presentation:\n * \"notes\"`, DqDagAfsluiten \"Nog open van vandaag\"): per row its pill, title,\n * meta and due text, a field for what happened, an optional integration leaf\n * (humaniq's hours) and a button that records the note on the row's own\n * notes endpoint. What is typed and not yet recorded stays in this browser\n * (session storage) and is counted as the \"ready to record\" figure; one\n * button records every filled row. After a note is recorded the page's\n * endpoint widgets refetch, so the row moves to whatever lists the server\n * keeps of recorded work.\n *\n * @spec openspec/changes/record-list-notes/specs/record-list-notes/spec.md#requirement-a-record-list-can-collect-and-record-a-note-per-row\n */\nexport default {\n\tname: 'CnRecordNoteRows',\n\n\tcomponents: { CnIcon, CnLeafMountHost, CnStatusBadge, NcButton, NcTextField },\n\n\tprops: {\n\t\t/** The list's rows as the record list builds them (`key`, `row`, `tag`, `attrs`, `title`, `meta`, `pill`, `due`). */\n\t\titems: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * `noteAction`, already translated: `{ urlField?, url?, bodyField?,\n\t\t * label, fieldLabel, placeholder?, readyLabel, draftsKey?, errorText? }`.\n\t\t */\n\t\taction: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** `recordAll`, already translated: `{ label, title?, text?, icon?, cancel?: { label, to } }`, or null. */\n\t\trecordAll: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** `rowLeaf`: `{ id, app?, register?, objectIdField, schemaField?, schema?, unavailableText? }`, or null. */\n\t\tleaf: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tsetup() {\n\t\tconst { getById } = useIntegrationRegistry()\n\t\treturn { getProvider: getById }\n\t},\n\n\tdata() {\n\t\treturn { drafts: this.readDrafts(), busy: false, error: '' }\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} The session storage key of this list's drafts. */\n\t\tdraftsKey() {\n\t\t\treturn `cn-record-list-notes:${this.action.draftsKey || 'default'}`\n\t\t},\n\n\t\t/** @return {number} The rows with a note typed and not yet recorded. */\n\t\treadyCount() {\n\t\t\treturn this.items.filter((item) => this.hasDraft(item.key)).length\n\t\t},\n\n\t\t/** @return {string} The label of the ready figure. */\n\t\treadyLabel() {\n\t\t\treturn this.action.readyLabel || t('nextcloud-vue', 'filled in, not recorded yet')\n\t\t},\n\n\t\t/** @return {string} */\n\t\trecordLabel() {\n\t\t\treturn this.action.label || t('nextcloud-vue', 'Record')\n\t\t},\n\n\t\t/** @return {string} */\n\t\tfieldLabel() {\n\t\t\treturn this.action.fieldLabel || t('nextcloud-vue', 'Note')\n\t\t},\n\n\t\t/** @return {string} */\n\t\tplaceholder() {\n\t\t\treturn this.action.placeholder || ''\n\t\t},\n\n\t\t/** @return {object|null} The leaf's integration provider, when one is registered. */\n\t\tleafProvider() {\n\t\t\treturn this.leaf && this.leaf.id ? (this.getProvider(this.leaf.id) || null) : null\n\t\t},\n\n\t\t/** @return {boolean} Whether the leaf mounts itself (`renderMode: \"mount\"`). */\n\t\tleafIsMount() {\n\t\t\tconst provider = this.leafProvider\n\t\t\treturn Boolean(provider) && provider.renderMode === 'mount' && typeof provider.mount === 'function'\n\t\t},\n\n\t\t/** @return {object|null} The leaf's component, in component mode. */\n\t\tleafComponent() {\n\t\t\tconst provider = this.leafProvider\n\t\t\treturn provider ? (provider.tab || provider.widget || null) : null\n\t\t},\n\n\t\t/**\n\t\t * Whether the leaf's app is installed but the leaf is not available, so\n\t\t * the row says so instead of silently showing no field.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tleafUnavailable() {\n\t\t\treturn Boolean(this.leaf && this.leaf.app && !this.leafProvider && isAppInstalled(this.leaf.app))\n\t\t},\n\n\t\t/** @return {string} */\n\t\tleafUnavailableText() {\n\t\t\treturn this.leaf?.unavailableText || t('nextcloud-vue', 'This field is not available here.')\n\t\t},\n\t},\n\n\twatch: {\n\t\tdrafts: {\n\t\t\tdeep: true,\n\t\t\thandler() {\n\t\t\t\tthis.saveDrafts()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The drafts this browser session kept, by row key.\n\t\t *\n\t\t * @return {Object<string, string>}\n\t\t */\n\t\treadDrafts() {\n\t\t\ttry {\n\t\t\t\tconst raw = window.sessionStorage.getItem(`cn-record-list-notes:${this.action?.draftsKey || 'default'}`)\n\t\t\t\tconst parsed = raw ? JSON.parse(raw) : {}\n\t\t\t\treturn parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}\n\t\t\t} catch {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t},\n\n\t\t/** @return {void} */\n\t\tsaveDrafts() {\n\t\t\ttry {\n\t\t\t\twindow.sessionStorage.setItem(this.draftsKey, JSON.stringify(this.drafts))\n\t\t\t} catch {\n\t\t\t\t// Without session storage the drafts live as long as the page.\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The colour of a due text: a term that ended or ends today in the\n\t\t * error colour, one still to come in the warning colour.\n\t\t *\n\t\t * @param {{tone: string}} due The due text.\n\t\t * @return {string} `error`, `warning` or `muted`.\n\t\t */\n\t\tdueToneOf(due) {\n\t\t\tif (due.tone === 'danger' || due.tone === 'today') {\n\t\t\t\treturn 'error'\n\t\t\t}\n\t\t\treturn due.tone === 'upcoming' ? 'warning' : 'muted'\n\t\t},\n\n\t\t/**\n\t\t * @param {string} key The row key.\n\t\t * @param {string} value The typed note.\n\t\t * @return {void}\n\t\t */\n\t\tsetDraft(key, value) {\n\t\t\tthis.drafts = { ...this.drafts, [key]: String(value ?? '') }\n\t\t},\n\n\t\t/**\n\t\t * @param {string} key The row key.\n\t\t * @return {boolean} Whether the row has a note typed.\n\t\t */\n\t\thasDraft(key) {\n\t\t\treturn String(this.drafts[key] ?? '').trim() !== ''\n\t\t},\n\n\t\t/**\n\t\t * The props the row's leaf gets: the row's object, register and schema.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object}\n\t\t */\n\t\tleafProps(row) {\n\t\t\tconst leaf = this.leaf || {}\n\t\t\treturn {\n\t\t\t\tobjectId: String(readPath(row, leaf.objectIdField || 'id') ?? ''),\n\t\t\t\tregister: leaf.register || '',\n\t\t\t\tschema: leaf.schemaField ? String(readPath(row, leaf.schemaField) ?? '') : (leaf.schema || ''),\n\t\t\t\tsurface: 'row',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The row's notes endpoint: the row's `urlField`, else `url` with\n\t\t * `{field}` filled from the row.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} The URL, or ''.\n\t\t */\n\t\turlOf(row) {\n\t\t\tif (this.action.urlField) {\n\t\t\t\treturn String(readPath(row, this.action.urlField) ?? '')\n\t\t\t}\n\t\t\tconst template = String(this.action.url || '')\n\t\t\treturn template.replace(/\\{([A-Za-z0-9_.]+)\\}/g, (_, path) => encodeURIComponent(String(readPath(row, path) ?? '')))\n\t\t},\n\n\t\t/**\n\t\t * Record one row's note, then let the page refetch.\n\t\t *\n\t\t * @param {object} item The row item.\n\t\t * @param {boolean} [refresh] Whether to refresh the page after it.\n\t\t * @return {Promise<boolean>} Whether it was recorded.\n\t\t */\n\t\tasync record(item, refresh = true) {\n\t\t\tconst text = String(this.drafts[item.key] ?? '').trim()\n\t\t\tconst url = this.urlOf(item.row)\n\t\t\tif (text === '' || url === '') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst { default: axios } = await import('@nextcloud/axios')\n\t\t\t\tawait axios.post(prefixUrl(url), { [this.action.bodyField || 'message']: text })\n\t\t\t\tconst { [item.key]: done, ...rest } = this.drafts\n\t\t\t\tthis.drafts = rest\n\t\t\t\tif (refresh) {\n\t\t\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t} catch {\n\t\t\t\tthis.error = this.action.errorText || t('nextcloud-vue', 'The note could not be recorded. It is kept here; try again.')\n\t\t\t\treturn false\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Record every row with a note, one after another, then refresh once.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync recordEvery() {\n\t\t\tconst filled = this.items.filter((item) => this.hasDraft(item.key))\n\t\t\tlet any = false\n\t\t\tfor (const item of filled) {\n\t\t\t\tany = (await this.record(item, false)) || any\n\t\t\t}\n\t\t\tif (any) {\n\t\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-record-notes {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 0;\n}\n\n.cn-record-notes__ready {\n\tdisplay: flex;\n\talign-items: baseline;\n\tgap: 6px;\n\tmargin: 0 0 8px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-record-notes__ready-figure {\n\tfont-weight: 700;\n\tcolor: var(--color-main-text);\n}\n\n.cn-record-notes__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 10px;\n\tpadding: 14px 0;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-notes__head {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-record-notes__title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-record-notes__title--link {\n\ttext-decoration: underline;\n}\n\n.cn-record-notes__meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-record-notes__spacer {\n\tflex: 1 1 auto;\n}\n\n.cn-record-notes__due {\n\tfont-weight: 600;\n}\n\n.cn-record-notes__due--error {\n\tcolor: var(--color-error-text);\n}\n\n.cn-record-notes__due--warning {\n\tcolor: var(--color-warning-text);\n}\n\n.cn-record-notes__due--muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-weight: 400;\n}\n\n.cn-record-notes__fields {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 10px;\n}\n\n.cn-record-notes__field {\n\tflex: 1 1 260px;\n}\n\n.cn-record-notes__leaf {\n\tflex: 0 0 auto;\n}\n\n.cn-record-notes__leaf-missing,\n.cn-record-notes__error {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-record-notes__error {\n\tcolor: var(--color-error-text);\n}\n\n.cn-record-notes__all {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-top: 8px;\n\tpadding-top: 14px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-notes__all-title {\n\tmargin: 0;\n\tfont-size: 16px;\n}\n\n.cn-record-notes__all-sentence {\n\tmargin: 4px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-record-notes__all-actions {\n\tdisplay: flex;\n\tgap: 8px;\n}\n</style>\n"],"names":["CnIcon","CnLeafMountHost","CnStatusBadge","t"],"mappings":"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"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, createElementVNode, toDisplayString, Fragment, renderList, createBlock, createCommentVNode, resolveDynamicComponent, mergeProps, withCtx, createTextVNode, normalizeClass, createVNode, createSlots } from 'vue';
|
|
2
|
+
|
|
3
|
+
const _hoisted_1 = {
|
|
4
|
+
class: "cn-record-notes",
|
|
5
|
+
"data-testid": "cn-record-list-notes"
|
|
6
|
+
};
|
|
7
|
+
const _hoisted_2 = {
|
|
8
|
+
class: "cn-record-notes__ready",
|
|
9
|
+
"data-testid": "cn-record-list-notes-ready"
|
|
10
|
+
};
|
|
11
|
+
const _hoisted_3 = { class: "cn-record-notes__ready-figure" };
|
|
12
|
+
const _hoisted_4 = { class: "cn-record-notes__ready-label" };
|
|
13
|
+
const _hoisted_5 = { class: "cn-record-notes__head" };
|
|
14
|
+
const _hoisted_6 = {
|
|
15
|
+
key: 2,
|
|
16
|
+
class: "cn-record-notes__meta"
|
|
17
|
+
};
|
|
18
|
+
const _hoisted_7 = { class: "cn-record-notes__fields" };
|
|
19
|
+
const _hoisted_8 = ["data-testid"];
|
|
20
|
+
const _hoisted_9 = ["data-testid"];
|
|
21
|
+
const _hoisted_10 = {
|
|
22
|
+
key: 0,
|
|
23
|
+
class: "cn-record-notes__error",
|
|
24
|
+
role: "alert",
|
|
25
|
+
"data-testid": "cn-record-list-notes-error"
|
|
26
|
+
};
|
|
27
|
+
const _hoisted_11 = {
|
|
28
|
+
key: 1,
|
|
29
|
+
class: "cn-record-notes__all",
|
|
30
|
+
"data-testid": "cn-record-list-record-all"
|
|
31
|
+
};
|
|
32
|
+
const _hoisted_12 = { class: "cn-record-notes__all-text" };
|
|
33
|
+
const _hoisted_13 = {
|
|
34
|
+
key: 0,
|
|
35
|
+
class: "cn-record-notes__all-title"
|
|
36
|
+
};
|
|
37
|
+
const _hoisted_14 = {
|
|
38
|
+
key: 1,
|
|
39
|
+
class: "cn-record-notes__all-sentence"
|
|
40
|
+
};
|
|
41
|
+
const _hoisted_15 = { class: "cn-record-notes__all-actions" };
|
|
42
|
+
|
|
43
|
+
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
44
|
+
const _component_CnStatusBadge = resolveComponent("CnStatusBadge");
|
|
45
|
+
const _component_NcTextField = resolveComponent("NcTextField");
|
|
46
|
+
const _component_CnLeafMountHost = resolveComponent("CnLeafMountHost");
|
|
47
|
+
const _component_NcButton = resolveComponent("NcButton");
|
|
48
|
+
const _component_CnIcon = resolveComponent("CnIcon");
|
|
49
|
+
|
|
50
|
+
return (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
51
|
+
createElementVNode("p", _hoisted_2, [
|
|
52
|
+
createElementVNode("span", _hoisted_3, toDisplayString($options.readyCount), 1 /* TEXT */),
|
|
53
|
+
createElementVNode("span", _hoisted_4, toDisplayString($options.readyLabel), 1 /* TEXT */)
|
|
54
|
+
]),
|
|
55
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($props.items, (item) => {
|
|
56
|
+
return (openBlock(), createElementBlock("div", {
|
|
57
|
+
key: item.key,
|
|
58
|
+
class: "cn-record-notes__row",
|
|
59
|
+
"data-testid": "cn-record-list-item"
|
|
60
|
+
}, [
|
|
61
|
+
createElementVNode("div", _hoisted_5, [
|
|
62
|
+
(item.pill)
|
|
63
|
+
? (openBlock(), createBlock(_component_CnStatusBadge, {
|
|
64
|
+
key: 0,
|
|
65
|
+
label: item.pill.label,
|
|
66
|
+
variant: item.pill.variant,
|
|
67
|
+
size: "small"
|
|
68
|
+
}, null, 8 /* PROPS */, ["label", "variant"]))
|
|
69
|
+
: createCommentVNode("v-if", true),
|
|
70
|
+
(item.title)
|
|
71
|
+
? (openBlock(), createBlock(resolveDynamicComponent(item.tag), mergeProps({
|
|
72
|
+
key: 1,
|
|
73
|
+
class: ["cn-record-notes__title", { 'cn-record-notes__title--link': item.tag !== 'div' }]
|
|
74
|
+
}, { ref_for: true }, item.attrs), {
|
|
75
|
+
default: withCtx(() => [
|
|
76
|
+
createTextVNode(toDisplayString(item.title), 1 /* TEXT */)
|
|
77
|
+
]),
|
|
78
|
+
_: 2 /* DYNAMIC */
|
|
79
|
+
}, 1040 /* FULL_PROPS, DYNAMIC_SLOTS */, ["class"]))
|
|
80
|
+
: createCommentVNode("v-if", true),
|
|
81
|
+
(item.meta)
|
|
82
|
+
? (openBlock(), createElementBlock("span", _hoisted_6, toDisplayString(item.meta), 1 /* TEXT */))
|
|
83
|
+
: createCommentVNode("v-if", true),
|
|
84
|
+
_cache[0] || (_cache[0] = createElementVNode("span", { class: "cn-record-notes__spacer" }, null, -1 /* CACHED */)),
|
|
85
|
+
(item.due)
|
|
86
|
+
? (openBlock(), createElementBlock("span", {
|
|
87
|
+
key: 3,
|
|
88
|
+
class: normalizeClass(["cn-record-notes__due", `cn-record-notes__due--${$options.dueToneOf(item.due)}`]),
|
|
89
|
+
"data-testid": "cn-record-list-due"
|
|
90
|
+
}, toDisplayString(item.due.text), 3 /* TEXT, CLASS */))
|
|
91
|
+
: createCommentVNode("v-if", true)
|
|
92
|
+
]),
|
|
93
|
+
createElementVNode("div", _hoisted_7, [
|
|
94
|
+
createVNode(_component_NcTextField, {
|
|
95
|
+
class: "cn-record-notes__field",
|
|
96
|
+
modelValue: $data.drafts[item.key] || '',
|
|
97
|
+
label: $options.fieldLabel,
|
|
98
|
+
labelOutside: false,
|
|
99
|
+
placeholder: $options.placeholder,
|
|
100
|
+
disabled: $data.busy,
|
|
101
|
+
"data-testid": `cn-record-list-note-${item.key}`,
|
|
102
|
+
"onUpdate:modelValue": $event => ($options.setDraft(item.key, $event))
|
|
103
|
+
}, null, 8 /* PROPS */, ["modelValue", "label", "placeholder", "disabled", "data-testid", "onUpdate:modelValue"]),
|
|
104
|
+
($props.leaf && $options.leafProvider)
|
|
105
|
+
? (openBlock(), createElementBlock("div", {
|
|
106
|
+
key: 0,
|
|
107
|
+
class: "cn-record-notes__leaf",
|
|
108
|
+
"data-testid": `cn-record-list-leaf-${item.key}`
|
|
109
|
+
}, [
|
|
110
|
+
($options.leafIsMount)
|
|
111
|
+
? (openBlock(), createBlock(_component_CnLeafMountHost, {
|
|
112
|
+
key: 0,
|
|
113
|
+
provider: $options.leafProvider,
|
|
114
|
+
mountProps: $options.leafProps(item.row)
|
|
115
|
+
}, null, 8 /* PROPS */, ["provider", "mountProps"]))
|
|
116
|
+
: ($options.leafComponent)
|
|
117
|
+
? (openBlock(), createBlock(resolveDynamicComponent($options.leafComponent), mergeProps({
|
|
118
|
+
key: 1,
|
|
119
|
+
ref_for: true
|
|
120
|
+
}, $options.leafProps(item.row)), null, 16 /* FULL_PROPS */))
|
|
121
|
+
: createCommentVNode("v-if", true)
|
|
122
|
+
], 8 /* PROPS */, _hoisted_8))
|
|
123
|
+
: createCommentVNode("v-if", true),
|
|
124
|
+
createVNode(_component_NcButton, {
|
|
125
|
+
variant: "secondary",
|
|
126
|
+
disabled: $data.busy || !$options.hasDraft(item.key),
|
|
127
|
+
"data-testid": `cn-record-list-record-${item.key}`,
|
|
128
|
+
onClick: $event => ($options.record(item))
|
|
129
|
+
}, {
|
|
130
|
+
default: withCtx(() => [
|
|
131
|
+
createTextVNode(toDisplayString($options.recordLabel), 1 /* TEXT */)
|
|
132
|
+
]),
|
|
133
|
+
_: 1 /* STABLE */
|
|
134
|
+
}, 8 /* PROPS */, ["disabled", "data-testid", "onClick"])
|
|
135
|
+
]),
|
|
136
|
+
($options.leafUnavailable)
|
|
137
|
+
? (openBlock(), createElementBlock("p", {
|
|
138
|
+
key: 0,
|
|
139
|
+
class: "cn-record-notes__leaf-missing",
|
|
140
|
+
"data-testid": `cn-record-list-leaf-missing-${item.key}`
|
|
141
|
+
}, toDisplayString($options.leafUnavailableText), 9 /* TEXT, PROPS */, _hoisted_9))
|
|
142
|
+
: createCommentVNode("v-if", true)
|
|
143
|
+
]))
|
|
144
|
+
}), 128 /* KEYED_FRAGMENT */)),
|
|
145
|
+
($data.error)
|
|
146
|
+
? (openBlock(), createElementBlock("p", _hoisted_10, toDisplayString($data.error), 1 /* TEXT */))
|
|
147
|
+
: createCommentVNode("v-if", true),
|
|
148
|
+
($props.recordAll)
|
|
149
|
+
? (openBlock(), createElementBlock("div", _hoisted_11, [
|
|
150
|
+
createElementVNode("div", _hoisted_12, [
|
|
151
|
+
($props.recordAll.title)
|
|
152
|
+
? (openBlock(), createElementBlock("h3", _hoisted_13, toDisplayString($props.recordAll.title), 1 /* TEXT */))
|
|
153
|
+
: createCommentVNode("v-if", true),
|
|
154
|
+
($props.recordAll.text)
|
|
155
|
+
? (openBlock(), createElementBlock("p", _hoisted_14, toDisplayString($props.recordAll.text), 1 /* TEXT */))
|
|
156
|
+
: createCommentVNode("v-if", true)
|
|
157
|
+
]),
|
|
158
|
+
createElementVNode("div", _hoisted_15, [
|
|
159
|
+
($props.recordAll.cancel)
|
|
160
|
+
? (openBlock(), createBlock(_component_NcButton, {
|
|
161
|
+
key: 0,
|
|
162
|
+
variant: "secondary",
|
|
163
|
+
to: $props.recordAll.cancel.to
|
|
164
|
+
}, {
|
|
165
|
+
default: withCtx(() => [
|
|
166
|
+
createTextVNode(toDisplayString($props.recordAll.cancel.label), 1 /* TEXT */)
|
|
167
|
+
]),
|
|
168
|
+
_: 1 /* STABLE */
|
|
169
|
+
}, 8 /* PROPS */, ["to"]))
|
|
170
|
+
: createCommentVNode("v-if", true),
|
|
171
|
+
createVNode(_component_NcButton, {
|
|
172
|
+
variant: "primary",
|
|
173
|
+
disabled: $data.busy || $options.readyCount === 0,
|
|
174
|
+
"data-testid": "cn-record-list-record-all-button",
|
|
175
|
+
onClick: $options.recordEvery
|
|
176
|
+
}, createSlots({
|
|
177
|
+
default: withCtx(() => [
|
|
178
|
+
createTextVNode(" " + toDisplayString($props.recordAll.label), 1 /* TEXT */)
|
|
179
|
+
]),
|
|
180
|
+
_: 2 /* DYNAMIC */
|
|
181
|
+
}, [
|
|
182
|
+
($props.recordAll.icon)
|
|
183
|
+
? {
|
|
184
|
+
name: "icon",
|
|
185
|
+
fn: withCtx(() => [
|
|
186
|
+
createVNode(_component_CnIcon, {
|
|
187
|
+
name: $props.recordAll.icon,
|
|
188
|
+
size: 20
|
|
189
|
+
}, null, 8 /* PROPS */, ["name"])
|
|
190
|
+
]),
|
|
191
|
+
key: "0"
|
|
192
|
+
}
|
|
193
|
+
: undefined
|
|
194
|
+
]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["disabled", "onClick"])
|
|
195
|
+
])
|
|
196
|
+
]))
|
|
197
|
+
: createCommentVNode("v-if", true)
|
|
198
|
+
]))
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export { render };
|
|
202
|
+
//# sourceMappingURL=CnRecordNoteRows.vue3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnRecordNoteRows.vue3.js","sources":["../../../../src/components/CnRecordListWidget/CnRecordNoteRows.vue?vue&type=template&id=6d7ff53b&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-record-notes\" data-testid=\"cn-record-list-notes\">\n\t\t<p class=\"cn-record-notes__ready\" data-testid=\"cn-record-list-notes-ready\">\n\t\t\t<span class=\"cn-record-notes__ready-figure\">{{ readyCount }}</span>\n\t\t\t<span class=\"cn-record-notes__ready-label\">{{ readyLabel }}</span>\n\t\t</p>\n\t\t<div\n\t\t\tv-for=\"item in items\"\n\t\t\t:key=\"item.key\"\n\t\t\tclass=\"cn-record-notes__row\"\n\t\t\tdata-testid=\"cn-record-list-item\">\n\t\t\t<div class=\"cn-record-notes__head\">\n\t\t\t\t<CnStatusBadge\n\t\t\t\t\tv-if=\"item.pill\"\n\t\t\t\t\t:label=\"item.pill.label\"\n\t\t\t\t\t:variant=\"item.pill.variant\"\n\t\t\t\t\tsize=\"small\" />\n\t\t\t\t<component\n\t\t\t\t\t:is=\"item.tag\"\n\t\t\t\t\tv-if=\"item.title\"\n\t\t\t\t\tclass=\"cn-record-notes__title\"\n\t\t\t\t\t:class=\"{ 'cn-record-notes__title--link': item.tag !== 'div' }\"\n\t\t\t\t\tv-bind=\"item.attrs\">\n\t\t\t\t\t{{ item.title }}\n\t\t\t\t</component>\n\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-notes__meta\">{{ item.meta }}</span>\n\t\t\t\t<span class=\"cn-record-notes__spacer\" />\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"item.due\"\n\t\t\t\t\tclass=\"cn-record-notes__due\"\n\t\t\t\t\t:class=\"`cn-record-notes__due--${dueToneOf(item.due)}`\"\n\t\t\t\t\tdata-testid=\"cn-record-list-due\">{{ item.due.text }}</span>\n\t\t\t</div>\n\t\t\t<div class=\"cn-record-notes__fields\">\n\t\t\t\t<NcTextField\n\t\t\t\t\tclass=\"cn-record-notes__field\"\n\t\t\t\t\t:modelValue=\"drafts[item.key] || ''\"\n\t\t\t\t\t:label=\"fieldLabel\"\n\t\t\t\t\t:labelOutside=\"false\"\n\t\t\t\t\t:placeholder=\"placeholder\"\n\t\t\t\t\t:disabled=\"busy\"\n\t\t\t\t\t:data-testid=\"`cn-record-list-note-${item.key}`\"\n\t\t\t\t\t@update:modelValue=\"setDraft(item.key, $event)\" />\n\t\t\t\t<div v-if=\"leaf && leafProvider\" class=\"cn-record-notes__leaf\" :data-testid=\"`cn-record-list-leaf-${item.key}`\">\n\t\t\t\t\t<CnLeafMountHost\n\t\t\t\t\t\tv-if=\"leafIsMount\"\n\t\t\t\t\t\t:provider=\"leafProvider\"\n\t\t\t\t\t\t:mountProps=\"leafProps(item.row)\" />\n\t\t\t\t\t<component :is=\"leafComponent\" v-else-if=\"leafComponent\" v-bind=\"leafProps(item.row)\" />\n\t\t\t\t</div>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t:disabled=\"busy || !hasDraft(item.key)\"\n\t\t\t\t\t:data-testid=\"`cn-record-list-record-${item.key}`\"\n\t\t\t\t\t@click=\"record(item)\">\n\t\t\t\t\t{{ recordLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t\t<p\n\t\t\t\tv-if=\"leafUnavailable\"\n\t\t\t\tclass=\"cn-record-notes__leaf-missing\"\n\t\t\t\t:data-testid=\"`cn-record-list-leaf-missing-${item.key}`\">\n\t\t\t\t{{ leafUnavailableText }}\n\t\t\t</p>\n\t\t</div>\n\t\t<p v-if=\"error\"\n\t\t\tclass=\"cn-record-notes__error\"\n\t\t\trole=\"alert\"\n\t\t\tdata-testid=\"cn-record-list-notes-error\">\n\t\t\t{{ error }}\n\t\t</p>\n\t\t<div v-if=\"recordAll\" class=\"cn-record-notes__all\" data-testid=\"cn-record-list-record-all\">\n\t\t\t<div class=\"cn-record-notes__all-text\">\n\t\t\t\t<h3 v-if=\"recordAll.title\" class=\"cn-record-notes__all-title\">\n\t\t\t\t\t{{ recordAll.title }}\n\t\t\t\t</h3>\n\t\t\t\t<p v-if=\"recordAll.text\" class=\"cn-record-notes__all-sentence\">\n\t\t\t\t\t{{ recordAll.text }}\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t\t<div class=\"cn-record-notes__all-actions\">\n\t\t\t\t<NcButton v-if=\"recordAll.cancel\" variant=\"secondary\" :to=\"recordAll.cancel.to\">\n\t\t\t\t\t{{ recordAll.cancel.label }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"busy || readyCount === 0\"\n\t\t\t\t\tdata-testid=\"cn-record-list-record-all-button\"\n\t\t\t\t\t@click=\"recordEvery\">\n\t\t\t\t\t<template v-if=\"recordAll.icon\" #icon>\n\t\t\t\t\t\t<CnIcon :name=\"recordAll.icon\" :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ recordAll.label }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { emit } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcTextField } from '@nextcloud/vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\nimport CnLeafMountHost from '../CnLeafMountHost/CnLeafMountHost.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js'\nimport { isAppInstalled } from '../../utils/appInstalled.js'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { readPath } from '../../utils/readPath.js'\n\n/** The bus channel every endpoint-bound widget on the page refetches on. */\nconst PAGE_REFRESH_CHANNEL = 'cn:page:refresh'\n\n/**\n * The rows of a record list drawn as notes to record (`presentation:\n * \"notes\"`, DqDagAfsluiten \"Nog open van vandaag\"): per row its pill, title,\n * meta and due text, a field for what happened, an optional integration leaf\n * (humaniq's hours) and a button that records the note on the row's own\n * notes endpoint. What is typed and not yet recorded stays in this browser\n * (session storage) and is counted as the \"ready to record\" figure; one\n * button records every filled row. After a note is recorded the page's\n * endpoint widgets refetch, so the row moves to whatever lists the server\n * keeps of recorded work.\n *\n * @spec openspec/changes/record-list-notes/specs/record-list-notes/spec.md#requirement-a-record-list-can-collect-and-record-a-note-per-row\n */\nexport default {\n\tname: 'CnRecordNoteRows',\n\n\tcomponents: { CnIcon, CnLeafMountHost, CnStatusBadge, NcButton, NcTextField },\n\n\tprops: {\n\t\t/** The list's rows as the record list builds them (`key`, `row`, `tag`, `attrs`, `title`, `meta`, `pill`, `due`). */\n\t\titems: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * `noteAction`, already translated: `{ urlField?, url?, bodyField?,\n\t\t * label, fieldLabel, placeholder?, readyLabel, draftsKey?, errorText? }`.\n\t\t */\n\t\taction: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** `recordAll`, already translated: `{ label, title?, text?, icon?, cancel?: { label, to } }`, or null. */\n\t\trecordAll: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** `rowLeaf`: `{ id, app?, register?, objectIdField, schemaField?, schema?, unavailableText? }`, or null. */\n\t\tleaf: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tsetup() {\n\t\tconst { getById } = useIntegrationRegistry()\n\t\treturn { getProvider: getById }\n\t},\n\n\tdata() {\n\t\treturn { drafts: this.readDrafts(), busy: false, error: '' }\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} The session storage key of this list's drafts. */\n\t\tdraftsKey() {\n\t\t\treturn `cn-record-list-notes:${this.action.draftsKey || 'default'}`\n\t\t},\n\n\t\t/** @return {number} The rows with a note typed and not yet recorded. */\n\t\treadyCount() {\n\t\t\treturn this.items.filter((item) => this.hasDraft(item.key)).length\n\t\t},\n\n\t\t/** @return {string} The label of the ready figure. */\n\t\treadyLabel() {\n\t\t\treturn this.action.readyLabel || t('nextcloud-vue', 'filled in, not recorded yet')\n\t\t},\n\n\t\t/** @return {string} */\n\t\trecordLabel() {\n\t\t\treturn this.action.label || t('nextcloud-vue', 'Record')\n\t\t},\n\n\t\t/** @return {string} */\n\t\tfieldLabel() {\n\t\t\treturn this.action.fieldLabel || t('nextcloud-vue', 'Note')\n\t\t},\n\n\t\t/** @return {string} */\n\t\tplaceholder() {\n\t\t\treturn this.action.placeholder || ''\n\t\t},\n\n\t\t/** @return {object|null} The leaf's integration provider, when one is registered. */\n\t\tleafProvider() {\n\t\t\treturn this.leaf && this.leaf.id ? (this.getProvider(this.leaf.id) || null) : null\n\t\t},\n\n\t\t/** @return {boolean} Whether the leaf mounts itself (`renderMode: \"mount\"`). */\n\t\tleafIsMount() {\n\t\t\tconst provider = this.leafProvider\n\t\t\treturn Boolean(provider) && provider.renderMode === 'mount' && typeof provider.mount === 'function'\n\t\t},\n\n\t\t/** @return {object|null} The leaf's component, in component mode. */\n\t\tleafComponent() {\n\t\t\tconst provider = this.leafProvider\n\t\t\treturn provider ? (provider.tab || provider.widget || null) : null\n\t\t},\n\n\t\t/**\n\t\t * Whether the leaf's app is installed but the leaf is not available, so\n\t\t * the row says so instead of silently showing no field.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tleafUnavailable() {\n\t\t\treturn Boolean(this.leaf && this.leaf.app && !this.leafProvider && isAppInstalled(this.leaf.app))\n\t\t},\n\n\t\t/** @return {string} */\n\t\tleafUnavailableText() {\n\t\t\treturn this.leaf?.unavailableText || t('nextcloud-vue', 'This field is not available here.')\n\t\t},\n\t},\n\n\twatch: {\n\t\tdrafts: {\n\t\t\tdeep: true,\n\t\t\thandler() {\n\t\t\t\tthis.saveDrafts()\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The drafts this browser session kept, by row key.\n\t\t *\n\t\t * @return {Object<string, string>}\n\t\t */\n\t\treadDrafts() {\n\t\t\ttry {\n\t\t\t\tconst raw = window.sessionStorage.getItem(`cn-record-list-notes:${this.action?.draftsKey || 'default'}`)\n\t\t\t\tconst parsed = raw ? JSON.parse(raw) : {}\n\t\t\t\treturn parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}\n\t\t\t} catch {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t},\n\n\t\t/** @return {void} */\n\t\tsaveDrafts() {\n\t\t\ttry {\n\t\t\t\twindow.sessionStorage.setItem(this.draftsKey, JSON.stringify(this.drafts))\n\t\t\t} catch {\n\t\t\t\t// Without session storage the drafts live as long as the page.\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The colour of a due text: a term that ended or ends today in the\n\t\t * error colour, one still to come in the warning colour.\n\t\t *\n\t\t * @param {{tone: string}} due The due text.\n\t\t * @return {string} `error`, `warning` or `muted`.\n\t\t */\n\t\tdueToneOf(due) {\n\t\t\tif (due.tone === 'danger' || due.tone === 'today') {\n\t\t\t\treturn 'error'\n\t\t\t}\n\t\t\treturn due.tone === 'upcoming' ? 'warning' : 'muted'\n\t\t},\n\n\t\t/**\n\t\t * @param {string} key The row key.\n\t\t * @param {string} value The typed note.\n\t\t * @return {void}\n\t\t */\n\t\tsetDraft(key, value) {\n\t\t\tthis.drafts = { ...this.drafts, [key]: String(value ?? '') }\n\t\t},\n\n\t\t/**\n\t\t * @param {string} key The row key.\n\t\t * @return {boolean} Whether the row has a note typed.\n\t\t */\n\t\thasDraft(key) {\n\t\t\treturn String(this.drafts[key] ?? '').trim() !== ''\n\t\t},\n\n\t\t/**\n\t\t * The props the row's leaf gets: the row's object, register and schema.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object}\n\t\t */\n\t\tleafProps(row) {\n\t\t\tconst leaf = this.leaf || {}\n\t\t\treturn {\n\t\t\t\tobjectId: String(readPath(row, leaf.objectIdField || 'id') ?? ''),\n\t\t\t\tregister: leaf.register || '',\n\t\t\t\tschema: leaf.schemaField ? String(readPath(row, leaf.schemaField) ?? '') : (leaf.schema || ''),\n\t\t\t\tsurface: 'row',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The row's notes endpoint: the row's `urlField`, else `url` with\n\t\t * `{field}` filled from the row.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} The URL, or ''.\n\t\t */\n\t\turlOf(row) {\n\t\t\tif (this.action.urlField) {\n\t\t\t\treturn String(readPath(row, this.action.urlField) ?? '')\n\t\t\t}\n\t\t\tconst template = String(this.action.url || '')\n\t\t\treturn template.replace(/\\{([A-Za-z0-9_.]+)\\}/g, (_, path) => encodeURIComponent(String(readPath(row, path) ?? '')))\n\t\t},\n\n\t\t/**\n\t\t * Record one row's note, then let the page refetch.\n\t\t *\n\t\t * @param {object} item The row item.\n\t\t * @param {boolean} [refresh] Whether to refresh the page after it.\n\t\t * @return {Promise<boolean>} Whether it was recorded.\n\t\t */\n\t\tasync record(item, refresh = true) {\n\t\t\tconst text = String(this.drafts[item.key] ?? '').trim()\n\t\t\tconst url = this.urlOf(item.row)\n\t\t\tif (text === '' || url === '') {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tthis.busy = true\n\t\t\tthis.error = ''\n\t\t\ttry {\n\t\t\t\tconst { default: axios } = await import('@nextcloud/axios')\n\t\t\t\tawait axios.post(prefixUrl(url), { [this.action.bodyField || 'message']: text })\n\t\t\t\tconst { [item.key]: done, ...rest } = this.drafts\n\t\t\t\tthis.drafts = rest\n\t\t\t\tif (refresh) {\n\t\t\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t} catch {\n\t\t\t\tthis.error = this.action.errorText || t('nextcloud-vue', 'The note could not be recorded. It is kept here; try again.')\n\t\t\t\treturn false\n\t\t\t} finally {\n\t\t\t\tthis.busy = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Record every row with a note, one after another, then refresh once.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync recordEvery() {\n\t\t\tconst filled = this.items.filter((item) => this.hasDraft(item.key))\n\t\t\tlet any = false\n\t\t\tfor (const item of filled) {\n\t\t\t\tany = (await this.record(item, false)) || any\n\t\t\t}\n\t\t\tif (any) {\n\t\t\t\temit(PAGE_REFRESH_CHANNEL, {})\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-record-notes {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 0;\n}\n\n.cn-record-notes__ready {\n\tdisplay: flex;\n\talign-items: baseline;\n\tgap: 6px;\n\tmargin: 0 0 8px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-record-notes__ready-figure {\n\tfont-weight: 700;\n\tcolor: var(--color-main-text);\n}\n\n.cn-record-notes__row {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 10px;\n\tpadding: 14px 0;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-notes__head {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-record-notes__title {\n\tfont-weight: 600;\n\tcolor: var(--color-main-text);\n}\n\n.cn-record-notes__title--link {\n\ttext-decoration: underline;\n}\n\n.cn-record-notes__meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-record-notes__spacer {\n\tflex: 1 1 auto;\n}\n\n.cn-record-notes__due {\n\tfont-weight: 600;\n}\n\n.cn-record-notes__due--error {\n\tcolor: var(--color-error-text);\n}\n\n.cn-record-notes__due--warning {\n\tcolor: var(--color-warning-text);\n}\n\n.cn-record-notes__due--muted {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-weight: 400;\n}\n\n.cn-record-notes__fields {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 10px;\n}\n\n.cn-record-notes__field {\n\tflex: 1 1 260px;\n}\n\n.cn-record-notes__leaf {\n\tflex: 0 0 auto;\n}\n\n.cn-record-notes__leaf-missing,\n.cn-record-notes__error {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-record-notes__error {\n\tcolor: var(--color-error-text);\n}\n\n.cn-record-notes__all {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tmargin-top: 8px;\n\tpadding-top: 14px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-notes__all-title {\n\tmargin: 0;\n\tfont-size: 16px;\n}\n\n.cn-record-notes__all-sentence {\n\tmargin: 4px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-record-notes__all-actions {\n\tdisplay: flex;\n\tgap: 8px;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_Fragment","_renderList","_createBlock","_resolveDynamicComponent","_mergeProps","_createTextVNode","_createVNode"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnTabsWidget.vue2.js';
|
|
|
2
2
|
import { render } from './CnTabsWidget.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-5704a09b";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -196,13 +196,16 @@ var script = {
|
|
|
196
196
|
* `layout: [{ widgetId, gridWidth }]` sets a width (of 12) per widget, as
|
|
197
197
|
* the page grid does. Widgets wrap onto the next row.
|
|
198
198
|
*
|
|
199
|
+
* A tab with `default: true` is the one the strip opens on, unless a
|
|
200
|
+
* route hash names another tab or the marked tab sits under "More".
|
|
201
|
+
*
|
|
199
202
|
* `maxVisibleTabs` caps the strip: later tabs go under "More" (in the
|
|
200
203
|
* board look: into the Actions menu, with History kept in the strip).
|
|
201
204
|
* `hideEmpty: true` moves a tab whose count is 0 there too.
|
|
202
205
|
* `moreLabel` names that menu. `variant: "segmented"` draws the strip
|
|
203
206
|
* as a pill switch (CnTabs' segmented variant); `line` is the default.
|
|
204
207
|
*
|
|
205
|
-
* @type {{ tabs?: Array<{widgetId?: string, widgetIds?: string[], label?: string, icon?: string, count?: number, countField?: string, countFrom?: ('widget'|{register: string, schema: string, filter?: object}), overflow?: boolean, columns?: number, layout?: Array<{widgetId: string, gridWidth: number}>}>, ariaLabel?: string, maxVisibleTabs?: number, hideEmpty?: boolean, moreLabel?: string, variant?: ('line'|'segmented') }}
|
|
208
|
+
* @type {{ tabs?: Array<{widgetId?: string, widgetIds?: string[], label?: string, icon?: string, count?: number, countField?: string, countFrom?: ('widget'|{register: string, schema: string, filter?: object}), overflow?: boolean, default?: boolean, columns?: number, layout?: Array<{widgetId: string, gridWidth: number}>}>, ariaLabel?: string, maxVisibleTabs?: number, hideEmpty?: boolean, moreLabel?: string, variant?: ('line'|'segmented') }}
|
|
206
209
|
*/
|
|
207
210
|
content: {
|
|
208
211
|
type: Object,
|
|
@@ -492,6 +495,7 @@ var script = {
|
|
|
492
495
|
count,
|
|
493
496
|
overflow,
|
|
494
497
|
pending: visibility === 'pending',
|
|
498
|
+
opensFirst: Boolean(tab && typeof tab === 'object' && tab.default === true),
|
|
495
499
|
});
|
|
496
500
|
});
|
|
497
501
|
return resolved
|
|
@@ -677,9 +681,9 @@ var script = {
|
|
|
677
681
|
},
|
|
678
682
|
|
|
679
683
|
/**
|
|
680
|
-
* Open on the
|
|
681
|
-
* when tab 0 sits under "More" the strip
|
|
682
|
-
* chose to tuck away.
|
|
684
|
+
* Open on the tab marked `default: true`, else on the first tab that is in
|
|
685
|
+
* the strip. Tab 0 is not enough: when tab 0 sits under "More" the strip
|
|
686
|
+
* would open on a tab the author chose to tuck away.
|
|
683
687
|
*
|
|
684
688
|
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-tab-counts-and-overflow
|
|
685
689
|
*/
|
|
@@ -695,8 +699,9 @@ var script = {
|
|
|
695
699
|
if (named >= 0) {
|
|
696
700
|
this.hashTargetId = wanted;
|
|
697
701
|
}
|
|
702
|
+
const marked = this.resolvedTabs.findIndex((tab) => tab.opensFirst && !tab.overflow && !tab.pending);
|
|
698
703
|
const first = this.resolvedTabs.findIndex((tab) => !tab.overflow && !tab.pending);
|
|
699
|
-
const fallback = first >= 0 ? first : this.resolvedTabs.findIndex((tab) => !tab.overflow);
|
|
704
|
+
const fallback = marked >= 0 ? marked : first >= 0 ? first : this.resolvedTabs.findIndex((tab) => !tab.overflow);
|
|
700
705
|
if (fallback > 0) {
|
|
701
706
|
this.activeIndex = fallback;
|
|
702
707
|
}
|