@conduction/nextcloud-vue 0.1.0-beta.1

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.
Files changed (93) hide show
  1. package/dist/nextcloud-vue.cjs.js +10710 -0
  2. package/dist/nextcloud-vue.cjs.js.map +1 -0
  3. package/dist/nextcloud-vue.css +803 -0
  4. package/dist/nextcloud-vue.esm.js +10665 -0
  5. package/dist/nextcloud-vue.esm.js.map +1 -0
  6. package/package.json +63 -0
  7. package/src/components/CnCardGrid/CnCardGrid.vue +152 -0
  8. package/src/components/CnCardGrid/index.js +1 -0
  9. package/src/components/CnCellRenderer/CnCellRenderer.vue +132 -0
  10. package/src/components/CnCellRenderer/index.js +1 -0
  11. package/src/components/CnConfigurationCard/CnConfigurationCard.vue +77 -0
  12. package/src/components/CnConfigurationCard/index.js +1 -0
  13. package/src/components/CnDataTable/CnDataTable.vue +354 -0
  14. package/src/components/CnDataTable/index.js +1 -0
  15. package/src/components/CnDetailViewLayout/CnDetailViewLayout.vue +88 -0
  16. package/src/components/CnDetailViewLayout/index.js +1 -0
  17. package/src/components/CnEmptyState/CnEmptyState.vue +78 -0
  18. package/src/components/CnEmptyState/index.js +1 -0
  19. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +223 -0
  20. package/src/components/CnFacetSidebar/index.js +1 -0
  21. package/src/components/CnFilterBar/CnFilterBar.vue +152 -0
  22. package/src/components/CnFilterBar/index.js +1 -0
  23. package/src/components/CnIndexPage/CnIndexPage.vue +682 -0
  24. package/src/components/CnIndexPage/index.js +1 -0
  25. package/src/components/CnKpiGrid/CnKpiGrid.vue +89 -0
  26. package/src/components/CnKpiGrid/index.js +1 -0
  27. package/src/components/CnListViewLayout/CnListViewLayout.vue +80 -0
  28. package/src/components/CnListViewLayout/index.js +1 -0
  29. package/src/components/CnMassActionBar/CnMassActionBar.vue +160 -0
  30. package/src/components/CnMassActionBar/index.js +1 -0
  31. package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +320 -0
  32. package/src/components/CnMassCopyDialog/index.js +1 -0
  33. package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +238 -0
  34. package/src/components/CnMassDeleteDialog/index.js +1 -0
  35. package/src/components/CnMassExportDialog/CnMassExportDialog.vue +190 -0
  36. package/src/components/CnMassExportDialog/index.js +1 -0
  37. package/src/components/CnMassImportDialog/CnMassImportDialog.vue +491 -0
  38. package/src/components/CnMassImportDialog/index.js +1 -0
  39. package/src/components/CnObjectCard/CnObjectCard.vue +292 -0
  40. package/src/components/CnObjectCard/index.js +1 -0
  41. package/src/components/CnPagination/CnPagination.vue +252 -0
  42. package/src/components/CnPagination/index.js +1 -0
  43. package/src/components/CnRowActions/CnRowActions.vue +73 -0
  44. package/src/components/CnRowActions/index.js +1 -0
  45. package/src/components/CnSettingsCard/CnSettingsCard.vue +92 -0
  46. package/src/components/CnSettingsCard/index.js +1 -0
  47. package/src/components/CnSettingsSection/CnSettingsSection.vue +266 -0
  48. package/src/components/CnSettingsSection/index.js +1 -0
  49. package/src/components/CnStatsBlock/CnStatsBlock.vue +366 -0
  50. package/src/components/CnStatsBlock/index.js +1 -0
  51. package/src/components/CnStatusBadge/CnStatusBadge.vue +77 -0
  52. package/src/components/CnStatusBadge/index.js +1 -0
  53. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +312 -0
  54. package/src/components/CnVersionInfoCard/index.js +1 -0
  55. package/src/components/CnViewModeToggle/CnViewModeToggle.vue +77 -0
  56. package/src/components/CnViewModeToggle/index.js +1 -0
  57. package/src/components/index.js +25 -0
  58. package/src/composables/index.js +3 -0
  59. package/src/composables/useDetailView.js +132 -0
  60. package/src/composables/useListView.js +153 -0
  61. package/src/composables/useSubResource.js +142 -0
  62. package/src/css/badge.css +51 -0
  63. package/src/css/card.css +128 -0
  64. package/src/css/detail.css +68 -0
  65. package/src/css/index.css +8 -0
  66. package/src/css/layout.css +90 -0
  67. package/src/css/pagination.css +72 -0
  68. package/src/css/table.css +143 -0
  69. package/src/css/utilities.css +46 -0
  70. package/src/index.js +50 -0
  71. package/src/store/createSubResourcePlugin.js +135 -0
  72. package/src/store/index.js +3 -0
  73. package/src/store/plugins/auditTrails.js +17 -0
  74. package/src/store/plugins/files.js +186 -0
  75. package/src/store/plugins/index.js +4 -0
  76. package/src/store/plugins/lifecycle.js +180 -0
  77. package/src/store/plugins/relations.js +68 -0
  78. package/src/store/useObjectStore.js +625 -0
  79. package/src/types/auditTrail.d.ts +32 -0
  80. package/src/types/file.d.ts +23 -0
  81. package/src/types/index.d.ts +35 -0
  82. package/src/types/notification.d.ts +36 -0
  83. package/src/types/object.d.ts +40 -0
  84. package/src/types/organisation.d.ts +41 -0
  85. package/src/types/register.d.ts +25 -0
  86. package/src/types/schema.d.ts +39 -0
  87. package/src/types/shared.d.ts +79 -0
  88. package/src/types/source.d.ts +14 -0
  89. package/src/types/task.d.ts +31 -0
  90. package/src/utils/errors.js +96 -0
  91. package/src/utils/headers.js +44 -0
  92. package/src/utils/index.js +3 -0
  93. package/src/utils/schema.js +287 -0
