@conduction/nextcloud-vue 0.1.0-beta.10 → 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.
Files changed (60) hide show
  1. package/dist/nextcloud-vue.cjs.js +64663 -63467
  2. package/dist/nextcloud-vue.cjs.js.map +1 -1
  3. package/dist/nextcloud-vue.css +443 -444
  4. package/dist/nextcloud-vue.esm.js +64637 -63443
  5. package/dist/nextcloud-vue.esm.js.map +1 -1
  6. package/l10n/en.json +164 -0
  7. package/l10n/nl.json +164 -0
  8. package/package.json +19 -3
  9. package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +8 -7
  10. package/src/components/CnAdvancedFormDialog/CnDataTab.vue +2 -2
  11. package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +5 -5
  12. package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +2 -2
  13. package/src/components/CnCardGrid/CnCardGrid.vue +2 -1
  14. package/src/components/CnChartWidget/CnChartWidget.vue +29 -1
  15. package/src/components/CnCopyDialog/CnCopyDialog.vue +15 -6
  16. package/src/components/CnDashboardPage/CnDashboardPage.vue +5 -4
  17. package/src/components/CnDetailGrid/CnDetailGrid.vue +3 -1
  18. package/src/components/CnDetailPage/CnDetailPage.vue +5 -4
  19. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +3 -2
  20. package/src/components/CnFilterBar/CnFilterBar.vue +3 -2
  21. package/src/components/CnFormDialog/CnFormDialog.vue +122 -9
  22. package/src/components/CnIndexPage/CnIndexPage.vue +1 -0
  23. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +8 -7
  24. package/src/components/CnJsonViewer/CnJsonViewer.vue +33 -4
  25. package/src/components/CnMassActionBar/CnMassActionBar.vue +1 -1
  26. package/src/components/CnMassImportDialog/CnMassImportDialog.vue +2 -2
  27. package/src/components/CnNotesCard/CnNotesCard.vue +7 -6
  28. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +11 -10
  29. package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +3 -2
  30. package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +8 -7
  31. package/src/components/CnObjectSidebar/CnFilesTab.vue +6 -5
  32. package/src/components/CnObjectSidebar/CnNotesTab.vue +8 -7
  33. package/src/components/CnObjectSidebar/CnObjectSidebar.vue +6 -5
  34. package/src/components/CnObjectSidebar/CnTagsTab.vue +3 -2
  35. package/src/components/CnObjectSidebar/CnTasksTab.vue +11 -10
  36. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +14 -13
  37. package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +15 -14
  38. package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +4 -4
  39. package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +10 -10
  40. package/src/components/CnSettingsSection/CnSettingsSection.vue +5 -4
  41. package/src/components/CnStatsBlock/CnStatsBlock.vue +5 -4
  42. package/src/components/CnStatsPanel/CnStatsPanel.vue +3 -2
  43. package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +9 -8
  44. package/src/components/CnTableWidget/CnTableWidget.vue +3 -2
  45. package/src/components/CnTasksCard/CnTasksCard.vue +5 -4
  46. package/src/components/CnTimelineStages/CnTimelineStages.vue +3 -1
  47. package/src/components/CnUserActionMenu/CnUserActionMenu.vue +7 -6
  48. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +4 -3
  49. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +3 -1
  50. package/src/index.js +4 -0
  51. package/src/l10n/index.js +12 -0
  52. package/src/store/createCrudStore.d.ts +350 -0
  53. package/src/store/createCrudStore.js +58 -5
  54. package/src/store/pluginMerge.js +55 -0
  55. package/src/store/plugins/index.js +1 -0
  56. package/src/store/plugins/logs.d.ts +22 -0
  57. package/src/store/plugins/logs.js +172 -0
  58. package/src/store/useObjectStore.js +19 -49
  59. package/src/types/index.d.ts +32 -0
  60. package/src/utils/schema.js +3 -2
