@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,9 @@
|
|
|
1
|
+
import script from './CnNcDashboardList.vue2.js';
|
|
2
|
+
import { render } from './CnNcDashboardList.vue3.js';
|
|
3
|
+
|
|
4
|
+
script.render = render;
|
|
5
|
+
script.__scopeId = "data-v-3c7adfed";
|
|
6
|
+
const __sfc_main = script;
|
|
7
|
+
|
|
8
|
+
export { __sfc_main as default };
|
|
9
|
+
//# sourceMappingURL=CnNcDashboardList.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnNcDashboardList.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { translate } from '@nextcloud/l10n';
|
|
2
|
+
import { generateUrl } from '@nextcloud/router';
|
|
3
|
+
import { fetchEndpointSource } from '../../composables/useEndpointSource.js';
|
|
4
|
+
import { fillTemplate } from '../../utils/fillTemplate.js';
|
|
5
|
+
import { openRowTarget } from '../../utils/linkNavigation.js';
|
|
6
|
+
import { readPath } from '../../utils/readPath.js';
|
|
7
|
+
import { resolveDeepTokens } from '../../utils/resolveFilterTokens.js';
|
|
8
|
+
import __sfc_main from '../CnDataTable/CnDataTable.vue.js';
|
|
9
|
+
|
|
10
|
+
/** A name and a muted, right-aligned trailing line. */
|
|
11
|
+
const COLUMNS = [
|
|
12
|
+
{ key: 'mainText', cellClass: 'cn-cell--strong' },
|
|
13
|
+
{ key: 'subText', cellClass: 'cn-cell--muted cn-cell--end' },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A url for an anchor: absolute and app-root paths as given, else through `generateUrl`.
|
|
18
|
+
*
|
|
19
|
+
* @param {string} url The url.
|
|
20
|
+
* @return {string}
|
|
21
|
+
*/
|
|
22
|
+
function appUrl(url) {
|
|
23
|
+
return /^(https?:)?\/\//i.test(url) || url.startsWith('/index.php') ? url : generateUrl(url)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* CnNcDashboardList — the compact list a Nextcloud Dashboard widget shows:
|
|
28
|
+
* a bold name, a muted trailing line, and a "View all" link, read from
|
|
29
|
+
* OpenRegister or any endpoint. Each row opens its record in place (a
|
|
30
|
+
* modifier or middle click opens a new tab), because the Nextcloud Dashboard
|
|
31
|
+
* has no app router. `registerNcDashboardWidgets` mounts it per widget id, so
|
|
32
|
+
* an app's dashboard widgets are configuration rather than one component each.
|
|
33
|
+
*
|
|
34
|
+
* `config`:
|
|
35
|
+
* - `source`: `{ register, schema, params? }` (OpenRegister objects, rows in
|
|
36
|
+
* `results`) or an endpointSource `{ url, params?, responsePath? }`. Filter
|
|
37
|
+
* tokens (`@me`, `@today`, `@today-7d`) resolve in `params`, and an operator
|
|
38
|
+
* map (`{ deadline: { lt: '@today' } }`) goes out as `deadline[lt]=…`.
|
|
39
|
+
* - `itemsPath`: the rows' path in the response, when not `results`.
|
|
40
|
+
* - `limit`: the most rows shown (default 7).
|
|
41
|
+
* - `title`, `subtitle`: templates filled from a row (default `{title}`, none).
|
|
42
|
+
* - `href`: a template for a row's page (`/apps/dossiq/cases/{id}`).
|
|
43
|
+
* - `viewAll`: `{ href, label? }`.
|
|
44
|
+
* - `emptyText`: text for an empty list.
|
|
45
|
+
*
|
|
46
|
+
* Texts go through the host translate.
|
|
47
|
+
*/
|
|
48
|
+
var script = {
|
|
49
|
+
name: 'CnNcDashboardList',
|
|
50
|
+
|
|
51
|
+
components: { CnDataTable: __sfc_main },
|
|
52
|
+
|
|
53
|
+
inject: {
|
|
54
|
+
/** The host app's translate (provided by `registerNcDashboardWidgets`). */
|
|
55
|
+
cnTranslate: { default: () => (key) => key },
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
props: {
|
|
59
|
+
/**
|
|
60
|
+
* The list's configuration (see the component docs).
|
|
61
|
+
*
|
|
62
|
+
* @type {object}
|
|
63
|
+
*/
|
|
64
|
+
config: {
|
|
65
|
+
type: Object,
|
|
66
|
+
default: () => ({}),
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
data() {
|
|
71
|
+
return {
|
|
72
|
+
items: [],
|
|
73
|
+
loading: true,
|
|
74
|
+
error: false,
|
|
75
|
+
columns: COLUMNS,
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
computed: {
|
|
80
|
+
/** @return {number} The most rows shown. */
|
|
81
|
+
limit() {
|
|
82
|
+
return Number(this.config.limit) > 0 ? Number(this.config.limit) : 7
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The rows as the table draws them.
|
|
87
|
+
*
|
|
88
|
+
* @spec openspec/changes/nc-dashboard-widgets/specs/nc-dashboard-widgets/spec.md#requirement-dashboard-widgets-are-configuration
|
|
89
|
+
* @return {Array<{id: string, mainText: string, subText: string, href: string}>}
|
|
90
|
+
*/
|
|
91
|
+
rows() {
|
|
92
|
+
return this.items.slice(0, this.limit).map((item, index) => {
|
|
93
|
+
const id = item.id ?? (item['@self'] && item['@self'].id) ?? index;
|
|
94
|
+
return {
|
|
95
|
+
id: String(id),
|
|
96
|
+
mainText: this.fill(this.config.title || '{title}', item) || String(id),
|
|
97
|
+
subText: this.fill(this.config.subtitle, item),
|
|
98
|
+
href: this.config.href ? appUrl(fillTemplate(this.config.href, { ...item, id })) : '',
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/** @return {string} The "View all" href, or ''. */
|
|
104
|
+
viewAllHref() {
|
|
105
|
+
const viewAll = this.config.viewAll;
|
|
106
|
+
return viewAll && viewAll.href ? appUrl(viewAll.href) : ''
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
mounted() {
|
|
111
|
+
this.load();
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
methods: {
|
|
115
|
+
t: translate,
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* A config text through the host translate.
|
|
119
|
+
*
|
|
120
|
+
* @param {unknown} value The text.
|
|
121
|
+
* @return {string}
|
|
122
|
+
*/
|
|
123
|
+
text(value) {
|
|
124
|
+
return typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* A template filled from a row, translated first.
|
|
129
|
+
*
|
|
130
|
+
* @param {unknown} template The template.
|
|
131
|
+
* @param {object} item The row.
|
|
132
|
+
* @return {string}
|
|
133
|
+
*/
|
|
134
|
+
fill(template, item) {
|
|
135
|
+
return typeof template === 'string' && template !== '' ? fillTemplate(this.cnTranslate(template), item).trim() : ''
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Read the rows: OpenRegister objects or an endpoint.
|
|
140
|
+
*
|
|
141
|
+
* @spec openspec/changes/nc-dashboard-widgets/specs/nc-dashboard-widgets/spec.md#requirement-dashboard-widgets-are-configuration
|
|
142
|
+
* @return {Promise<void>}
|
|
143
|
+
*/
|
|
144
|
+
async load() {
|
|
145
|
+
const source = this.config.source || {};
|
|
146
|
+
this.loading = true;
|
|
147
|
+
this.error = false;
|
|
148
|
+
try {
|
|
149
|
+
// Tokens nested below an operator map (`'@self': { lastUserActivity: { lt: '@today-7d' } }`)
|
|
150
|
+
// resolve here; the endpoint path resolves one level only.
|
|
151
|
+
const params = resolveDeepTokens(source.params || {}, {});
|
|
152
|
+
const request = source.register && source.schema
|
|
153
|
+
? { url: `/apps/openregister/api/objects/${source.register}/${source.schema}`, params: { _limit: this.limit, ...params } }
|
|
154
|
+
: { ...source, params };
|
|
155
|
+
const data = await fetchEndpointSource(request, {}, { fresh: true });
|
|
156
|
+
const list = readPath(data, this.config.itemsPath || (source.register ? 'results' : '')) ?? data;
|
|
157
|
+
this.items = Array.isArray(list) ? list.filter((item) => item && typeof item === 'object') : [];
|
|
158
|
+
} catch {
|
|
159
|
+
this.error = true;
|
|
160
|
+
this.items = [];
|
|
161
|
+
} finally {
|
|
162
|
+
this.loading = false;
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Open a row's page.
|
|
168
|
+
*
|
|
169
|
+
* @param {object} row The row.
|
|
170
|
+
* @param {MouseEvent} event The click.
|
|
171
|
+
*/
|
|
172
|
+
onRowClick(row, event) {
|
|
173
|
+
if (row && row.href) {
|
|
174
|
+
openRowTarget(event, row.href);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export { script as default };
|
|
181
|
+
//# sourceMappingURL=CnNcDashboardList.vue2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnNcDashboardList.vue2.js","sources":["../../../../src/components/CnNcDashboardList/CnNcDashboardList.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-nc-dashboard-list\" data-testid=\"cn-nc-dashboard-list\">\n\t\t<p v-if=\"error\"\n\t\t\tclass=\"cn-nc-dashboard-list__error\"\n\t\t\trole=\"status\"\n\t\t\tdata-testid=\"cn-nc-dashboard-list-error\">\n\t\t\t{{ t('nextcloud-vue', 'This list could not be read.') }}\n\t\t</p>\n\t\t<CnDataTable v-else\n\t\t\t:rows=\"rows\"\n\t\t\t:columns=\"columns\"\n\t\t\t:loading=\"loading\"\n\t\t\thideHeader\n\t\t\tborderless\n\t\t\t:emptyText=\"text(config.emptyText) || t('nextcloud-vue', 'Nothing to show')\"\n\t\t\t@rowClick=\"onRowClick\"\n\t\t\t@rowAuxClick=\"onRowClick\">\n\t\t\t<template v-if=\"viewAllHref\" #footer>\n\t\t\t\t<a class=\"cn-data-table__view-all\" :href=\"viewAllHref\" data-testid=\"cn-nc-dashboard-list-view-all\">\n\t\t\t\t\t{{ text(config.viewAll.label) || t('nextcloud-vue', 'View all') }} →\n\t\t\t\t</a>\n\t\t\t</template>\n\t\t</CnDataTable>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { fetchEndpointSource } from '../../composables/useEndpointSource.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { openRowTarget } from '../../utils/linkNavigation.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { resolveDeepTokens } from '../../utils/resolveFilterTokens.js'\nimport { CnDataTable } from '../CnDataTable/index.js'\n\n/** A name and a muted, right-aligned trailing line. */\nconst COLUMNS = [\n\t{ key: 'mainText', cellClass: 'cn-cell--strong' },\n\t{ key: 'subText', cellClass: 'cn-cell--muted cn-cell--end' },\n]\n\n/**\n * A url for an anchor: absolute and app-root paths as given, else through `generateUrl`.\n *\n * @param {string} url The url.\n * @return {string}\n */\nfunction appUrl(url) {\n\treturn /^(https?:)?\\/\\//i.test(url) || url.startsWith('/index.php') ? url : generateUrl(url)\n}\n\n/**\n * CnNcDashboardList — the compact list a Nextcloud Dashboard widget shows:\n * a bold name, a muted trailing line, and a \"View all\" link, read from\n * OpenRegister or any endpoint. Each row opens its record in place (a\n * modifier or middle click opens a new tab), because the Nextcloud Dashboard\n * has no app router. `registerNcDashboardWidgets` mounts it per widget id, so\n * an app's dashboard widgets are configuration rather than one component each.\n *\n * `config`:\n * - `source`: `{ register, schema, params? }` (OpenRegister objects, rows in\n * `results`) or an endpointSource `{ url, params?, responsePath? }`. Filter\n * tokens (`@me`, `@today`, `@today-7d`) resolve in `params`, and an operator\n * map (`{ deadline: { lt: '@today' } }`) goes out as `deadline[lt]=…`.\n * - `itemsPath`: the rows' path in the response, when not `results`.\n * - `limit`: the most rows shown (default 7).\n * - `title`, `subtitle`: templates filled from a row (default `{title}`, none).\n * - `href`: a template for a row's page (`/apps/dossiq/cases/{id}`).\n * - `viewAll`: `{ href, label? }`.\n * - `emptyText`: text for an empty list.\n *\n * Texts go through the host translate.\n */\nexport default {\n\tname: 'CnNcDashboardList',\n\n\tcomponents: { CnDataTable },\n\n\tinject: {\n\t\t/** The host app's translate (provided by `registerNcDashboardWidgets`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The list's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tconfig: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\titems: [],\n\t\t\tloading: true,\n\t\t\terror: false,\n\t\t\tcolumns: COLUMNS,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {number} The most rows shown. */\n\t\tlimit() {\n\t\t\treturn Number(this.config.limit) > 0 ? Number(this.config.limit) : 7\n\t\t},\n\n\t\t/**\n\t\t * The rows as the table draws them.\n\t\t *\n\t\t * @spec openspec/changes/nc-dashboard-widgets/specs/nc-dashboard-widgets/spec.md#requirement-dashboard-widgets-are-configuration\n\t\t * @return {Array<{id: string, mainText: string, subText: string, href: string}>}\n\t\t */\n\t\trows() {\n\t\t\treturn this.items.slice(0, this.limit).map((item, index) => {\n\t\t\t\tconst id = item.id ?? (item['@self'] && item['@self'].id) ?? index\n\t\t\t\treturn {\n\t\t\t\t\tid: String(id),\n\t\t\t\t\tmainText: this.fill(this.config.title || '{title}', item) || String(id),\n\t\t\t\t\tsubText: this.fill(this.config.subtitle, item),\n\t\t\t\t\thref: this.config.href ? appUrl(fillTemplate(this.config.href, { ...item, id })) : '',\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {string} The \"View all\" href, or ''. */\n\t\tviewAllHref() {\n\t\t\tconst viewAll = this.config.viewAll\n\t\t\treturn viewAll && viewAll.href ? appUrl(viewAll.href) : ''\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * A template filled from a row, translated first.\n\t\t *\n\t\t * @param {unknown} template The template.\n\t\t * @param {object} item The row.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template, item) {\n\t\t\treturn typeof template === 'string' && template !== '' ? fillTemplate(this.cnTranslate(template), item).trim() : ''\n\t\t},\n\n\t\t/**\n\t\t * Read the rows: OpenRegister objects or an endpoint.\n\t\t *\n\t\t * @spec openspec/changes/nc-dashboard-widgets/specs/nc-dashboard-widgets/spec.md#requirement-dashboard-widgets-are-configuration\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tconst source = this.config.source || {}\n\t\t\tthis.loading = true\n\t\t\tthis.error = false\n\t\t\ttry {\n\t\t\t\t// Tokens nested below an operator map (`'@self': { lastUserActivity: { lt: '@today-7d' } }`)\n\t\t\t\t// resolve here; the endpoint path resolves one level only.\n\t\t\t\tconst params = resolveDeepTokens(source.params || {}, {})\n\t\t\t\tconst request = source.register && source.schema\n\t\t\t\t\t? { url: `/apps/openregister/api/objects/${source.register}/${source.schema}`, params: { _limit: this.limit, ...params } }\n\t\t\t\t\t: { ...source, params }\n\t\t\t\tconst data = await fetchEndpointSource(request, {}, { fresh: true })\n\t\t\t\tconst list = readPath(data, this.config.itemsPath || (source.register ? 'results' : '')) ?? data\n\t\t\t\tthis.items = Array.isArray(list) ? list.filter((item) => item && typeof item === 'object') : []\n\t\t\t} catch {\n\t\t\t\tthis.error = true\n\t\t\t\tthis.items = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open a row's page.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @param {MouseEvent} event The click.\n\t\t */\n\t\tonRowClick(row, event) {\n\t\t\tif (row && row.href) {\n\t\t\t\topenRowTarget(event, row.href)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-nc-dashboard-list__error {\n\tmargin: 0;\n\tpadding: 8px 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["CnDataTable","t"],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, toDisplayString, createBlock, createSlots, withCtx, createElementVNode } from 'vue';
|
|
2
|
+
|
|
3
|
+
const _hoisted_1 = {
|
|
4
|
+
class: "cn-nc-dashboard-list",
|
|
5
|
+
"data-testid": "cn-nc-dashboard-list"
|
|
6
|
+
};
|
|
7
|
+
const _hoisted_2 = {
|
|
8
|
+
key: 0,
|
|
9
|
+
class: "cn-nc-dashboard-list__error",
|
|
10
|
+
role: "status",
|
|
11
|
+
"data-testid": "cn-nc-dashboard-list-error"
|
|
12
|
+
};
|
|
13
|
+
const _hoisted_3 = ["href"];
|
|
14
|
+
|
|
15
|
+
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
16
|
+
const _component_CnDataTable = resolveComponent("CnDataTable");
|
|
17
|
+
|
|
18
|
+
return (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
19
|
+
($data.error)
|
|
20
|
+
? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($options.t('nextcloud-vue', 'This list could not be read.')), 1 /* TEXT */))
|
|
21
|
+
: (openBlock(), createBlock(_component_CnDataTable, {
|
|
22
|
+
key: 1,
|
|
23
|
+
rows: $options.rows,
|
|
24
|
+
columns: $data.columns,
|
|
25
|
+
loading: $data.loading,
|
|
26
|
+
hideHeader: "",
|
|
27
|
+
borderless: "",
|
|
28
|
+
emptyText: $options.text($props.config.emptyText) || $options.t('nextcloud-vue', 'Nothing to show'),
|
|
29
|
+
onRowClick: $options.onRowClick,
|
|
30
|
+
onRowAuxClick: $options.onRowClick
|
|
31
|
+
}, createSlots({ _: 2 /* DYNAMIC */ }, [
|
|
32
|
+
($options.viewAllHref)
|
|
33
|
+
? {
|
|
34
|
+
name: "footer",
|
|
35
|
+
fn: withCtx(() => [
|
|
36
|
+
createElementVNode("a", {
|
|
37
|
+
class: "cn-data-table__view-all",
|
|
38
|
+
href: $options.viewAllHref,
|
|
39
|
+
"data-testid": "cn-nc-dashboard-list-view-all"
|
|
40
|
+
}, toDisplayString($options.text($props.config.viewAll.label) || $options.t('nextcloud-vue', 'View all')) + " → ", 9 /* TEXT, PROPS */, _hoisted_3)
|
|
41
|
+
]),
|
|
42
|
+
key: "0"
|
|
43
|
+
}
|
|
44
|
+
: undefined
|
|
45
|
+
]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["rows", "columns", "loading", "emptyText", "onRowClick", "onRowAuxClick"]))
|
|
46
|
+
]))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { render };
|
|
50
|
+
//# sourceMappingURL=CnNcDashboardList.vue3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnNcDashboardList.vue3.js","sources":["../../../../src/components/CnNcDashboardList/CnNcDashboardList.vue?vue&type=template&id=3c7adfed&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-nc-dashboard-list\" data-testid=\"cn-nc-dashboard-list\">\n\t\t<p v-if=\"error\"\n\t\t\tclass=\"cn-nc-dashboard-list__error\"\n\t\t\trole=\"status\"\n\t\t\tdata-testid=\"cn-nc-dashboard-list-error\">\n\t\t\t{{ t('nextcloud-vue', 'This list could not be read.') }}\n\t\t</p>\n\t\t<CnDataTable v-else\n\t\t\t:rows=\"rows\"\n\t\t\t:columns=\"columns\"\n\t\t\t:loading=\"loading\"\n\t\t\thideHeader\n\t\t\tborderless\n\t\t\t:emptyText=\"text(config.emptyText) || t('nextcloud-vue', 'Nothing to show')\"\n\t\t\t@rowClick=\"onRowClick\"\n\t\t\t@rowAuxClick=\"onRowClick\">\n\t\t\t<template v-if=\"viewAllHref\" #footer>\n\t\t\t\t<a class=\"cn-data-table__view-all\" :href=\"viewAllHref\" data-testid=\"cn-nc-dashboard-list-view-all\">\n\t\t\t\t\t{{ text(config.viewAll.label) || t('nextcloud-vue', 'View all') }} →\n\t\t\t\t</a>\n\t\t\t</template>\n\t\t</CnDataTable>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { fetchEndpointSource } from '../../composables/useEndpointSource.js'\nimport { fillTemplate } from '../../utils/fillTemplate.js'\nimport { openRowTarget } from '../../utils/linkNavigation.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { resolveDeepTokens } from '../../utils/resolveFilterTokens.js'\nimport { CnDataTable } from '../CnDataTable/index.js'\n\n/** A name and a muted, right-aligned trailing line. */\nconst COLUMNS = [\n\t{ key: 'mainText', cellClass: 'cn-cell--strong' },\n\t{ key: 'subText', cellClass: 'cn-cell--muted cn-cell--end' },\n]\n\n/**\n * A url for an anchor: absolute and app-root paths as given, else through `generateUrl`.\n *\n * @param {string} url The url.\n * @return {string}\n */\nfunction appUrl(url) {\n\treturn /^(https?:)?\\/\\//i.test(url) || url.startsWith('/index.php') ? url : generateUrl(url)\n}\n\n/**\n * CnNcDashboardList — the compact list a Nextcloud Dashboard widget shows:\n * a bold name, a muted trailing line, and a \"View all\" link, read from\n * OpenRegister or any endpoint. Each row opens its record in place (a\n * modifier or middle click opens a new tab), because the Nextcloud Dashboard\n * has no app router. `registerNcDashboardWidgets` mounts it per widget id, so\n * an app's dashboard widgets are configuration rather than one component each.\n *\n * `config`:\n * - `source`: `{ register, schema, params? }` (OpenRegister objects, rows in\n * `results`) or an endpointSource `{ url, params?, responsePath? }`. Filter\n * tokens (`@me`, `@today`, `@today-7d`) resolve in `params`, and an operator\n * map (`{ deadline: { lt: '@today' } }`) goes out as `deadline[lt]=…`.\n * - `itemsPath`: the rows' path in the response, when not `results`.\n * - `limit`: the most rows shown (default 7).\n * - `title`, `subtitle`: templates filled from a row (default `{title}`, none).\n * - `href`: a template for a row's page (`/apps/dossiq/cases/{id}`).\n * - `viewAll`: `{ href, label? }`.\n * - `emptyText`: text for an empty list.\n *\n * Texts go through the host translate.\n */\nexport default {\n\tname: 'CnNcDashboardList',\n\n\tcomponents: { CnDataTable },\n\n\tinject: {\n\t\t/** The host app's translate (provided by `registerNcDashboardWidgets`). */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The list's configuration (see the component docs).\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tconfig: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\titems: [],\n\t\t\tloading: true,\n\t\t\terror: false,\n\t\t\tcolumns: COLUMNS,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {number} The most rows shown. */\n\t\tlimit() {\n\t\t\treturn Number(this.config.limit) > 0 ? Number(this.config.limit) : 7\n\t\t},\n\n\t\t/**\n\t\t * The rows as the table draws them.\n\t\t *\n\t\t * @spec openspec/changes/nc-dashboard-widgets/specs/nc-dashboard-widgets/spec.md#requirement-dashboard-widgets-are-configuration\n\t\t * @return {Array<{id: string, mainText: string, subText: string, href: string}>}\n\t\t */\n\t\trows() {\n\t\t\treturn this.items.slice(0, this.limit).map((item, index) => {\n\t\t\t\tconst id = item.id ?? (item['@self'] && item['@self'].id) ?? index\n\t\t\t\treturn {\n\t\t\t\t\tid: String(id),\n\t\t\t\t\tmainText: this.fill(this.config.title || '{title}', item) || String(id),\n\t\t\t\t\tsubText: this.fill(this.config.subtitle, item),\n\t\t\t\t\thref: this.config.href ? appUrl(fillTemplate(this.config.href, { ...item, id })) : '',\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {string} The \"View all\" href, or ''. */\n\t\tviewAllHref() {\n\t\t\tconst viewAll = this.config.viewAll\n\t\t\treturn viewAll && viewAll.href ? appUrl(viewAll.href) : ''\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A config text through the host translate.\n\t\t *\n\t\t * @param {unknown} value The text.\n\t\t * @return {string}\n\t\t */\n\t\ttext(value) {\n\t\t\treturn typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''\n\t\t},\n\n\t\t/**\n\t\t * A template filled from a row, translated first.\n\t\t *\n\t\t * @param {unknown} template The template.\n\t\t * @param {object} item The row.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template, item) {\n\t\t\treturn typeof template === 'string' && template !== '' ? fillTemplate(this.cnTranslate(template), item).trim() : ''\n\t\t},\n\n\t\t/**\n\t\t * Read the rows: OpenRegister objects or an endpoint.\n\t\t *\n\t\t * @spec openspec/changes/nc-dashboard-widgets/specs/nc-dashboard-widgets/spec.md#requirement-dashboard-widgets-are-configuration\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tconst source = this.config.source || {}\n\t\t\tthis.loading = true\n\t\t\tthis.error = false\n\t\t\ttry {\n\t\t\t\t// Tokens nested below an operator map (`'@self': { lastUserActivity: { lt: '@today-7d' } }`)\n\t\t\t\t// resolve here; the endpoint path resolves one level only.\n\t\t\t\tconst params = resolveDeepTokens(source.params || {}, {})\n\t\t\t\tconst request = source.register && source.schema\n\t\t\t\t\t? { url: `/apps/openregister/api/objects/${source.register}/${source.schema}`, params: { _limit: this.limit, ...params } }\n\t\t\t\t\t: { ...source, params }\n\t\t\t\tconst data = await fetchEndpointSource(request, {}, { fresh: true })\n\t\t\t\tconst list = readPath(data, this.config.itemsPath || (source.register ? 'results' : '')) ?? data\n\t\t\t\tthis.items = Array.isArray(list) ? list.filter((item) => item && typeof item === 'object') : []\n\t\t\t} catch {\n\t\t\t\tthis.error = true\n\t\t\t\tthis.items = []\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open a row's page.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @param {MouseEvent} event The click.\n\t\t */\n\t\tonRowClick(row, event) {\n\t\t\tif (row && row.href) {\n\t\t\t\topenRowTarget(event, row.href)\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-nc-dashboard-list__error {\n\tmargin: 0;\n\tpadding: 8px 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_createBlock","_createElementVNode"],"mappings":";;;AASM,EAAA,KAAK,EAAC,sBAAsB;AAAC,EAAA,aAAQ,EAAA,sBAAA;;;;AAExC,EAAA,KAAE,EAAA,6BAAA;AACF,EAAA,IAAI,EAAC,QAAQ;AACb,EAAA,aAAW,EAAA,4BAAA;;;;;;;AAJX,EAAA,QAAAA,SAAA,EAAA,EAAAC,kBAAA,QAAA;KACQ,KAAK,CAAA,KAAA;wCAKV,CAAA,GAAA,YAAA,EAAAC,eAAA,CADA,QAAC,CAAA,CAAA,CAAA,eAAA,EAAA,8BAAA,CAAA,CAAA,EAAA,CAAA,YAAA;sBAEJC;;AACC,UAAA,IAAI,EAAE,QAAE,CAAA,IAAA;AACR,UAAA,OAAO,EAAE,KAAO,CAAA,OAAA;AAChB,UAAA,OAAO,EAAE,KAAO,CAAA,OAAA;AACjB,UAAA,UAAU,EAAV,EAAU;AACV,UAAA,UAAU,EAAV,EAAU;AACT,UAAA,SAAS,EAAE,QAAI,CAAA,IAAA,CAAC,aAAM,CAAC,SAAS,KAAK,QAAC,CAAA,CAAA,CAAA,eAAA,EAAA,iBAAA,CAAA;oBAC9B,EAAE,QAAU,CAAA,UAAA;AACpB,UAAA,aAAG,EAAA,QAAA,CAAA,UAAA;;WACM,QAAA,CAAA,WAAA;;sBAAA,QAAA;4BACT;kBAAAC;AAAG,oBAAA,KAAK,EAAC,yBAAE;AAAA,oBAAA,IAAA,EAAA,QAAA,CAAA,WAAA;AAAA,oBAAA,aAAA,EAAA,+BAAA;qCACT,QAAA,CAAA,IAAA,CAAA,aAAA,CAAA,OAAA,CAAA,KAAA,CAAA,IAAA,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,UAAA,CAAA,CAAA,GAAA;;;;;;;;;;;"}
|
|
@@ -2,7 +2,7 @@ import script from './CnObjectListWidget.vue2.js';
|
|
|
2
2
|
import { render } from './CnObjectListWidget.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-78ec6d64";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -14,6 +14,7 @@ import { objectFieldValue, objectDisplayName } from '../../utils/objectName.js';
|
|
|
14
14
|
import { rowIdOf, renumberRows, moveIndex } from '../../utils/reorderRows.js';
|
|
15
15
|
import { dropOptionalUnresolved, resolveFilterTokens, hasUnresolvedTokens } from '../../utils/resolveFilterTokens.js';
|
|
16
16
|
import { markNewTabHandled } from '../../utils/rowAuxClick.js';
|
|
17
|
+
import { changedKeys } from '../../utils/saveEdited.js';
|
|
17
18
|
import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js';
|
|
18
19
|
import __sfc_main$4 from '../CnRowActions/CnRowActions.vue.js';
|
|
19
20
|
|
|
@@ -152,7 +153,7 @@ var script = {
|
|
|
152
153
|
},
|
|
153
154
|
},
|
|
154
155
|
|
|
155
|
-
emits: ['created', 'row-click', 'row-aux-click', 'view-all', 'files-dropped'],
|
|
156
|
+
emits: ['created', 'updated', 'row-click', 'row-aux-click', 'view-all', 'files-dropped'],
|
|
156
157
|
|
|
157
158
|
data() {
|
|
158
159
|
return {
|
|
@@ -178,6 +179,8 @@ var script = {
|
|
|
178
179
|
fitStage: 0,
|
|
179
180
|
/** Whether the create dialog is open. */
|
|
180
181
|
showCreate: false,
|
|
182
|
+
/** The row the form edits (`content.editable`), or null while it creates. */
|
|
183
|
+
editingRow: null,
|
|
181
184
|
/**
|
|
182
185
|
* The list's own schema definition, once fetched.
|
|
183
186
|
*
|
|
@@ -426,6 +429,12 @@ var script = {
|
|
|
426
429
|
...this.rowActionLink(action),
|
|
427
430
|
handler: (row) => this.runRowAction(action, row),
|
|
428
431
|
}));
|
|
432
|
+
if (this.content.editable === true) {
|
|
433
|
+
mapped.unshift({ label: translate('nextcloud-vue', 'Edit'), icon: 'Pencil', handler: (row) => this.openEdit(row) });
|
|
434
|
+
}
|
|
435
|
+
if (this.content.deletable === true) {
|
|
436
|
+
mapped.push({ label: translate('nextcloud-vue', 'Delete'), icon: 'TrashCanOutline', destructive: true, handler: (row) => this.deleteRow(row) });
|
|
437
|
+
}
|
|
429
438
|
if (this.reorderField === '') {
|
|
430
439
|
return mapped
|
|
431
440
|
}
|
|
@@ -1240,6 +1249,7 @@ var script = {
|
|
|
1240
1249
|
forgetCreateSchema() {
|
|
1241
1250
|
this.createSchema = null;
|
|
1242
1251
|
this.showCreate = false;
|
|
1252
|
+
this.editingRow = null;
|
|
1243
1253
|
},
|
|
1244
1254
|
|
|
1245
1255
|
/**
|
|
@@ -1250,6 +1260,97 @@ var script = {
|
|
|
1250
1260
|
*
|
|
1251
1261
|
* @return {Promise<void>}
|
|
1252
1262
|
*/
|
|
1263
|
+
/**
|
|
1264
|
+
* Edit a row in the list's own form (`content.editable`): the form the
|
|
1265
|
+
* Add button opens, filled with the row.
|
|
1266
|
+
*
|
|
1267
|
+
* @param {object} row The row.
|
|
1268
|
+
* @return {Promise<void>}
|
|
1269
|
+
* @spec openspec/changes/object-list-edit-in-place/specs/object-list-widget/spec.md#requirement-a-list-can-edit-a-row-in-its-own-form
|
|
1270
|
+
*/
|
|
1271
|
+
async openEdit(row) {
|
|
1272
|
+
try {
|
|
1273
|
+
await this.ensureSchema();
|
|
1274
|
+
this.showCreate = false;
|
|
1275
|
+
this.editingRow = row ? { ...row } : null;
|
|
1276
|
+
} catch (e) {
|
|
1277
|
+
this.error = (e && e.message) || 'error';
|
|
1278
|
+
}
|
|
1279
|
+
},
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* Delete a row of the list's own schema (`content.deletable`). The
|
|
1283
|
+
* toast offers Undo; a refusal toasts the server's message.
|
|
1284
|
+
*
|
|
1285
|
+
* @param {object} row The row.
|
|
1286
|
+
* @return {Promise<void>}
|
|
1287
|
+
* @spec openspec/changes/object-list-edit-in-place/specs/object-list-widget/spec.md#requirement-a-list-can-edit-a-row-in-its-own-form
|
|
1288
|
+
*/
|
|
1289
|
+
async deleteRow(row) {
|
|
1290
|
+
const c = this.content || {};
|
|
1291
|
+
const id = rowIdOf(row);
|
|
1292
|
+
if (id === '') {
|
|
1293
|
+
return
|
|
1294
|
+
}
|
|
1295
|
+
const pending = (async () => {
|
|
1296
|
+
const [{ default: axios }, { generateUrl }] = await Promise.all([import('@nextcloud/axios'), import('@nextcloud/router')]);
|
|
1297
|
+
try {
|
|
1298
|
+
await axios.delete(generateUrl('/apps/openregister/api/objects/{register}/{schema}/{id}', { register: c.register, schema: c.schema, id }));
|
|
1299
|
+
} catch (e) {
|
|
1300
|
+
const data = e && e.response && e.response.data;
|
|
1301
|
+
const refusal = data && (data.message || data.error);
|
|
1302
|
+
throw new Error((typeof refusal === 'string' && refusal) || (e && e.message) || translate('nextcloud-vue', 'The delete failed'), { cause: e })
|
|
1303
|
+
}
|
|
1304
|
+
return true
|
|
1305
|
+
})();
|
|
1306
|
+
await this.reportDelete(row, pending);
|
|
1307
|
+
await pending.catch(() => {});
|
|
1308
|
+
this.fetchRows();
|
|
1309
|
+
},
|
|
1310
|
+
|
|
1311
|
+
/** Close the form, whether it created or edited. */
|
|
1312
|
+
closeForm() {
|
|
1313
|
+
this.showCreate = false;
|
|
1314
|
+
this.editingRow = null;
|
|
1315
|
+
},
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* Save an edited row: a PATCH of the keys the form changed, so every
|
|
1319
|
+
* key it does not show keeps its stored value; nothing when nothing
|
|
1320
|
+
* changed. A refusal shows the server's message in the dialog.
|
|
1321
|
+
*
|
|
1322
|
+
* @param {object} formData The edited values.
|
|
1323
|
+
* @return {Promise<void>}
|
|
1324
|
+
* @spec openspec/changes/object-list-edit-in-place/specs/object-list-widget/spec.md#requirement-a-list-can-edit-a-row-in-its-own-form
|
|
1325
|
+
*/
|
|
1326
|
+
async onEditConfirm(formData) {
|
|
1327
|
+
const c = this.content || {};
|
|
1328
|
+
const row = this.editingRow;
|
|
1329
|
+
const id = rowIdOf(row);
|
|
1330
|
+
const changes = changedKeys(row, formData);
|
|
1331
|
+
try {
|
|
1332
|
+
if (id !== '' && Object.keys(changes).length > 0) {
|
|
1333
|
+
const [{ default: axios }, { generateUrl }] = await Promise.all([import('@nextcloud/axios'), import('@nextcloud/router')]);
|
|
1334
|
+
await axios.patch(generateUrl('/apps/openregister/api/objects/{register}/{schema}/{id}', { register: c.register, schema: c.schema, id }), changes);
|
|
1335
|
+
}
|
|
1336
|
+
if (this.$refs.createDialog) {
|
|
1337
|
+
this.$refs.createDialog.setResult({ success: true });
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* @event updated Emitted after a row was edited, with the keys that changed.
|
|
1341
|
+
* @type {object}
|
|
1342
|
+
*/
|
|
1343
|
+
this.$emit('updated', { id, changes });
|
|
1344
|
+
this.fetchRows();
|
|
1345
|
+
} catch (e) {
|
|
1346
|
+
const data = e && e.response && e.response.data;
|
|
1347
|
+
const refusal = data && (data.message || data.error);
|
|
1348
|
+
if (this.$refs.createDialog) {
|
|
1349
|
+
this.$refs.createDialog.setResult({ error: (typeof refusal === 'string' && refusal) || (e && e.message) || 'error' });
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
},
|
|
1353
|
+
|
|
1253
1354
|
async openCreate() {
|
|
1254
1355
|
const c = this.content || {};
|
|
1255
1356
|
if (!c.schema) {
|