@@ -0,0 +1,625 @@
1
+ import { defineStore } from 'pinia'
2
+ import { buildHeaders, buildQueryString } from '../utils/headers.js'
3
+ import { parseResponseError, networkError, genericError } from '../utils/errors.js'
4
+
5
+ /**
6
+ * Generic Pinia store for OpenRegister object CRUD operations.
7
+ *
8
+ * Provides a unified interface for managing objects across registers and schemas.
9
+ * Apps register their object types with schema/register IDs, then use type slugs
10
+ * for all operations. Supports plugins for sub-resources (files, audit trails, etc.).
11
+ *
12
+ * @example
13
+ * // Basic usage (CRUD only)
14
+ * import { useObjectStore } from '@conduction/nextcloud-vue'
15
+ * const store = useObjectStore()
16
+ *
17
+ * @example
18
+ * // With plugins
19
+ * import { createObjectStore, filesPlugin, auditTrailsPlugin } from '@conduction/nextcloud-vue'
20
+ * const useMyStore = createObjectStore('object', {
21
+ * plugins: [filesPlugin(), auditTrailsPlugin()],
22
+ * })
23
+ */
24
+
25
+ const DEFAULT_STORE_ID = 'conduction-objects'
26
+ const DEFAULT_BASE_URL = '/apps/openregister/api/objects'
27
+
28
+ /**
29
+ * Capitalize the first letter of a string.
30
+ *
31
+ * @param {string} str Input string
32
+ * @return {string} Capitalized string
33
+ */
34
+ function capitalize(str) {
35
+ return str.charAt(0).toUpperCase() + str.slice(1)
36
+ }
37
+
38
+ /**
39
+ * Merge plugin state factories into a single state object.
40
+ *
41
+ * @param {Array} plugins Array of plugin definitions
42
+ * @return {object} Merged state object
43
+ */
44
+ function mergePluginState(plugins) {
45
+ const merged = {}
46
+ for (const plugin of plugins) {
47
+ if (plugin.state) {
48
+ Object.assign(merged, plugin.state())
49
+ }
50
+ }
51
+ return merged
52
+ }
53
+
54
+ /**
55
+ * Merge plugin getters into a single getters object.
56
+ *
57
+ * @param {Array} plugins Array of plugin definitions
58
+ * @return {object} Merged getters object
59
+ */
60
+ function mergePluginGetters(plugins) {
61
+ const merged = {}
62
+ for (const plugin of plugins) {
63
+ if (plugin.getters) {
64
+ Object.assign(merged, plugin.getters)
65
+ }
66
+ }
67
+ return merged
68
+ }
69
+
70
+ /**
71
+ * Merge plugin actions into a single actions object.
72
+ *
73
+ * @param {Array} plugins Array of plugin definitions
74
+ * @return {object} Merged actions object
75
+ */
76
+ function mergePluginActions(plugins) {
77
+ const merged = {}
78
+ for (const plugin of plugins) {
79
+ if (plugin.actions) {
80
+ Object.assign(merged, plugin.actions)
81
+ }
82
+ }
83
+ return merged
84
+ }
85
+
86
+ // ── Base state ──────────────────────────────────────────────────────────
87
+
88
+ function baseState() {
89
+ return {
90
+ /** @type {Object<string, {schema: string, register: string}>} */
91
+ objectTypeRegistry: {},
92
+ /** @type {Object<string, Array>} */
93
+ collections: {},
94
+ /** @type {Object<string, Object<string, object>>} */
95
+ objects: {},
96
+ /** @type {Object<string, boolean>} */
97
+ loading: {},
98
+ /** @type {Object<string, import('../utils/errors.js').ApiError|null>} */
99
+ errors: {},
100
+ /** @type {Object<string, {total: number, page: number, pages: number, limit: number}>} */
101
+ pagination: {},
102
+ /** @type {Object<string, string>} */
103
+ searchTerms: {},
104
+ /** @type {Object<string, object|null>} */
105
+ schemas: {},
106
+ /** @type {{baseUrl: string}} */
107
+ _options: {
108
+ baseUrl: DEFAULT_BASE_URL,
109
+ },
110
+ }
111
+ }
112
+
113
+ // ── Base getters ────────────────────────────────────────────────────────
114
+
115
+ const baseGetters = {
116
+ /**
117
+ * Get all registered object type slugs.
118
+ * @return {string[]}
119
+ */
120
+ objectTypes: (state) => Object.keys(state.objectTypeRegistry),
121
+
122
+ /**
123
+ * Get the collection array for a type.
124
+ * @return {Function} (type: string) => Array
125
+ */
126
+ getCollection: (state) => (type) => state.collections[type] || [],
127
+
128
+ /**
129
+ * Get a single cached object by type and ID.
130
+ * @return {Function} (type: string, id: string) => object|null
131
+ */
132
+ getObject: (state) => (type, id) => state.objects[type]?.[id] || null,
133
+
134
+ /**
135
+ * Alias for getObject — check cache without fetching.
136
+ * @return {Function} (type: string, id: string) => object|null
137
+ */
138
+ getCachedObject: (state) => (type, id) => state.objects[type]?.[id] || null,
139
+
140
+ /**
141
+ * Check if a type is currently loading.
142
+ * @return {Function} (type: string) => boolean
143
+ */
144
+ isLoading: (state) => (type) => state.loading[type] || false,
145
+
146
+ /**
147
+ * Get the current error for a type.
148
+ * @return {Function} (type: string) => ApiError|null
149
+ */
150
+ getError: (state) => (type) => state.errors[type] || null,
151
+
152
+ /**
153
+ * Get pagination state for a type.
154
+ * @return {Function} (type: string) => {total, page, pages, limit}
155
+ */
156
+ getPagination: (state) => (type) =>
157
+ state.pagination[type] || { total: 0, page: 1, pages: 1, limit: 20 },
158
+
159
+ /**
160
+ * Get the current search term for a type.
161
+ * @return {Function} (type: string) => string
162
+ */
163
+ getSearchTerm: (state) => (type) => state.searchTerms[type] || '',
164
+
165
+ /**
166
+ * Get a cached schema for a type.
167
+ * @return {Function} (type: string) => object|null
168
+ */
169
+ getSchema: (state) => (type) => state.schemas[type] || null,
170
+ }
171
+
172
+ // ── Base actions ────────────────────────────────────────────────────────
173
+
174
+ const baseActions = {
175
+ /**
176
+ * Configure the store with custom options.
177
+ * Call once before using the store if you need a custom base URL.
178
+ *
179
+ * @param {object} options Configuration options
180
+ * @param {string} [options.baseUrl] Custom base URL for API calls
181
+ */
182
+ configure(options) {
183
+ Object.assign(this._options, options)
184
+ },
185
+
186
+ /**
187
+ * Register an object type for CRUD operations.
188
+ *
189
+ * @param {string} slug Short name for the type (e.g. 'client', 'case')
190
+ * @param {string} schemaId OpenRegister schema ID
191
+ * @param {string} registerId OpenRegister register ID
192
+ */
193
+ registerObjectType(slug, schemaId, registerId) {
194
+ this.objectTypeRegistry[slug] = { schema: schemaId, register: registerId }
195
+ this.collections[slug] = []
196
+ this.objects[slug] = {}
197
+ this.loading[slug] = false
198
+ this.errors[slug] = null
199
+ this.pagination[slug] = { total: 0, page: 1, pages: 1, limit: 20 }
200
+ this.searchTerms[slug] = ''
201
+ this.schemas[slug] = null
202
+ },
203
+
204
+ /**
205
+ * Unregister an object type and clean up all its state.
206
+ *
207
+ * @param {string} slug The type slug to unregister
208
+ */
209
+ unregisterObjectType(slug) {
210
+ delete this.objectTypeRegistry[slug]
211
+ delete this.collections[slug]
212
+ delete this.objects[slug]
213
+ delete this.loading[slug]
214
+ delete this.errors[slug]
215
+ delete this.pagination[slug]
216
+ delete this.searchTerms[slug]
217
+ delete this.schemas[slug]
218
+ },
219
+
220
+ /**
221
+ * Get the type config or throw if not registered.
222
+ *
223
+ * @param {string} type The type slug
224
+ * @return {{schema: string, register: string}} Type configuration
225
+ * @throws {Error} If the type is not registered
226
+ */
227
+ _getTypeConfig(type) {
228
+ const config = this.objectTypeRegistry[type]
229
+ if (!config) {
230
+ throw new Error(`Object type "${type}" is not registered in the store. Call registerObjectType('${type}', schemaId, registerId) first.`)
231
+ }
232
+ return config
233
+ },
234
+
235
+ /**
236
+ * Build the API URL for a type and optional object ID.
237
+ *
238
+ * @param {string} type The type slug
239
+ * @param {string|null} [id=null] Optional object ID
240
+ * @return {string} Full API URL path
241
+ */
242
+ _buildUrl(type, id = null) {
243
+ const config = this._getTypeConfig(type)
244
+ let url = `${this._options.baseUrl}/${config.register}/${config.schema}`
245
+ if (id) {
246
+ url += `/${id}`
247
+ }
248
+ return url
249
+ },
250
+
251
+ /**
252
+ * Clear the error state for a type.
253
+ *
254
+ * @param {string} type The type slug
255
+ */
256
+ clearError(type) {
257
+ this.errors[type] = null
258
+ },
259
+
260
+ /**
261
+ * Set the search term for a type.
262
+ *
263
+ * @param {string} type The type slug
264
+ * @param {string} term The search term
265
+ */
266
+ setSearchTerm(type, term) {
267
+ this.searchTerms[type] = term
268
+ },
269
+
270
+ /**
271
+ * Clear the search term for a type.
272
+ *
273
+ * @param {string} type The type slug
274
+ */
275
+ clearSearchTerm(type) {
276
+ this.searchTerms[type] = ''
277
+ },
278
+
279
+ /**
280
+ * Fetch the schema definition for a registered type.
281
+ * Uses cache — only fetches once per type per session.
282
+ *
283
+ * @param {string} type The registered type slug
284
+ * @return {Promise<object|null>} The schema object or null on error
285
+ */
286
+ async fetchSchema(type) {
287
+ const config = this._getTypeConfig(type)
288
+
289
+ if (this.schemas[type]) {
290
+ return this.schemas[type]
291
+ }
292
+
293
+ try {
294
+ const response = await fetch(
295
+ `/apps/openregister/api/schemas/${config.schema}`,
296
+ { method: 'GET', headers: buildHeaders() },
297
+ )
298
+
299
+ if (!response.ok) return null
300
+
301
+ const schema = await response.json()
302
+ this.schemas[type] = schema
303
+ return schema
304
+ } catch {
305
+ return null
306
+ }
307
+ },
308
+
309
+ /**
310
+ * Fetch a collection of objects for a registered type.
311
+ *
312
+ * @param {string} type The registered type slug
313
+ * @param {object} [params={}] Query parameters (_limit, _page, _search, _order, filters)
314
+ * @return {Promise<Array>} The fetched collection (also stored in state)
315
+ */
316
+ async fetchCollection(type, params = {}) {
317
+ this.loading[type] = true
318
+ this.errors[type] = null
319
+
320
+ try {
321
+ const url = this._buildUrl(type) + buildQueryString(params)
322
+
323
+ const response = await fetch(url, {
324
+ method: 'GET',
325
+ headers: buildHeaders(),
326
+ })
327
+
328
+ if (!response.ok) {
329
+ this.errors[type] = await parseResponseError(response, type)
330
+ console.error(`Error fetching ${type} collection:`, this.errors[type])
331
+ return []
332
+ }
333
+
334
+ const data = await response.json()
335
+
336
+ this.collections[type] = data.results || data
337
+ this.pagination[type] = {
338
+ total: data.total || (data.results || data).length,
339
+ page: data.page || 1,
340
+ pages: data.pages || 1,
341
+ limit: params._limit || 20,
342
+ }
343
+
344
+ return this.collections[type]
345
+ } catch (error) {
346
+ this.errors[type] = error.name === 'TypeError'
347
+ ? networkError(error)
348
+ : genericError(error)
349
+ console.error(`Error fetching ${type} collection:`, error)
350
+ return []
351
+ } finally {
352
+ this.loading[type] = false
353
+ }
354
+ },
355
+
356
+ /**
357
+ * Fetch a single object by type and ID.
358
+ *
359
+ * @param {string} type The registered type slug
360
+ * @param {string} id The object ID or UUID
361
+ * @return {Promise<object|null>} The fetched object (also cached in state)
362
+ */
363
+ async fetchObject(type, id) {
364
+ this.loading[type] = true
365
+ this.errors[type] = null
366
+
367
+ try {
368
+ const url = this._buildUrl(type, id)
369
+
370
+ const response = await fetch(url, {
371
+ method: 'GET',
372
+ headers: buildHeaders(),
373
+ })
374
+
375
+ if (!response.ok) {
376
+ this.errors[type] = await parseResponseError(response, type)
377
+ console.error(`Error fetching ${type}/${id}:`, this.errors[type])
378
+ return null
379
+ }
380
+
381
+ const data = await response.json()
382
+
383
+ if (!this.objects[type]) {
384
+ this.objects[type] = {}
385
+ }
386
+ this.objects[type][id] = data
387
+
388
+ return data
389
+ } catch (error) {
390
+ this.errors[type] = error.name === 'TypeError'
391
+ ? networkError(error)
392
+ : genericError(error)
393
+ console.error(`Error fetching ${type}/${id}:`, error)
394
+ return null
395
+ } finally {
396
+ this.loading[type] = false
397
+ }
398
+ },
399
+
400
+ /**
401
+ * Create or update an object. Uses POST for new objects, PUT for updates.
402
+ *
403
+ * @param {string} type The registered type slug
404
+ * @param {object} objectData The object data (include `id` for updates)
405
+ * @return {Promise<object|null>} The saved object or null on error
406
+ */
407
+ async saveObject(type, objectData) {
408
+ this.loading[type] = true
409
+ this.errors[type] = null
410
+
411
+ try {
412
+ const isUpdate = !!objectData.id
413
+ const url = isUpdate
414
+ ? this._buildUrl(type, objectData.id)
415
+ : this._buildUrl(type)
416
+ const method = isUpdate ? 'PUT' : 'POST'
417
+
418
+ const response = await fetch(url, {
419
+ method,
420
+ headers: buildHeaders(),
421
+ body: JSON.stringify(objectData),
422
+ })
423
+
424
+ if (!response.ok) {
425
+ this.errors[type] = await parseResponseError(response, type)
426
+ console.error(`Error saving ${type}:`, this.errors[type])
427
+ return null
428
+ }
429
+
430
+ const data = await response.json()
431
+
432
+ if (!this.objects[type]) {
433
+ this.objects[type] = {}
434
+ }
435
+ const savedId = data.id || objectData.id
436
+ this.objects[type][savedId] = data
437
+
438
+ return data
439
+ } catch (error) {
440
+ this.errors[type] = error.name === 'TypeError'
441
+ ? networkError(error)
442
+ : genericError(error)
443
+ console.error(`Error saving ${type}:`, error)
444
+ return null
445
+ } finally {
446
+ this.loading[type] = false
447
+ }
448
+ },
449
+
450
+ /**
451
+ * Delete an object by type and ID.
452
+ *
453
+ * @param {string} type The registered type slug
454
+ * @param {string} id The object ID
455
+ * @return {Promise<boolean>} True if deleted successfully
456
+ */
457
+ async deleteObject(type, id) {
458
+ this.loading[type] = true
459
+ this.errors[type] = null
460
+
461
+ try {
462
+ const url = this._buildUrl(type, id)
463
+
464
+ const response = await fetch(url, {
465
+ method: 'DELETE',
466
+ headers: buildHeaders(),
467
+ })
468
+
469
+ if (!response.ok) {
470
+ this.errors[type] = await parseResponseError(response, type)
471
+ console.error(`Error deleting ${type}/${id}:`, this.errors[type])
472
+ return false
473
+ }
474
+
475
+ if (this.objects[type]) {
476
+ delete this.objects[type][id]
477
+ }
478
+ if (this.collections[type]) {
479
+ this.collections[type] = this.collections[type].filter(
480
+ (obj) => obj.id !== id,
481
+ )
482
+ }
483
+
484
+ return true
485
+ } catch (error) {
486
+ this.errors[type] = error.name === 'TypeError'
487
+ ? networkError(error)
488
+ : genericError(error)
489
+ console.error(`Error deleting ${type}/${id}:`, error)
490
+ return false
491
+ } finally {
492
+ this.loading[type] = false
493
+ }
494
+ },
495
+
496
+ /**
497
+ * Batch-resolve references by fetching multiple objects by their IDs.
498
+ * Uses the cache first, only fetches uncached objects.
499
+ *
500
+ * @param {string} type The registered type slug
501
+ * @param {string[]} ids Array of object IDs to resolve
502
+ * @return {Promise<Object<string, object>>} Map of id -> object
503
+ */
504
+ async resolveReferences(type, ids) {
505
+ if (!ids || ids.length === 0) return {}
506
+
507
+ const uniqueIds = [...new Set(ids.filter(Boolean))]
508
+ const result = {}
509
+ const toFetch = []
510
+
511
+ for (const id of uniqueIds) {
512
+ const cached = this.objects[type]?.[id]
513
+ if (cached) {
514
+ result[id] = cached
515
+ } else {
516
+ toFetch.push(id)
517
+ }
518
+ }
519
+
520
+ if (toFetch.length > 0) {
521
+ const fetches = toFetch.map(async (id) => {
522
+ try {
523
+ const url = this._buildUrl(type, id)
524
+ const response = await fetch(url, {
525
+ method: 'GET',
526
+ headers: buildHeaders(),
527
+ })
528
+ if (response.ok) {
529
+ const data = await response.json()
530
+ if (!this.objects[type]) this.objects[type] = {}
531
+ this.objects[type][id] = data
532
+ result[id] = data
533
+ }
534
+ } catch {
535
+ // Non-blocking — leave unresolved
536
+ }
537
+ })
538
+ await Promise.all(fetches)
539
+ }
540
+
541
+ return result
542
+ },
543
+ }
544
+
545
+ // ── Store factory ───────────────────────────────────────────────────────
546
+
547
+ /**
548
+ * Create the object store definition with a given store ID and optional plugins.
549
+ *
550
+ * Plugins are merged into the store at definition time. Each plugin provides
551
+ * additional state, getters, and actions (e.g. for sub-resources like files,
552
+ * audit trails, relations).
553
+ *
554
+ * @param {string} storeId Pinia store identifier
555
+ * @param {Array} [plugins=[]] Array of plugin definitions
556
+ * @return {Function} Pinia store composable
557
+ */
558
+ function defineObjectStore(storeId, plugins = []) {
559
+ const pluginState = mergePluginState(plugins)
560
+ const pluginGetters = mergePluginGetters(plugins)
561
+ const pluginActions = mergePluginActions(plugins)
562
+
563
+ return defineStore(storeId, {
564
+ state: () => ({
565
+ ...baseState(),
566
+ ...pluginState,
567
+ }),
568
+
569
+ getters: {
570
+ ...baseGetters,
571
+ ...pluginGetters,
572
+ },
573
+
574
+ actions: {
575
+ ...baseActions,
576
+ ...pluginActions,
577
+
578
+ /**
579
+ * Clear all sub-resource data from active plugins.
580
+ * Calls each plugin's clear method (e.g. clearFiles, clearAuditTrails).
581
+ */
582
+ clearAllSubResources() {
583
+ for (const plugin of plugins) {
584
+ const clearFn = `clear${capitalize(plugin.name)}`
585
+ if (typeof this[clearFn] === 'function') {
586
+ this[clearFn]()
587
+ }
588
+ }
589
+ },
590
+ },
591
+ })
592
+ }
593
+
594
+ /**
595
+ * Default object store instance with ID 'conduction-objects'.
596
+ *
597
+ * @example
598
+ * import { useObjectStore } from '@conduction/nextcloud-vue'
599
+ * const store = useObjectStore()
600
+ */
601
+ export const useObjectStore = defineObjectStore(DEFAULT_STORE_ID)
602
+
603
+ /**
604
+ * Factory function to create an object store with a custom Pinia store ID
605
+ * and optional plugins for sub-resources.
606
+ *
607
+ * @param {string} storeId Custom Pinia store identifier
608
+ * @param {object} [options={}] Configuration options
609
+ * @param {Array} [options.plugins=[]] Array of sub-resource plugins
610
+ * @return {Function} Pinia store composable
611
+ *
612
+ * @example
613
+ * // Basic (backwards compatible)
614
+ * const useMyStore = createObjectStore('object')
615
+ *
616
+ * @example
617
+ * // With plugins
618
+ * import { filesPlugin, auditTrailsPlugin } from '@conduction/nextcloud-vue'
619
+ * const useMyStore = createObjectStore('object', {
620
+ * plugins: [filesPlugin(), auditTrailsPlugin()],
621
+ * })
622
+ */
623
+ export function createObjectStore(storeId, options = {}) {
624
+ return defineObjectStore(storeId, options.plugins || [])
625
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * OpenRegister Audit Trail entity type.
3
+ *
4
+ * Records changes made to objects for compliance and history tracking.
5
+ */
6
+ export interface TAuditTrail {
7
+ id: number
8
+ uuid: string
9
+ schema: number
10
+ register: number
11
+ object: number
12
+ objectUuid: string | null
13
+ registerUuid: string | null
14
+ schemaUuid: string | null
15
+ action: string
16
+ changed: Record<string, unknown> | unknown[]
17
+ user: string
18
+ userName: string
19
+ session: string
20
+ request: string
21
+ ipAddress: string
22
+ version: string | null
23
+ created: string
24
+ organisationId: string | null
25
+ organisationIdType: string | null
26
+ processingActivityId: string | null
27
+ processingActivityUrl: string | null
28
+ processingId: string | null
29
+ confidentiality: string | null
30
+ retentionPeriod: string | null
31
+ size: number
32
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * OpenRegister File entity type.
3
+ *
4
+ * Represents a file attached to an object. Files are stored in Nextcloud's
5
+ * file system and referenced by the object's files sub-resource.
6
+ */
7
+ export interface TFile {
8
+ id: string | number
9
+ uuid?: string
10
+ name: string
11
+ mimeType: string
12
+ size: number
13
+ path?: string
14
+ url?: string
15
+ downloadUrl?: string
16
+ shareUrl?: string | null
17
+ tags?: string[]
18
+ published?: string | null
19
+ depublished?: string | null
20
+ owner?: string
21
+ created: string
22
+ updated?: string
23
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * TypeScript type definitions for OpenRegister entities and shared utilities.
3
+ *
4
+ * @example
5
+ * // Import specific types
6
+ * import type { TObject, TSchema, TRegister } from '@conduction/nextcloud-vue/src/types'
7
+ *
8
+ * // Import shared utilities
9
+ * import type { TPaginated, TApiError, TQuota } from '@conduction/nextcloud-vue/src/types'
10
+ */
11
+
12
+ // Shared utility types
13
+ export type {
14
+ TPaginated,
15
+ TEmptyPaginated,
16
+ TQuota,
17
+ TUsage,
18
+ TEntityStats,
19
+ TCrudAuthorization,
20
+ TApiError,
21
+ TObjectPath,
22
+ } from './shared'
23
+
24
+ // Core entity types
25
+ export type { TObject } from './object'
26
+ export type { TSchema, TSchemaConfiguration } from './schema'
27
+ export type { TRegister } from './register'
28
+ export type { TAuditTrail } from './auditTrail'
29
+ export type { TSource } from './source'
30
+ export type { TOrganisation, TOrganisationAuthorization } from './organisation'
31
+
32
+ // Sub-resource entity types
33
+ export type { TFile } from './file'
34
+ export type { TTask, TTaskPriority, TTaskStatus } from './task'
35
+ export type { TNotification, TNotificationType, TNotificationPriority } from './notification'