@@ -0,0 +1,172 @@
1
+ import { buildHeaders, buildQueryString } from '../../utils/headers.js'
2
+ import { parseResponseError, networkError, genericError } from '../../utils/errors.js'
3
+
4
+ /**
5
+ * Logs sub-resource plugin for createCrudStore.
6
+ *
7
+ * Many apps expose a logs collection keyed off the parent resource ID, via a
8
+ * flat endpoint like `/sources/logs?source_id=<id>`. This plugin generates the
9
+ * standard state, getters, and actions for that pattern so consumer stores
10
+ * don't have to re-implement the same fetch/refresh logic.
11
+ *
12
+ * **Contributed state**
13
+ * - `logs` — last fetched response (raw array or `{ results, ... }` shape)
14
+ * - `logsLoading` — per-store loading flag
15
+ * - `logsError` — last error (`ApiError` or null)
16
+ *
17
+ * **Contributed getters**
18
+ * - `getLogs`, `isLogsLoading`, `getLogsError`
19
+ *
20
+ * **Contributed actions**
21
+ * - `refreshLogs(filters?)` — fetch and store; returns `{ response, data }`
22
+ * - `setLogs(data)` — replace state directly
23
+ * - `clearLogs()` — reset to empty
24
+ *
25
+ * When `autoRefreshOnItemChange: true`, the plugin registers a
26
+ * `store.$onAction` subscriber in its `setup` hook that observes `setItem`
27
+ * and triggers `refreshLogs()` / `clearLogs()` after the action resolves.
28
+ * This composes cleanly with other plugins that also want to observe
29
+ * `setItem` — none of them have to override the action.
30
+ *
31
+ * **URL construction**
32
+ * - `this._options.baseApiUrl + '/' + path` → e.g. `/sources/logs`
33
+ * - Query params: `{ [parentIdParam]: item.id, ...defaultSort, ...filters }` —
34
+ * caller-supplied filters always win.
35
+ *
36
+ * @param {object} options Plugin options
37
+ * @param {string} options.parentIdParam Required. Query param name carrying the
38
+ * parent resource's id — e.g. `'source_id'` for a sources store.
39
+ * @param {string} [options.path] Path segment appended to the store's base API
40
+ * URL. Default: `'logs'`.
41
+ * @param {object} [options.defaultSort] Default query params merged before
42
+ * user filters. Default: `{ '_sort[created]': 'desc' }`.
43
+ * @param {boolean} [options.autoRefreshOnItemChange] When true, overrides
44
+ * `setItem` so selecting a new item automatically refreshes logs. Default:
45
+ * `false`.
46
+ * @return {object} Plugin definition consumed by `createCrudStore`.
47
+ *
48
+ * @example
49
+ * import { createCrudStore, logsPlugin } from '@conduction/nextcloud-vue'
50
+ *
51
+ * export const useSourceStore = createCrudStore('source', {
52
+ * endpoint: 'sources',
53
+ * entity: Source,
54
+ * plugins: [logsPlugin({ parentIdParam: 'source_id', autoRefreshOnItemChange: true })],
55
+ * })
56
+ *
57
+ * // Later
58
+ * const store = useSourceStore()
59
+ * await store.refreshLogs({ '_limit': 50 })
60
+ * console.log(store.logs, store.logsLoading)
61
+ */
62
+ export function logsPlugin(options = {}) {
63
+ const {
64
+ parentIdParam,
65
+ path = 'logs',
66
+ defaultSort = { '_sort[created]': 'desc' },
67
+ autoRefreshOnItemChange = false,
68
+ } = options
69
+
70
+ if (!parentIdParam) {
71
+ throw new Error('logsPlugin: options.parentIdParam is required')
72
+ }
73
+
74
+ const actions = {
75
+ /**
76
+ * Fetch logs for the current item from the logs endpoint.
77
+ *
78
+ * @param {object} [filters] Extra query params (override defaults).
79
+ * @return {Promise<{ response: Response, data: unknown }>}
80
+ */
81
+ async refreshLogs(filters = {}) {
82
+ this.logsLoading = true
83
+ this.logsError = null
84
+
85
+ try {
86
+ const params = { ...defaultSort }
87
+ if (!(parentIdParam in filters) && this.item?.id != null) {
88
+ params[parentIdParam] = String(this.item.id)
89
+ }
90
+ Object.assign(params, filters)
91
+
92
+ const url = this._options.baseApiUrl + '/' + path + buildQueryString(params)
93
+ const response = await fetch(url, {
94
+ method: 'GET',
95
+ headers: buildHeaders(),
96
+ })
97
+
98
+ if (!response.ok) {
99
+ this.logsError = await parseResponseError(response, 'logs')
100
+ return { response, data: null }
101
+ }
102
+
103
+ const data = await response.json()
104
+ this.logs = data
105
+ return { response, data }
106
+ } catch (error) {
107
+ this.logsError = error?.name === 'TypeError'
108
+ ? networkError(error)
109
+ : genericError(error)
110
+ throw error
111
+ } finally {
112
+ this.logsLoading = false
113
+ }
114
+ },
115
+
116
+ /**
117
+ * Replace the logs state directly (e.g. when a parent action returned
118
+ * logs as a side effect).
119
+ *
120
+ * @param {unknown} data Response data (array or `{ results, ... }`).
121
+ */
122
+ setLogs(data) {
123
+ this.logs = data
124
+ },
125
+
126
+ /**
127
+ * Reset logs state back to empty defaults.
128
+ */
129
+ clearLogs() {
130
+ this.logs = []
131
+ this.logsLoading = false
132
+ this.logsError = null
133
+ },
134
+ }
135
+
136
+ const plugin = {
137
+ name: 'logs',
138
+
139
+ state: () => ({
140
+ logs: [],
141
+ logsLoading: false,
142
+ logsError: null,
143
+ }),
144
+
145
+ getters: {
146
+ getLogs: (state) => state.logs,
147
+ isLogsLoading: (state) => state.logsLoading,
148
+ getLogsError: (state) => state.logsError,
149
+ },
150
+
151
+ actions,
152
+ }
153
+
154
+ if (autoRefreshOnItemChange) {
155
+ plugin.setup = function setup(store) {
156
+ store.$onAction(({ name, after }) => {
157
+ if (name !== 'setItem') return
158
+ after(() => {
159
+ if (store.item?.id != null) {
160
+ store.refreshLogs().catch((error) => {
161
+ console.error('logsPlugin: auto-refresh failed:', error)
162
+ })
163
+ } else {
164
+ store.clearLogs()
165
+ }
166
+ })
167
+ })
168
+ }
169
+ }
170
+
171
+ return plugin
172
+ }
@@ -2,6 +2,7 @@ import { defineStore } from 'pinia'
2
2
  import { buildHeaders, buildQueryString, prefixUrl, capitalize } from '../utils/headers.js'
