@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.
- package/dist/nextcloud-vue.cjs.js +64663 -63467
- package/dist/nextcloud-vue.cjs.js.map +1 -1
- package/dist/nextcloud-vue.css +443 -444
- package/dist/nextcloud-vue.esm.js +64637 -63443
- package/dist/nextcloud-vue.esm.js.map +1 -1
- package/l10n/en.json +164 -0
- package/l10n/nl.json +164 -0
- package/package.json +19 -3
- package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +8 -7
- package/src/components/CnAdvancedFormDialog/CnDataTab.vue +2 -2
- package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +5 -5
- package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +2 -2
- package/src/components/CnCardGrid/CnCardGrid.vue +2 -1
- package/src/components/CnChartWidget/CnChartWidget.vue +29 -1
- package/src/components/CnCopyDialog/CnCopyDialog.vue +15 -6
- package/src/components/CnDashboardPage/CnDashboardPage.vue +5 -4
- package/src/components/CnDetailGrid/CnDetailGrid.vue +3 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +5 -4
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +3 -2
- package/src/components/CnFilterBar/CnFilterBar.vue +3 -2
- package/src/components/CnFormDialog/CnFormDialog.vue +122 -9
- package/src/components/CnIndexPage/CnIndexPage.vue +1 -0
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +8 -7
- package/src/components/CnJsonViewer/CnJsonViewer.vue +33 -4
- package/src/components/CnMassActionBar/CnMassActionBar.vue +1 -1
- package/src/components/CnMassImportDialog/CnMassImportDialog.vue +2 -2
- package/src/components/CnNotesCard/CnNotesCard.vue +7 -6
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +11 -10
- package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +3 -2
- package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +8 -7
- package/src/components/CnObjectSidebar/CnFilesTab.vue +6 -5
- package/src/components/CnObjectSidebar/CnNotesTab.vue +8 -7
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +6 -5
- package/src/components/CnObjectSidebar/CnTagsTab.vue +3 -2
- package/src/components/CnObjectSidebar/CnTasksTab.vue +11 -10
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +14 -13
- package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +15 -14
- package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +4 -4
- package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +10 -10
- package/src/components/CnSettingsSection/CnSettingsSection.vue +5 -4
- package/src/components/CnStatsBlock/CnStatsBlock.vue +5 -4
- package/src/components/CnStatsPanel/CnStatsPanel.vue +3 -2
- package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +9 -8
- package/src/components/CnTableWidget/CnTableWidget.vue +3 -2
- package/src/components/CnTasksCard/CnTasksCard.vue +5 -4
- package/src/components/CnTimelineStages/CnTimelineStages.vue +3 -1
- package/src/components/CnUserActionMenu/CnUserActionMenu.vue +7 -6
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +4 -3
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +3 -1
- package/src/index.js +4 -0
- package/src/l10n/index.js +12 -0
- package/src/store/createCrudStore.d.ts +350 -0
- package/src/store/createCrudStore.js +58 -5
- package/src/store/pluginMerge.js +55 -0
- package/src/store/plugins/index.js +1 -0
- package/src/store/plugins/logs.d.ts +22 -0
- package/src/store/plugins/logs.js +172 -0
- package/src/store/useObjectStore.js +19 -49
- package/src/types/index.d.ts +32 -0
- 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
|
-
|
|
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
|
/**
|
package/src/types/index.d.ts
CHANGED
|
@@ -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'
|
package/src/utils/schema.js
CHANGED
|
@@ -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
|
|
327
|
-
|
|
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]) => {
|