@conduction/nextcloud-vue 0.1.0-beta.3 → 0.1.0-beta.4
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/README.md +226 -226
- package/dist/nextcloud-vue.cjs.js +60455 -8755
- package/dist/nextcloud-vue.cjs.js.map +1 -1
- package/dist/nextcloud-vue.css +2062 -528
- package/dist/nextcloud-vue.esm.js +60411 -8731
- package/dist/nextcloud-vue.esm.js.map +1 -1
- package/package.json +75 -62
- package/src/components/CnActionsBar/CnActionsBar.vue +235 -225
- package/src/components/CnActionsBar/index.js +1 -1
- package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +579 -0
- package/src/components/CnAdvancedFormDialog/CnDataTab.vue +217 -0
- package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +121 -0
- package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +418 -0
- package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +247 -0
- package/src/components/CnAdvancedFormDialog/index.js +1 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +152 -152
- package/src/components/CnCardGrid/index.js +1 -1
- package/src/components/CnCellRenderer/CnCellRenderer.vue +132 -132
- package/src/components/CnCellRenderer/index.js +1 -1
- package/src/components/CnChartWidget/CnChartWidget.vue +320 -0
- package/src/components/CnChartWidget/index.js +1 -0
- package/src/components/CnConfigurationCard/CnConfigurationCard.vue +77 -77
- package/src/components/CnConfigurationCard/index.js +1 -1
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +225 -0
- package/src/components/CnDashboardGrid/index.js +1 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +390 -0
- package/src/components/CnDashboardPage/index.js +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +349 -349
- package/src/components/CnDataTable/index.js +1 -1
- package/src/components/CnDetailCard/CnDetailCard.vue +214 -0
- package/src/components/CnDetailCard/index.js +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +281 -0
- package/src/components/CnDetailPage/index.js +1 -0
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +231 -223
- package/src/components/CnFacetSidebar/index.js +1 -1
- package/src/components/CnFilterBar/CnFilterBar.vue +152 -152
- package/src/components/CnFilterBar/index.js +1 -1
- package/src/components/CnIcon/CnIcon.vue +89 -89
- package/src/components/CnIcon/index.js +1 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +874 -816
- package/src/components/CnIndexPage/index.js +1 -1
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +503 -484
- package/src/components/CnIndexSidebar/index.js +1 -1
- package/src/components/CnItemCard/CnItemCard.vue +132 -0
- package/src/components/CnItemCard/index.js +1 -0
- package/src/components/CnKpiGrid/CnKpiGrid.vue +89 -89
- package/src/components/CnKpiGrid/index.js +1 -1
- package/src/components/CnMassActionBar/CnMassActionBar.vue +160 -160
- package/src/components/CnMassActionBar/index.js +1 -1
- package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +320 -320
- package/src/components/CnMassCopyDialog/index.js +1 -1
- package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +238 -238
- package/src/components/CnMassDeleteDialog/index.js +1 -1
- package/src/components/CnMassExportDialog/CnMassExportDialog.vue +190 -190
- package/src/components/CnMassExportDialog/index.js +1 -1
- package/src/components/CnMassImportDialog/CnMassImportDialog.vue +491 -491
- package/src/components/CnMassImportDialog/index.js +1 -1
- package/src/components/CnNoteCard/CnNoteCard.vue +149 -0
- package/src/components/CnNoteCard/index.js +1 -0
- package/src/components/CnNotesCard/CnNotesCard.vue +413 -0
- package/src/components/CnNotesCard/index.js +1 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +292 -292
- package/src/components/CnObjectCard/index.js +1 -1
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +876 -0
- package/src/components/CnObjectSidebar/index.js +1 -0
- package/src/components/CnPageHeader/CnPageHeader.vue +57 -57
- package/src/components/CnPageHeader/index.js +1 -1
- package/src/components/CnPagination/CnPagination.vue +252 -252
- package/src/components/CnPagination/index.js +1 -1
- package/src/components/CnRowActions/CnRowActions.vue +73 -73
- package/src/components/CnRowActions/index.js +1 -1
- package/src/components/CnSchemaFormDialog/CnSchemaConfigurationTab.vue +226 -0
- package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +787 -0
- package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +305 -0
- package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +1398 -0
- package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +236 -0
- package/src/components/CnSchemaFormDialog/index.js +1 -0
- package/src/components/CnSettingsCard/CnSettingsCard.vue +92 -92
- package/src/components/CnSettingsCard/index.js +1 -1
- package/src/components/CnSettingsSection/CnSettingsSection.vue +266 -266
- package/src/components/CnSettingsSection/index.js +1 -1
- package/src/components/CnStatsBlock/CnStatsBlock.vue +420 -366
- package/src/components/CnStatsBlock/index.js +1 -1
- package/src/components/CnStatusBadge/CnStatusBadge.vue +77 -77
- package/src/components/CnStatusBadge/index.js +1 -1
- package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +540 -0
- package/src/components/CnTabbedFormDialog/index.js +1 -0
- package/src/components/CnTasksCard/CnTasksCard.vue +373 -0
- package/src/components/CnTasksCard/index.js +1 -0
- package/src/components/CnTileWidget/CnTileWidget.vue +159 -0
- package/src/components/CnTileWidget/index.js +1 -0
- package/src/components/CnTimelineStages/CnTimelineStages.vue +292 -0
- package/src/components/CnTimelineStages/index.js +1 -0
- package/src/components/CnUserActionMenu/CnUserActionMenu.vue +435 -0
- package/src/components/CnUserActionMenu/index.js +1 -0
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +312 -312
- package/src/components/CnVersionInfoCard/index.js +1 -1
- package/src/components/CnWidgetRenderer/CnWidgetRenderer.vue +180 -0
- package/src/components/CnWidgetRenderer/index.js +1 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +211 -0
- package/src/components/CnWidgetWrapper/index.js +1 -0
- package/src/components/index.js +43 -29
- package/src/composables/index.js +4 -3
- package/src/composables/useDashboardView.js +240 -0
- package/src/composables/useDetailView.js +289 -132
- package/src/composables/useListView.js +363 -362
- package/src/composables/useSubResource.js +142 -142
- package/src/constants/metadata.js +30 -30
- package/src/css/CnSchemaFormDialog.css +546 -0
- package/src/css/__sample_nextcloud_tokens.css +110 -0
- package/src/css/actions-bar.css +48 -48
- package/src/css/badge.css +51 -51
- package/src/css/card.css +128 -128
- package/src/css/dashboard.css +70 -0
- package/src/css/detail-page.css +168 -0
- package/src/css/detail.css +68 -68
- package/src/css/index-page.css +44 -32
- package/src/css/index-sidebar.css +193 -187
- package/src/css/index.css +16 -12
- package/src/css/layout.css +90 -90
- package/src/css/page-header.css +33 -33
- package/src/css/pagination.css +72 -72
- package/src/css/table.css +142 -142
- package/src/css/timeline-stages.css +218 -0
- package/src/css/utilities.css +46 -46
- package/src/index.js +72 -53
- package/src/store/createSubResourcePlugin.js +135 -135
- package/src/store/index.js +3 -3
- package/src/store/plugins/auditTrails.js +17 -17
- package/src/store/plugins/files.js +250 -186
- package/src/store/plugins/index.js +7 -5
- package/src/store/plugins/lifecycle.js +180 -180
- package/src/store/plugins/relations.js +68 -68
- package/src/store/plugins/search.js +372 -0
- package/src/store/plugins/selection.js +104 -0
- package/src/store/useObjectStore.js +829 -686
- package/src/types/auditTrail.d.ts +32 -32
- package/src/types/file.d.ts +23 -23
- package/src/types/index.d.ts +35 -35
- package/src/types/notification.d.ts +36 -36
- package/src/types/object.d.ts +40 -40
- package/src/types/organisation.d.ts +41 -41
- package/src/types/register.d.ts +25 -25
- package/src/types/schema.d.ts +39 -39
- package/src/types/shared.d.ts +79 -79
- package/src/types/source.d.ts +14 -14
- package/src/types/task.d.ts +31 -31
- package/src/utils/errors.js +96 -96
- package/src/utils/headers.js +68 -50
- package/src/utils/id.js +13 -0
- package/src/utils/index.js +3 -3
- package/src/utils/schema.js +422 -419
|
@@ -1,362 +1,363 @@
|
|
|
1
|
-
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
|
|
2
|
-
import { useObjectStore } from '../store/index.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Composable for managing list view state with full objectStore integration.
|
|
6
|
-
*
|
|
7
|
-
* When called with an `objectType` string, connects to the objectStore and handles
|
|
8
|
-
* schema loading, collection fetching, sidebar wiring, and all event handlers
|
|
9
|
-
* automatically. Everything a `CnIndexPage`-based list view needs is returned
|
|
10
|
-
* directly — no additional computed properties or methods required in the component.
|
|
11
|
-
*
|
|
12
|
-
* Backward-compatible: existing `useListView(options)` and `useListView()` calls
|
|
13
|
-
* continue to work without modification.
|
|
14
|
-
*
|
|
15
|
-
* @param {string|object} [objectTypeOrOptions] Object type slug (new API) or legacy options object
|
|
16
|
-
* @param {object} [options] Options (new API only)
|
|
17
|
-
* @param {object|null} [options.sidebarState] Sidebar state object from `inject('sidebarState')`. When provided, the composable wires and unwires the sidebar automatically on mount/unmount.
|
|
18
|
-
* @param {number} [options.defaultPageSize=20] Default `_limit` sent to the API
|
|
19
|
-
* @param {number} [options.debounceMs=300] Search debounce in milliseconds
|
|
20
|
-
* @
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* @
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* @
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
searchTimeout
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* @param {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
sidebarState
|
|
177
|
-
sidebarState.
|
|
178
|
-
sidebarState.
|
|
179
|
-
sidebarState.
|
|
180
|
-
sidebarState.
|
|
181
|
-
sidebarState.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
sidebarState
|
|
190
|
-
sidebarState.
|
|
191
|
-
sidebarState.
|
|
192
|
-
sidebarState.
|
|
193
|
-
sidebarState.
|
|
194
|
-
sidebarState.
|
|
195
|
-
sidebarState.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
* @param {
|
|
257
|
-
* @param {
|
|
258
|
-
* @param {
|
|
259
|
-
* @param {number} [options.
|
|
260
|
-
* @param {
|
|
261
|
-
* @
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
searchTimeout
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
1
|
+
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'
|
|
2
|
+
import { useObjectStore } from '../store/index.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Composable for managing list view state with full objectStore integration.
|
|
6
|
+
*
|
|
7
|
+
* When called with an `objectType` string, connects to the objectStore and handles
|
|
8
|
+
* schema loading, collection fetching, sidebar wiring, and all event handlers
|
|
9
|
+
* automatically. Everything a `CnIndexPage`-based list view needs is returned
|
|
10
|
+
* directly — no additional computed properties or methods required in the component.
|
|
11
|
+
*
|
|
12
|
+
* Backward-compatible: existing `useListView(options)` and `useListView()` calls
|
|
13
|
+
* continue to work without modification.
|
|
14
|
+
*
|
|
15
|
+
* @param {string|object} [objectTypeOrOptions] Object type slug (new API) or legacy options object
|
|
16
|
+
* @param {object} [options] Options (new API only)
|
|
17
|
+
* @param {object|null} [options.sidebarState] Sidebar state object from `inject('sidebarState')`. When provided, the composable wires and unwires the sidebar automatically on mount/unmount.
|
|
18
|
+
* @param {number} [options.defaultPageSize=20] Default `_limit` sent to the API
|
|
19
|
+
* @param {number} [options.debounceMs=300] Search debounce in milliseconds
|
|
20
|
+
* @param {object} [options.defaultSort] Default sort applied on mount e.g. `{ key: 'createdAt', order: 'desc' }`
|
|
21
|
+
* @return {object} Reactive state and event handlers
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // New API — minimal
|
|
25
|
+
* const { schema, objects, loading, pagination,
|
|
26
|
+
* onSearch, onSort, onFilterChange, onPageChange, refresh } = useListView('client')
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // New API — with sidebar wiring
|
|
30
|
+
* const list = useListView('client', {
|
|
31
|
+
* sidebarState: inject('sidebarState', null),
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Legacy API — still works
|
|
36
|
+
* const { searchTerm, filters, onSearchInput, toggleSort } = useListView({
|
|
37
|
+
* objectType: 'client',
|
|
38
|
+
* fetchFn: (type, params) => objectStore.fetchCollection(type, params),
|
|
39
|
+
* })
|
|
40
|
+
*/
|
|
41
|
+
export function useListView(objectTypeOrOptions, options) {
|
|
42
|
+
// Backward compat: if first arg is an object or absent, delegate to legacy implementation
|
|
43
|
+
if (!objectTypeOrOptions || typeof objectTypeOrOptions === 'object') {
|
|
44
|
+
return useLegacyListView(objectTypeOrOptions || {})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── New API ──────────────────────────────────────────────────────────
|
|
48
|
+
const objectType = objectTypeOrOptions
|
|
49
|
+
const opts = options || {}
|
|
50
|
+
const sidebarState = opts.sidebarState || null
|
|
51
|
+
|
|
52
|
+
const objectStore = useObjectStore()
|
|
53
|
+
|
|
54
|
+
// ── State refs ───────────────────────────────────────────────────────
|
|
55
|
+
const schema = ref(null)
|
|
56
|
+
const searchTerm = ref('')
|
|
57
|
+
const sortKey = ref(opts.defaultSort?.key || null)
|
|
58
|
+
const sortOrder = ref(opts.defaultSort?.order || 'asc')
|
|
59
|
+
const activeFilters = ref({})
|
|
60
|
+
const visibleColumns = ref(null)
|
|
61
|
+
const pageSize = ref(opts.defaultPageSize || 20)
|
|
62
|
+
|
|
63
|
+
// ── Computed refs from the store ─────────────────────────────────────
|
|
64
|
+
const objects = computed(() => objectStore.collections[objectType] || [])
|
|
65
|
+
const loading = computed(() => objectStore.loading[objectType] || false)
|
|
66
|
+
const pagination = computed(
|
|
67
|
+
() => objectStore.pagination[objectType] || { total: 0, page: 1, pages: 1, limit: 20 },
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
let searchTimeout = null
|
|
71
|
+
|
|
72
|
+
// ── Param construction ───────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Build API fetch params from current reactive state.
|
|
76
|
+
*
|
|
77
|
+
* @param {number} page Page number to request
|
|
78
|
+
* @return {object} Params object ready to pass to fetchCollection
|
|
79
|
+
*/
|
|
80
|
+
function buildParams(page) {
|
|
81
|
+
const params = { _limit: pageSize.value, _page: page }
|
|
82
|
+
|
|
83
|
+
if (searchTerm.value) {
|
|
84
|
+
params._search = searchTerm.value
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (sortKey.value) {
|
|
88
|
+
params._order = { [sortKey.value]: sortOrder.value }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
for (const [key, values] of Object.entries(activeFilters.value)) {
|
|
92
|
+
if (values && values.length > 0) {
|
|
93
|
+
// Single-value arrays are unwrapped to scalar params
|
|
94
|
+
params[key] = values.length === 1 ? values[0] : values
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return params
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── Fetch ────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Fetch the collection using current state params and update sidebar facet data.
|
|
105
|
+
*
|
|
106
|
+
* @param {number} [page=1] Page to fetch
|
|
107
|
+
* @return {Promise<void>}
|
|
108
|
+
*/
|
|
109
|
+
async function refresh(page = 1) {
|
|
110
|
+
await objectStore.fetchCollection(objectType, buildParams(page))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ── Event handlers ───────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Handle search input. Debounced by `options.debounceMs` (default 300 ms).
|
|
117
|
+
*
|
|
118
|
+
* @param {string} value New search string
|
|
119
|
+
*/
|
|
120
|
+
function onSearch(value) {
|
|
121
|
+
searchTerm.value = value
|
|
122
|
+
clearTimeout(searchTimeout)
|
|
123
|
+
searchTimeout = setTimeout(() => refresh(1), opts.debounceMs || 300)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Handle sort change. Updates sort state and triggers refresh.
|
|
128
|
+
*
|
|
129
|
+
* @param {{key: string, order: string}} sort New sort definition
|
|
130
|
+
*/
|
|
131
|
+
function onSort({ key, order }) {
|
|
132
|
+
sortKey.value = key
|
|
133
|
+
sortOrder.value = order || 'asc'
|
|
134
|
+
refresh(1)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Handle filter change for a single key. Empty arrays remove the key.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} key Filter key (maps to API param name)
|
|
141
|
+
* @param {Array} values Selected filter values
|
|
142
|
+
*/
|
|
143
|
+
function onFilterChange(key, values) {
|
|
144
|
+
if (!values || values.length === 0) {
|
|
145
|
+
const updated = { ...activeFilters.value }
|
|
146
|
+
delete updated[key]
|
|
147
|
+
activeFilters.value = updated
|
|
148
|
+
} else {
|
|
149
|
+
activeFilters.value = { ...activeFilters.value, [key]: values }
|
|
150
|
+
}
|
|
151
|
+
refresh(1)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Handle page navigation.
|
|
156
|
+
*
|
|
157
|
+
* @param {number} page Page number to navigate to
|
|
158
|
+
*/
|
|
159
|
+
function onPageChange(page) {
|
|
160
|
+
refresh(page)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Handle page-size change. Resets to page 1.
|
|
165
|
+
*
|
|
166
|
+
* @param {number} size New page size
|
|
167
|
+
*/
|
|
168
|
+
function onPageSizeChange(size) {
|
|
169
|
+
pageSize.value = size
|
|
170
|
+
refresh(1)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ── Sidebar wiring ───────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
function setupSidebar() {
|
|
176
|
+
if (!sidebarState) return
|
|
177
|
+
sidebarState.active = true
|
|
178
|
+
sidebarState.schema = schema.value
|
|
179
|
+
sidebarState.searchValue = searchTerm.value
|
|
180
|
+
sidebarState.activeFilters = {}
|
|
181
|
+
sidebarState.onSearch = onSearch
|
|
182
|
+
sidebarState.onColumnsChange = (cols) => {
|
|
183
|
+
visibleColumns.value = cols
|
|
184
|
+
}
|
|
185
|
+
sidebarState.onFilterChange = ({ key, values }) => onFilterChange(key, values)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function teardownSidebar() {
|
|
189
|
+
if (!sidebarState) return
|
|
190
|
+
sidebarState.active = false
|
|
191
|
+
sidebarState.schema = null
|
|
192
|
+
sidebarState.activeFilters = {}
|
|
193
|
+
sidebarState.facetData = {}
|
|
194
|
+
sidebarState.onSearch = null
|
|
195
|
+
sidebarState.onColumnsChange = null
|
|
196
|
+
sidebarState.onFilterChange = null
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Push facet data to sidebar after each store update
|
|
200
|
+
if (sidebarState) {
|
|
201
|
+
watch(
|
|
202
|
+
() => objectStore.facets[objectType],
|
|
203
|
+
(facets) => {
|
|
204
|
+
sidebarState.facetData = facets || {}
|
|
205
|
+
},
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ── Lifecycle ────────────────────────────────────────────────────────
|
|
210
|
+
|
|
211
|
+
onMounted(async () => {
|
|
212
|
+
schema.value = await objectStore.fetchSchema(objectType)
|
|
213
|
+
if (sidebarState) {
|
|
214
|
+
setupSidebar()
|
|
215
|
+
}
|
|
216
|
+
await refresh(1)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
onBeforeUnmount(() => {
|
|
220
|
+
clearTimeout(searchTimeout)
|
|
221
|
+
teardownSidebar()
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// ── Return value ─────────────────────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
// Store-derived
|
|
228
|
+
schema,
|
|
229
|
+
objects,
|
|
230
|
+
loading,
|
|
231
|
+
pagination,
|
|
232
|
+
// Local state
|
|
233
|
+
searchTerm,
|
|
234
|
+
sortKey,
|
|
235
|
+
sortOrder,
|
|
236
|
+
activeFilters,
|
|
237
|
+
visibleColumns,
|
|
238
|
+
pageSize,
|
|
239
|
+
// Event handlers
|
|
240
|
+
onSearch,
|
|
241
|
+
onSort,
|
|
242
|
+
onFilterChange,
|
|
243
|
+
onPageChange,
|
|
244
|
+
onPageSizeChange,
|
|
245
|
+
// Explicit fetch
|
|
246
|
+
refresh,
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ── Legacy implementation ─────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Legacy `useListView(options)` implementation.
|
|
254
|
+
* Preserved verbatim for backward compatibility.
|
|
255
|
+
*
|
|
256
|
+
* @param {object} options Legacy options object
|
|
257
|
+
* @param {string} [options.objectType] The registered object type slug
|
|
258
|
+
* @param {Function} [options.fetchFn] Function to call: (type, params) => Promise<Array>
|
|
259
|
+
* @param {number} [options.debounceMs=300] Search debounce in milliseconds
|
|
260
|
+
* @param {number} [options.pageSize=20] Default page size
|
|
261
|
+
* @param {object} [options.defaultSort] Default sort: { key: string, order: 'asc'|'desc' }
|
|
262
|
+
* @return {object} Reactive state and methods
|
|
263
|
+
*/
|
|
264
|
+
function useLegacyListView(options) {
|
|
265
|
+
const searchTerm = ref('')
|
|
266
|
+
const filters = ref({})
|
|
267
|
+
const sortKey = ref(options.defaultSort?.key || null)
|
|
268
|
+
const sortOrder = ref(options.defaultSort?.order || 'asc')
|
|
269
|
+
const currentPage = ref(1)
|
|
270
|
+
const pageSize = ref(options.pageSize || 20)
|
|
271
|
+
|
|
272
|
+
let searchTimeout = null
|
|
273
|
+
|
|
274
|
+
function buildFetchParams() {
|
|
275
|
+
const params = {
|
|
276
|
+
_limit: pageSize.value,
|
|
277
|
+
_page: currentPage.value,
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (searchTerm.value) {
|
|
281
|
+
params._search = searchTerm.value
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (sortKey.value) {
|
|
285
|
+
params._order = { [sortKey.value]: sortOrder.value }
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
for (const [key, value] of Object.entries(filters.value)) {
|
|
289
|
+
if (value !== null && value !== '' && value !== false) {
|
|
290
|
+
params[key] = value
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return params
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function fetchData(page) {
|
|
298
|
+
if (page !== undefined) {
|
|
299
|
+
currentPage.value = page
|
|
300
|
+
}
|
|
301
|
+
const params = buildFetchParams()
|
|
302
|
+
if (options.fetchFn) {
|
|
303
|
+
return options.fetchFn(options.objectType, params)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function onSearchInput(value) {
|
|
308
|
+
searchTerm.value = value
|
|
309
|
+
clearTimeout(searchTimeout)
|
|
310
|
+
searchTimeout = setTimeout(() => fetchData(1), options.debounceMs || 300)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function toggleSort(key) {
|
|
314
|
+
if (sortKey.value === key) {
|
|
315
|
+
if (sortOrder.value === 'asc') {
|
|
316
|
+
sortOrder.value = 'desc'
|
|
317
|
+
} else {
|
|
318
|
+
sortKey.value = null
|
|
319
|
+
sortOrder.value = 'asc'
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
sortKey.value = key
|
|
323
|
+
sortOrder.value = 'asc'
|
|
324
|
+
}
|
|
325
|
+
fetchData(1)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function setFilter(key, value) {
|
|
329
|
+
filters.value = { ...filters.value, [key]: value }
|
|
330
|
+
fetchData(1)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function clearAllFilters() {
|
|
334
|
+
searchTerm.value = ''
|
|
335
|
+
filters.value = {}
|
|
336
|
+
sortKey.value = options.defaultSort?.key || null
|
|
337
|
+
sortOrder.value = options.defaultSort?.order || 'asc'
|
|
338
|
+
fetchData(1)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function goToPage(page) {
|
|
342
|
+
currentPage.value = page
|
|
343
|
+
fetchData()
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
onBeforeUnmount(() => clearTimeout(searchTimeout))
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
searchTerm,
|
|
350
|
+
filters,
|
|
351
|
+
sortKey,
|
|
352
|
+
sortOrder,
|
|
353
|
+
currentPage,
|
|
354
|
+
pageSize,
|
|
355
|
+
onSearchInput,
|
|
356
|
+
toggleSort,
|
|
357
|
+
setFilter,
|
|
358
|
+
clearAllFilters,
|
|
359
|
+
goToPage,
|
|
360
|
+
fetch: fetchData,
|
|
361
|
+
buildFetchParams,
|
|
362
|
+
}
|
|
363
|
+
}
|