3
3
  import { parseResponseError, networkError, genericError } from '../utils/errors.js'
4
4
  import { extractId } from '../utils/id.js'
5
+ import { mergePluginState, mergePluginGetters, mergePluginActions } from './pluginMerge.js'
5
6
 
6
7
  /**
7
8
  * Generic Pinia store for OpenRegister object CRUD operations.
@@ -26,54 +27,6 @@ import { extractId } from '../utils/id.js'
26
27
  const DEFAULT_STORE_ID = 'conduction-objects'
27
28
  const DEFAULT_BASE_URL = '/apps/openregister/api/objects'
28
29
 
29
- /**
30
- * Merge plugin state factories into a single state object.
31
- *
32
- * @param {Array} plugins Array of plugin definitions
33
- * @return {object} Merged state object
34
- */
35
- function mergePluginState(plugins) {
36
- const merged = {}
37
- for (const plugin of plugins) {
38
- if (plugin.state) {
39
- Object.assign(merged, plugin.state())
40
- }
41
- }
42
- return merged
43
- }
44
-
45
- /**
46
- * Merge plugin getters into a single getters object.
47
- *
48
- * @param {Array} plugins Array of plugin definitions
49
- * @return {object} Merged getters object
50
- */
51
- function mergePluginGetters(plugins) {
52
- const merged = {}
53
- for (const plugin of plugins) {
54
- if (plugin.getters) {
55
- Object.assign(merged, plugin.getters)
56
- }
57
- }
58
- return merged
59
- }
60
-
61
- /**
62
- * Merge plugin actions into a single actions object.
63
- *
64
- * @param {Array} plugins Array of plugin definitions
65
- * @return {object} Merged actions object
66
- */
67
- function mergePluginActions(plugins) {
68
- const merged = {}
69
- for (const plugin of plugins) {
70
- if (plugin.actions) {
71
- Object.assign(merged, plugin.actions)
72
- }
73
- }
74
- return merged
75
- }
76
-
77
30
  // ── Base state ──────────────────────────────────────────────────────────
