@conduction/nextcloud-vue 0.1.0-beta.11 → 0.1.0-beta.12
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/nextcloud-vue.cjs +67614 -0
- package/dist/nextcloud-vue.cjs.js +13518 -13617
- package/dist/nextcloud-vue.cjs.js.map +1 -1
- package/dist/nextcloud-vue.cjs.map +1 -0
- package/dist/nextcloud-vue.css +1796 -1800
- package/dist/nextcloud-vue.esm.js +13518 -13617
- package/dist/nextcloud-vue.esm.js.map +1 -1
- package/package.json +3 -2
- package/src/components/CnActionsBar/CnActionsBar.vue +254 -254
- package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +570 -570
- package/src/components/CnAdvancedFormDialog/CnDataTab.vue +217 -217
- package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +121 -121
- package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +422 -422
- package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +247 -247
- package/src/components/CnCard/CnCard.vue +415 -415
- package/src/components/CnCardGrid/CnCardGrid.vue +156 -156
- package/src/components/CnCellRenderer/CnCellRenderer.vue +132 -132
- package/src/components/CnChartWidget/CnChartWidget.vue +346 -346
- package/src/components/CnConfigurationCard/CnConfigurationCard.vue +77 -77
- package/src/components/CnContextMenu/CnContextMenu.vue +142 -142
- package/src/components/CnCopyDialog/CnCopyDialog.vue +266 -266
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +229 -229
- package/src/components/CnDashboardPage/CnDashboardPage.vue +397 -397
- package/src/components/CnDataTable/CnDataTable.vue +362 -362
- package/src/components/CnDeleteDialog/CnDeleteDialog.vue +177 -177
- package/src/components/CnDetailCard/CnDetailCard.vue +225 -225
- package/src/components/CnDetailGrid/CnDetailGrid.vue +256 -256
- package/src/components/CnDetailPage/CnDetailPage.vue +432 -432
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +234 -234
- package/src/components/CnFilterBar/CnFilterBar.vue +153 -153
- package/src/components/CnFormDialog/CnFormDialog.vue +1047 -1047
- package/src/components/CnIcon/CnIcon.vue +89 -89
- package/src/components/CnIndexPage/CnIndexPage.vue +981 -980
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +536 -536
- package/src/components/CnInfoWidget/CnInfoWidget.vue +219 -219
- package/src/components/CnItemCard/CnItemCard.vue +134 -134
- package/src/components/CnJsonViewer/CnJsonViewer.vue +312 -312
- package/src/components/CnKpiGrid/CnKpiGrid.vue +93 -93
- package/src/components/CnMassActionBar/CnMassActionBar.vue +161 -161
- package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +327 -327
- package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +245 -245
- package/src/components/CnMassExportDialog/CnMassExportDialog.vue +191 -191
- package/src/components/CnMassImportDialog/CnMassImportDialog.vue +494 -494
- package/src/components/CnNoteCard/CnNoteCard.vue +149 -149
- package/src/components/CnNotesCard/CnNotesCard.vue +416 -416
- package/src/components/CnObjectCard/CnObjectCard.vue +294 -294
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +854 -854
- package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +289 -289
- package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +369 -369
- package/src/components/CnObjectSidebar/CnFilesTab.vue +287 -287
- package/src/components/CnObjectSidebar/CnNotesTab.vue +250 -250
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +255 -255
- package/src/components/CnObjectSidebar/CnTagsTab.vue +259 -259
- package/src/components/CnObjectSidebar/CnTasksTab.vue +483 -483
- package/src/components/CnPageHeader/CnPageHeader.vue +61 -61
- package/src/components/CnPagination/CnPagination.vue +253 -253
- package/src/components/CnProgressBar/CnProgressBar.vue +262 -262
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +793 -793
- package/src/components/CnRowActions/CnRowActions.vue +95 -95
- package/src/components/CnSchemaFormDialog/CnSchemaConfigurationTab.vue +226 -226
- package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +788 -788
- package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +305 -305
- package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +1398 -1398
- package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +236 -236
- package/src/components/CnSettingsCard/CnSettingsCard.vue +92 -92
- package/src/components/CnSettingsSection/CnSettingsSection.vue +267 -267
- package/src/components/CnStatsBlock/CnStatsBlock.vue +437 -437
- package/src/components/CnStatsPanel/CnStatsPanel.vue +321 -321
- package/src/components/CnStatusBadge/CnStatusBadge.vue +90 -90
- package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +545 -545
- package/src/components/CnTableWidget/CnTableWidget.vue +333 -333
- package/src/components/CnTasksCard/CnTasksCard.vue +374 -374
- package/src/components/CnTileWidget/CnTileWidget.vue +159 -159
- package/src/components/CnTimelineStages/CnTimelineStages.vue +294 -294
- package/src/components/CnUserActionMenu/CnUserActionMenu.vue +436 -436
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +313 -313
- package/src/components/CnWidgetRenderer/CnWidgetRenderer.vue +180 -180
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +248 -248
|
@@ -1,333 +1,333 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
CnTableWidget — Data table widget with card wrapper and dual data sourcing.
|
|
3
|
-
|
|
4
|
-
Wraps CnDataTable in a card container with a title header, optional "View all"
|
|
5
|
-
footer link, and loading/empty states. Supports two data modes:
|
|
6
|
-
1. External: `rows` prop provided (no API calls)
|
|
7
|
-
2. Self-fetch: `register` + `schemaId` provided (fetches from OpenRegister API)
|
|
8
|
-
|
|
9
|
-
Used in dashboard and detail page grid layouts for displaying related data tables.
|
|
10
|
-
-->
|
|
11
|
-
<template>
|
|
12
|
-
<div class="cn-table-widget">
|
|
13
|
-
<!-- Header -->
|
|
14
|
-
<div v-if="title" class="cn-table-widget__header">
|
|
15
|
-
<h3 class="cn-table-widget__title">
|
|
16
|
-
{{ title }}
|
|
17
|
-
</h3>
|
|
18
|
-
<span v-if="totalCount > 0" class="cn-table-widget__count">
|
|
19
|
-
{{ totalCount }}
|
|
20
|
-
</span>
|
|
21
|
-
</div>
|
|
22
|
-
|
|
23
|
-
<!-- Loading state -->
|
|
24
|
-
<NcLoadingIcon v-if="isLoading" class="cn-table-widget__loading" :size="32" />
|
|
25
|
-
|
|
26
|
-
<!-- Empty state -->
|
|
27
|
-
<p v-else-if="displayRows.length === 0" class="cn-table-widget__empty">
|
|
28
|
-
{{ emptyText }}
|
|
29
|
-
</p>
|
|
30
|
-
|
|
31
|
-
<!-- Data table -->
|
|
32
|
-
<CnDataTable
|
|
33
|
-
v-else
|
|
34
|
-
:rows="displayRows"
|
|
35
|
-
:columns="columns"
|
|
36
|
-
:loading="false"
|
|
37
|
-
:selectable="false"
|
|
38
|
-
@row-click="onRowClick" />
|
|
39
|
-
|
|
40
|
-
<!-- Footer with view all link -->
|
|
41
|
-
<div
|
|
42
|
-
v-if="viewAllRoute && totalCount > limitedCount"
|
|
43
|
-
class="cn-table-widget__footer">
|
|
44
|
-
<a
|
|
45
|
-
class="cn-table-widget__view-all"
|
|
46
|
-
@click.prevent="$router.push(viewAllRoute)">
|
|
47
|
-
{{ viewAllLabel }}
|
|
48
|
-
</a>
|
|
49
|
-
</div>
|
|
50
|
-
</div>
|
|
51
|
-
</template>
|
|
52
|
-
|
|
53
|
-
<script>
|
|
54
|
-
import { translate as t } from '@nextcloud/l10n'
|
|
55
|
-
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
56
|
-
import CnDataTable from '../CnDataTable/CnDataTable.vue'
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* CnTableWidget — Data table widget with card wrapper and dual data sourcing.
|
|
60
|
-
*
|
|
61
|
-
* @example External data mode
|
|
62
|
-
* <CnTableWidget
|
|
63
|
-
* title="Related Skills"
|
|
64
|
-
* :rows="skillRows"
|
|
65
|
-
* :columns="skillColumns"
|
|
66
|
-
* :view-all-route="{ name: 'Skills' }" />
|
|
67
|
-
*
|
|
68
|
-
* @example Self-fetch mode
|
|
69
|
-
* <CnTableWidget
|
|
70
|
-
* title="Documents"
|
|
71
|
-
* register="9"
|
|
72
|
-
* schema-id="42"
|
|
73
|
-
* :limit="5" />
|
|
74
|
-
*/
|
|
75
|
-
export default {
|
|
76
|
-
name: 'CnTableWidget',
|
|
77
|
-
|
|
78
|
-
components: {
|
|
79
|
-
NcLoadingIcon,
|
|
80
|
-
CnDataTable,
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
props: {
|
|
84
|
-
/** Widget title shown in the header. */
|
|
85
|
-
title: {
|
|
86
|
-
type: String,
|
|
87
|
-
default: '',
|
|
88
|
-
},
|
|
89
|
-
/**
|
|
90
|
-
* External row data. When provided, no API calls are made.
|
|
91
|
-
*
|
|
92
|
-
* @type {object[]}
|
|
93
|
-
*/
|
|
94
|
-
rows: {
|
|
95
|
-
type: Array,
|
|
96
|
-
default: null,
|
|
97
|
-
},
|
|
98
|
-
/**
|
|
99
|
-
* Column definitions for CnDataTable.
|
|
100
|
-
*
|
|
101
|
-
* @type {{ key: string, label: string, sortable?: boolean }[]}
|
|
102
|
-
*/
|
|
103
|
-
columns: {
|
|
104
|
-
type: Array,
|
|
105
|
-
default: () => [],
|
|
106
|
-
},
|
|
107
|
-
/**
|
|
108
|
-
* OpenRegister register ID for self-fetch mode.
|
|
109
|
-
*
|
|
110
|
-
* @type {string|number}
|
|
111
|
-
*/
|
|
112
|
-
register: {
|
|
113
|
-
type: [String, Number],
|
|
114
|
-
default: null,
|
|
115
|
-
},
|
|
116
|
-
/**
|
|
117
|
-
* OpenRegister schema ID for self-fetch mode.
|
|
118
|
-
*
|
|
119
|
-
* @type {string|number}
|
|
120
|
-
*/
|
|
121
|
-
schemaId: {
|
|
122
|
-
type: [String, Number],
|
|
123
|
-
default: null,
|
|
124
|
-
},
|
|
125
|
-
/**
|
|
126
|
-
* Maximum number of rows to display. When total exceeds this,
|
|
127
|
-
* a "View all" link appears.
|
|
128
|
-
*
|
|
129
|
-
* @type {number}
|
|
130
|
-
*/
|
|
131
|
-
limit: {
|
|
132
|
-
type: Number,
|
|
133
|
-
default: 0,
|
|
134
|
-
},
|
|
135
|
-
/**
|
|
136
|
-
* Vue Router route for the "View all" link.
|
|
137
|
-
*
|
|
138
|
-
* @type {object}
|
|
139
|
-
*/
|
|
140
|
-
viewAllRoute: {
|
|
141
|
-
type: Object,
|
|
142
|
-
default: null,
|
|
143
|
-
},
|
|
144
|
-
/**
|
|
145
|
-
* Function that returns a route object for row click navigation.
|
|
146
|
-
* Receives the row data as argument.
|
|
147
|
-
*
|
|
148
|
-
* @type {Function}
|
|
149
|
-
*/
|
|
150
|
-
rowClickRoute: {
|
|
151
|
-
type: Function,
|
|
152
|
-
default: null,
|
|
153
|
-
},
|
|
154
|
-
/** Pre-translated "View all" label. */
|
|
155
|
-
viewAllLabel: {
|
|
156
|
-
type: String,
|
|
157
|
-
default: () => t('nextcloud-vue', 'View all'),
|
|
158
|
-
},
|
|
159
|
-
/** Pre-translated empty state text. */
|
|
160
|
-
emptyText: {
|
|
161
|
-
type: String,
|
|
162
|
-
default: () => t('nextcloud-vue', 'No data available'),
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
|
|
166
|
-
data() {
|
|
167
|
-
return {
|
|
168
|
-
fetchedRows: [],
|
|
169
|
-
loading: false,
|
|
170
|
-
}
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
computed: {
|
|
174
|
-
/**
|
|
175
|
-
* Whether data is currently loading.
|
|
176
|
-
*
|
|
177
|
-
* @return {boolean}
|
|
178
|
-
*/
|
|
179
|
-
isLoading() {
|
|
180
|
-
return this.loading
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* All rows (external or fetched).
|
|
185
|
-
*
|
|
186
|
-
* @return {object[]}
|
|
187
|
-
*/
|
|
188
|
-
allRows() {
|
|
189
|
-
return this.rows || this.fetchedRows
|
|
190
|
-
},
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Rows limited to the configured limit.
|
|
194
|
-
*
|
|
195
|
-
* @return {object[]}
|
|
196
|
-
*/
|
|
197
|
-
displayRows() {
|
|
198
|
-
if (this.limit > 0) {
|
|
199
|
-
return this.allRows.slice(0, this.limit)
|
|
200
|
-
}
|
|
201
|
-
return this.allRows
|
|
202
|
-
},
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Total count of all rows (before limiting).
|
|
206
|
-
*
|
|
207
|
-
* @return {number}
|
|
208
|
-
*/
|
|
209
|
-
totalCount() {
|
|
210
|
-
return this.allRows.length
|
|
211
|
-
},
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Count of displayed rows (after limiting).
|
|
215
|
-
*
|
|
216
|
-
* @return {number}
|
|
217
|
-
*/
|
|
218
|
-
limitedCount() {
|
|
219
|
-
return this.displayRows.length
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
|
|
223
|
-
mounted() {
|
|
224
|
-
if (this.rows === null && this.register && this.schemaId) {
|
|
225
|
-
this.fetchData()
|
|
226
|
-
}
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
methods: {
|
|
230
|
-
/**
|
|
231
|
-
* Fetch data from the OpenRegister API.
|
|
232
|
-
*
|
|
233
|
-
* @return {Promise<void>}
|
|
234
|
-
*/
|
|
235
|
-
async fetchData() {
|
|
236
|
-
this.loading = true
|
|
237
|
-
try {
|
|
238
|
-
const url = `/index.php/apps/openregister/api/objects/${this.register}/${this.schemaId}`
|
|
239
|
-
const response = await fetch(url, {
|
|
240
|
-
headers: {
|
|
241
|
-
'Content-Type': 'application/json',
|
|
242
|
-
'OCS-APIREQUEST': 'true',
|
|
243
|
-
},
|
|
244
|
-
})
|
|
245
|
-
if (response.ok) {
|
|
246
|
-
const data = await response.json()
|
|
247
|
-
this.fetchedRows = data.results || data || []
|
|
248
|
-
}
|
|
249
|
-
} catch (error) {
|
|
250
|
-
console.error('CnTableWidget: Failed to fetch data', error)
|
|
251
|
-
} finally {
|
|
252
|
-
this.loading = false
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Handle row click events. Navigates if rowClickRoute is configured.
|
|
258
|
-
*
|
|
259
|
-
* @param {object} row - The clicked row data.
|
|
260
|
-
*/
|
|
261
|
-
onRowClick(row) {
|
|
262
|
-
if (this.rowClickRoute) {
|
|
263
|
-
const route = this.rowClickRoute(row)
|
|
264
|
-
if (route) {
|
|
265
|
-
this.$router.push(route)
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
},
|
|
269
|
-
},
|
|
270
|
-
}
|
|
271
|
-
</script>
|
|
272
|
-
|
|
273
|
-
<style scoped>
|
|
274
|
-
.cn-table-widget {
|
|
275
|
-
background: var(--color-main-background);
|
|
276
|
-
border: 1px solid var(--color-border);
|
|
277
|
-
border-radius: var(--border-radius-large, 16px);
|
|
278
|
-
overflow: hidden;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
.cn-table-widget__header {
|
|
282
|
-
display: flex;
|
|
283
|
-
align-items: center;
|
|
284
|
-
justify-content: space-between;
|
|
285
|
-
padding: 12px 16px;
|
|
286
|
-
border-bottom: 1px solid var(--color-border);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
.cn-table-widget__title {
|
|
290
|
-
margin: 0;
|
|
291
|
-
font-size: 14px;
|
|
292
|
-
font-weight: 600;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
.cn-table-widget__count {
|
|
296
|
-
font-size: 12px;
|
|
297
|
-
color: var(--color-text-maxcontrast);
|
|
298
|
-
background: var(--color-background-dark);
|
|
299
|
-
padding: 2px 8px;
|
|
300
|
-
border-radius: 10px;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
.cn-table-widget__loading {
|
|
304
|
-
padding: 32px 0;
|
|
305
|
-
display: flex;
|
|
306
|
-
justify-content: center;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
.cn-table-widget__empty {
|
|
310
|
-
padding: 24px 16px;
|
|
311
|
-
text-align: center;
|
|
312
|
-
color: var(--color-text-maxcontrast);
|
|
313
|
-
font-size: 14px;
|
|
314
|
-
margin: 0;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
.cn-table-widget__footer {
|
|
318
|
-
padding: 8px 16px;
|
|
319
|
-
border-top: 1px solid var(--color-border);
|
|
320
|
-
text-align: center;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
.cn-table-widget__view-all {
|
|
324
|
-
font-size: 13px;
|
|
325
|
-
color: var(--color-primary-element);
|
|
326
|
-
cursor: pointer;
|
|
327
|
-
text-decoration: none;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.cn-table-widget__view-all:hover {
|
|
331
|
-
text-decoration: underline;
|
|
332
|
-
}
|
|
333
|
-
</style>
|
|
1
|
+
<!--
|
|
2
|
+
CnTableWidget — Data table widget with card wrapper and dual data sourcing.
|
|
3
|
+
|
|
4
|
+
Wraps CnDataTable in a card container with a title header, optional "View all"
|
|
5
|
+
footer link, and loading/empty states. Supports two data modes:
|
|
6
|
+
1. External: `rows` prop provided (no API calls)
|
|
7
|
+
2. Self-fetch: `register` + `schemaId` provided (fetches from OpenRegister API)
|
|
8
|
+
|
|
9
|
+
Used in dashboard and detail page grid layouts for displaying related data tables.
|
|
10
|
+
-->
|
|
11
|
+
<template>
|
|
12
|
+
<div class="cn-table-widget">
|
|
13
|
+
<!-- Header -->
|
|
14
|
+
<div v-if="title" class="cn-table-widget__header">
|
|
15
|
+
<h3 class="cn-table-widget__title">
|
|
16
|
+
{{ title }}
|
|
17
|
+
</h3>
|
|
18
|
+
<span v-if="totalCount > 0" class="cn-table-widget__count">
|
|
19
|
+
{{ totalCount }}
|
|
20
|
+
</span>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<!-- Loading state -->
|
|
24
|
+
<NcLoadingIcon v-if="isLoading" class="cn-table-widget__loading" :size="32" />
|
|
25
|
+
|
|
26
|
+
<!-- Empty state -->
|
|
27
|
+
<p v-else-if="displayRows.length === 0" class="cn-table-widget__empty">
|
|
28
|
+
{{ emptyText }}
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
<!-- Data table -->
|
|
32
|
+
<CnDataTable
|
|
33
|
+
v-else
|
|
34
|
+
:rows="displayRows"
|
|
35
|
+
:columns="columns"
|
|
36
|
+
:loading="false"
|
|
37
|
+
:selectable="false"
|
|
38
|
+
@row-click="onRowClick" />
|
|
39
|
+
|
|
40
|
+
<!-- Footer with view all link -->
|
|
41
|
+
<div
|
|
42
|
+
v-if="viewAllRoute && totalCount > limitedCount"
|
|
43
|
+
class="cn-table-widget__footer">
|
|
44
|
+
<a
|
|
45
|
+
class="cn-table-widget__view-all"
|
|
46
|
+
@click.prevent="$router.push(viewAllRoute)">
|
|
47
|
+
{{ viewAllLabel }}
|
|
48
|
+
</a>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<script>
|
|
54
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
55
|
+
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
56
|
+
import CnDataTable from '../CnDataTable/CnDataTable.vue'
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* CnTableWidget — Data table widget with card wrapper and dual data sourcing.
|
|
60
|
+
*
|
|
61
|
+
* @example External data mode
|
|
62
|
+
* <CnTableWidget
|
|
63
|
+
* title="Related Skills"
|
|
64
|
+
* :rows="skillRows"
|
|
65
|
+
* :columns="skillColumns"
|
|
66
|
+
* :view-all-route="{ name: 'Skills' }" />
|
|
67
|
+
*
|
|
68
|
+
* @example Self-fetch mode
|
|
69
|
+
* <CnTableWidget
|
|
70
|
+
* title="Documents"
|
|
71
|
+
* register="9"
|
|
72
|
+
* schema-id="42"
|
|
73
|
+
* :limit="5" />
|
|
74
|
+
*/
|
|
75
|
+
export default {
|
|
76
|
+
name: 'CnTableWidget',
|
|
77
|
+
|
|
78
|
+
components: {
|
|
79
|
+
NcLoadingIcon,
|
|
80
|
+
CnDataTable,
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
props: {
|
|
84
|
+
/** Widget title shown in the header. */
|
|
85
|
+
title: {
|
|
86
|
+
type: String,
|
|
87
|
+
default: '',
|
|
88
|
+
},
|
|
89
|
+
/**
|
|
90
|
+
* External row data. When provided, no API calls are made.
|
|
91
|
+
*
|
|
92
|
+
* @type {object[]}
|
|
93
|
+
*/
|
|
94
|
+
rows: {
|
|
95
|
+
type: Array,
|
|
96
|
+
default: null,
|
|
97
|
+
},
|
|
98
|
+
/**
|
|
99
|
+
* Column definitions for CnDataTable.
|
|
100
|
+
*
|
|
101
|
+
* @type {{ key: string, label: string, sortable?: boolean }[]}
|
|
102
|
+
*/
|
|
103
|
+
columns: {
|
|
104
|
+
type: Array,
|
|
105
|
+
default: () => [],
|
|
106
|
+
},
|
|
107
|
+
/**
|
|
108
|
+
* OpenRegister register ID for self-fetch mode.
|
|
109
|
+
*
|
|
110
|
+
* @type {string|number}
|
|
111
|
+
*/
|
|
112
|
+
register: {
|
|
113
|
+
type: [String, Number],
|
|
114
|
+
default: null,
|
|
115
|
+
},
|
|
116
|
+
/**
|
|
117
|
+
* OpenRegister schema ID for self-fetch mode.
|
|
118
|
+
*
|
|
119
|
+
* @type {string|number}
|
|
120
|
+
*/
|
|
121
|
+
schemaId: {
|
|
122
|
+
type: [String, Number],
|
|
123
|
+
default: null,
|
|
124
|
+
},
|
|
125
|
+
/**
|
|
126
|
+
* Maximum number of rows to display. When total exceeds this,
|
|
127
|
+
* a "View all" link appears.
|
|
128
|
+
*
|
|
129
|
+
* @type {number}
|
|
130
|
+
*/
|
|
131
|
+
limit: {
|
|
132
|
+
type: Number,
|
|
133
|
+
default: 0,
|
|
134
|
+
},
|
|
135
|
+
/**
|
|
136
|
+
* Vue Router route for the "View all" link.
|
|
137
|
+
*
|
|
138
|
+
* @type {object}
|
|
139
|
+
*/
|
|
140
|
+
viewAllRoute: {
|
|
141
|
+
type: Object,
|
|
142
|
+
default: null,
|
|
143
|
+
},
|
|
144
|
+
/**
|
|
145
|
+
* Function that returns a route object for row click navigation.
|
|
146
|
+
* Receives the row data as argument.
|
|
147
|
+
*
|
|
148
|
+
* @type {Function}
|
|
149
|
+
*/
|
|
150
|
+
rowClickRoute: {
|
|
151
|
+
type: Function,
|
|
152
|
+
default: null,
|
|
153
|
+
},
|
|
154
|
+
/** Pre-translated "View all" label. */
|
|
155
|
+
viewAllLabel: {
|
|
156
|
+
type: String,
|
|
157
|
+
default: () => t('nextcloud-vue', 'View all'),
|
|
158
|
+
},
|
|
159
|
+
/** Pre-translated empty state text. */
|
|
160
|
+
emptyText: {
|
|
161
|
+
type: String,
|
|
162
|
+
default: () => t('nextcloud-vue', 'No data available'),
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
data() {
|
|
167
|
+
return {
|
|
168
|
+
fetchedRows: [],
|
|
169
|
+
loading: false,
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
computed: {
|
|
174
|
+
/**
|
|
175
|
+
* Whether data is currently loading.
|
|
176
|
+
*
|
|
177
|
+
* @return {boolean}
|
|
178
|
+
*/
|
|
179
|
+
isLoading() {
|
|
180
|
+
return this.loading
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* All rows (external or fetched).
|
|
185
|
+
*
|
|
186
|
+
* @return {object[]}
|
|
187
|
+
*/
|
|
188
|
+
allRows() {
|
|
189
|
+
return this.rows || this.fetchedRows
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Rows limited to the configured limit.
|
|
194
|
+
*
|
|
195
|
+
* @return {object[]}
|
|
196
|
+
*/
|
|
197
|
+
displayRows() {
|
|
198
|
+
if (this.limit > 0) {
|
|
199
|
+
return this.allRows.slice(0, this.limit)
|
|
200
|
+
}
|
|
201
|
+
return this.allRows
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Total count of all rows (before limiting).
|
|
206
|
+
*
|
|
207
|
+
* @return {number}
|
|
208
|
+
*/
|
|
209
|
+
totalCount() {
|
|
210
|
+
return this.allRows.length
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Count of displayed rows (after limiting).
|
|
215
|
+
*
|
|
216
|
+
* @return {number}
|
|
217
|
+
*/
|
|
218
|
+
limitedCount() {
|
|
219
|
+
return this.displayRows.length
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
mounted() {
|
|
224
|
+
if (this.rows === null && this.register && this.schemaId) {
|
|
225
|
+
this.fetchData()
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
methods: {
|
|
230
|
+
/**
|
|
231
|
+
* Fetch data from the OpenRegister API.
|
|
232
|
+
*
|
|
233
|
+
* @return {Promise<void>}
|
|
234
|
+
*/
|
|
235
|
+
async fetchData() {
|
|
236
|
+
this.loading = true
|
|
237
|
+
try {
|
|
238
|
+
const url = `/index.php/apps/openregister/api/objects/${this.register}/${this.schemaId}`
|
|
239
|
+
const response = await fetch(url, {
|
|
240
|
+
headers: {
|
|
241
|
+
'Content-Type': 'application/json',
|
|
242
|
+
'OCS-APIREQUEST': 'true',
|
|
243
|
+
},
|
|
244
|
+
})
|
|
245
|
+
if (response.ok) {
|
|
246
|
+
const data = await response.json()
|
|
247
|
+
this.fetchedRows = data.results || data || []
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error('CnTableWidget: Failed to fetch data', error)
|
|
251
|
+
} finally {
|
|
252
|
+
this.loading = false
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Handle row click events. Navigates if rowClickRoute is configured.
|
|
258
|
+
*
|
|
259
|
+
* @param {object} row - The clicked row data.
|
|
260
|
+
*/
|
|
261
|
+
onRowClick(row) {
|
|
262
|
+
if (this.rowClickRoute) {
|
|
263
|
+
const route = this.rowClickRoute(row)
|
|
264
|
+
if (route) {
|
|
265
|
+
this.$router.push(route)
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
</script>
|
|
272
|
+
|
|
273
|
+
<style scoped>
|
|
274
|
+
.cn-table-widget {
|
|
275
|
+
background: var(--color-main-background);
|
|
276
|
+
border: 1px solid var(--color-border);
|
|
277
|
+
border-radius: var(--border-radius-large, 16px);
|
|
278
|
+
overflow: hidden;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.cn-table-widget__header {
|
|
282
|
+
display: flex;
|
|
283
|
+
align-items: center;
|
|
284
|
+
justify-content: space-between;
|
|
285
|
+
padding: 12px 16px;
|
|
286
|
+
border-bottom: 1px solid var(--color-border);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.cn-table-widget__title {
|
|
290
|
+
margin: 0;
|
|
291
|
+
font-size: 14px;
|
|
292
|
+
font-weight: 600;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.cn-table-widget__count {
|
|
296
|
+
font-size: 12px;
|
|
297
|
+
color: var(--color-text-maxcontrast);
|
|
298
|
+
background: var(--color-background-dark);
|
|
299
|
+
padding: 2px 8px;
|
|
300
|
+
border-radius: 10px;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.cn-table-widget__loading {
|
|
304
|
+
padding: 32px 0;
|
|
305
|
+
display: flex;
|
|
306
|
+
justify-content: center;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.cn-table-widget__empty {
|
|
310
|
+
padding: 24px 16px;
|
|
311
|
+
text-align: center;
|
|
312
|
+
color: var(--color-text-maxcontrast);
|
|
313
|
+
font-size: 14px;
|
|
314
|
+
margin: 0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.cn-table-widget__footer {
|
|
318
|
+
padding: 8px 16px;
|
|
319
|
+
border-top: 1px solid var(--color-border);
|
|
320
|
+
text-align: center;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.cn-table-widget__view-all {
|
|
324
|
+
font-size: 13px;
|
|
325
|
+
color: var(--color-primary-element);
|
|
326
|
+
cursor: pointer;
|
|
327
|
+
text-decoration: none;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.cn-table-widget__view-all:hover {
|
|
331
|
+
text-decoration: underline;
|
|
332
|
+
}
|
|
333
|
+
</style>
|