78
31
 
79
32
  function baseState(baseUrl = DEFAULT_BASE_URL) {
@@ -750,8 +703,10 @@ function defineObjectStore(storeId, plugins = [], baseUrl = DEFAULT_BASE_URL) {
750
703
  const pluginState = mergePluginState(plugins)
751
704
  const pluginGetters = mergePluginGetters(plugins)
752
705
  const pluginActions = mergePluginActions(plugins)
706
+ const setupPlugins = plugins.filter((p) => typeof p.setup === 'function')
707
+ const initialized = new WeakSet()
753
708
 
754
- return defineStore(storeId, {
709
+ const useStore = defineStore(storeId, {
755
710
  state: () => ({
756
711
  ...baseState(baseUrl),
757
712
  ...pluginState,
@@ -780,6 +735,21 @@ function defineObjectStore(storeId, plugins = [], baseUrl = DEFAULT_BASE_URL) {
780
735
  },
781
736
  },
782
737
  })
738
+
739
+ if (setupPlugins.length === 0) {
740
+ return useStore
741
+ }
742
+
743
+ return function useObjectStoreWithSetup(pinia) {
744
+ const store = useStore(pinia)
745
+ if (!initialized.has(store)) {
746
+ initialized.add(store)
747
+ for (const plugin of setupPlugins) {
748
+ plugin.setup(store)
749
+ }
750
+ }
751
+ return store
752
+ }
783
753
  }
784
754
 
785
755
  /**
@@ -33,3 +33,35 @@ export type { TOrganisation, TOrganisationAuthorization } from './organisation'
33
33
  export type { TFile } from './file'
34
34
  export type { TTask, TTaskPriority, TTaskStatus } from './task'
35
35
  export type { TNotification, TNotificationType, TNotificationPriority } from './notification'
36
+
37
+ // Runtime exports from the store factory. The implementation is in
38
+ // `../store/createCrudStore.js`; its companion `createCrudStore.d.ts`
39
+ // provides full generic types (entity inference, feature-flag gating,
40
+ // extend merging with correct `this` typing). We re-export both the
41
+ // factory and its supporting types so consumers can `import type
42
+ // { BaseState, MergedActions, Features } from '@conduction/nextcloud-vue'`.
43
+ export { createCrudStore } from '../store/createCrudStore'
44
+ export type {
45
+ Prettify,
46
+ EntityClass,
47
+ InferEntity,
48
+ Features,
49
+ LoadingState,
50
+ ViewModeState,
51
+ LoadingGetters,
52
+ ViewModeGetters,
53
+ ViewModeActions,
54
+ BaseState,
55
+ BaseActions,
56
+ MergedActions,
57
+ FullState,
58
+ FullGetters,
59
+ StoreThis,
60
+ ExtendConfig,
61
+ CrudConfig,
62
+ CrudPlugin,
63
+ } from '../store/createCrudStore'
64
+
65
+ // Runtime store plugins. Each plugin is a factory returning a plugin definition
66
+ // consumed by `createCrudStore({ plugins: [...] })` or `createObjectStore`.
67
+ export { logsPlugin } from '../store/plugins/logs'
@@ -323,8 +323,9 @@ export function fieldsFromSchema(schema, options = {}) {
323
323
  if (exclude.includes(key)) return false
324
324
  // Apply include whitelist
325
325
  if (include && !include.includes(key)) return false
326
- // Skip complex object types (not supported in auto-form)
327
- if (prop.type === 'object') return false
326
+ // Skip complex object types unless the caller opts in with an explicit widget
327
+ // (e.g. `widget: 'json'` or `widget: 'code'` in CnFormDialog).
328
+ if (prop.type === 'object' && !prop.widget) return false
328
329
  return true
329
330
  })
330
331
  .sort(([keyA, propA], [keyB, propB]) => {