@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
package/src/types/shared.d.ts
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared utility types used across OpenRegister entities.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/** Standard paginated API response shape. */
|
|
6
|
-
export interface TPaginated<T = unknown> {
|
|
7
|
-
results: T[]
|
|
8
|
-
total: number
|
|
9
|
-
page: number
|
|
10
|
-
pages: number
|
|
11
|
-
limit: number
|
|
12
|
-
offset: number
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** Empty paginated response (used as initial state). */
|
|
16
|
-
export type TEmptyPaginated = TPaginated<never>
|
|
17
|
-
|
|
18
|
-
/** Resource quota limits. Used by registers, organisations, views, applications. */
|
|
19
|
-
export interface TQuota {
|
|
20
|
-
storage?: number | null
|
|
21
|
-
bandwidth?: number | null
|
|
22
|
-
requests?: number | null
|
|
23
|
-
users?: number | null
|
|
24
|
-
groups?: number | null
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Resource usage tracking. Mirrors TQuota but values are always numbers. */
|
|
28
|
-
export interface TUsage {
|
|
29
|
-
storage?: number
|
|
30
|
-
bandwidth?: number
|
|
31
|
-
requests?: number
|
|
32
|
-
users?: number
|
|
33
|
-
groups?: number
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Aggregate statistics for objects, logs, and files. Used by schemas and registers. */
|
|
37
|
-
export interface TEntityStats {
|
|
38
|
-
objects: {
|
|
39
|
-
total: number
|
|
40
|
-
size: number
|
|
41
|
-
invalid: number
|
|
42
|
-
deleted: number
|
|
43
|
-
locked: number
|
|
44
|
-
published: number
|
|
45
|
-
}
|
|
46
|
-
logs: {
|
|
47
|
-
total: number
|
|
48
|
-
size: number
|
|
49
|
-
}
|
|
50
|
-
files: {
|
|
51
|
-
total: number
|
|
52
|
-
size: number
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** CRUD authorization configuration per entity type. */
|
|
57
|
-
export interface TCrudAuthorization {
|
|
58
|
-
create?: string[]
|
|
59
|
-
read?: string[]
|
|
60
|
-
update?: string[]
|
|
61
|
-
delete?: string[]
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Unified API error shape returned by all store actions. */
|
|
65
|
-
export interface TApiError {
|
|
66
|
-
status: number | null
|
|
67
|
-
message: string
|
|
68
|
-
details: unknown | null
|
|
69
|
-
isValidation: boolean
|
|
70
|
-
fields: Record<string, string> | null
|
|
71
|
-
toString(): string
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Object path reference for routing. */
|
|
75
|
-
export interface TObjectPath {
|
|
76
|
-
register: string
|
|
77
|
-
schema: string
|
|
78
|
-
objectId?: string
|
|
79
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Shared utility types used across OpenRegister entities.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Standard paginated API response shape. */
|
|
6
|
+
export interface TPaginated<T = unknown> {
|
|
7
|
+
results: T[]
|
|
8
|
+
total: number
|
|
9
|
+
page: number
|
|
10
|
+
pages: number
|
|
11
|
+
limit: number
|
|
12
|
+
offset: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Empty paginated response (used as initial state). */
|
|
16
|
+
export type TEmptyPaginated = TPaginated<never>
|
|
17
|
+
|
|
18
|
+
/** Resource quota limits. Used by registers, organisations, views, applications. */
|
|
19
|
+
export interface TQuota {
|
|
20
|
+
storage?: number | null
|
|
21
|
+
bandwidth?: number | null
|
|
22
|
+
requests?: number | null
|
|
23
|
+
users?: number | null
|
|
24
|
+
groups?: number | null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Resource usage tracking. Mirrors TQuota but values are always numbers. */
|
|
28
|
+
export interface TUsage {
|
|
29
|
+
storage?: number
|
|
30
|
+
bandwidth?: number
|
|
31
|
+
requests?: number
|
|
32
|
+
users?: number
|
|
33
|
+
groups?: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Aggregate statistics for objects, logs, and files. Used by schemas and registers. */
|
|
37
|
+
export interface TEntityStats {
|
|
38
|
+
objects: {
|
|
39
|
+
total: number
|
|
40
|
+
size: number
|
|
41
|
+
invalid: number
|
|
42
|
+
deleted: number
|
|
43
|
+
locked: number
|
|
44
|
+
published: number
|
|
45
|
+
}
|
|
46
|
+
logs: {
|
|
47
|
+
total: number
|
|
48
|
+
size: number
|
|
49
|
+
}
|
|
50
|
+
files: {
|
|
51
|
+
total: number
|
|
52
|
+
size: number
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** CRUD authorization configuration per entity type. */
|
|
57
|
+
export interface TCrudAuthorization {
|
|
58
|
+
create?: string[]
|
|
59
|
+
read?: string[]
|
|
60
|
+
update?: string[]
|
|
61
|
+
delete?: string[]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Unified API error shape returned by all store actions. */
|
|
65
|
+
export interface TApiError {
|
|
66
|
+
status: number | null
|
|
67
|
+
message: string
|
|
68
|
+
details: unknown | null
|
|
69
|
+
isValidation: boolean
|
|
70
|
+
fields: Record<string, string> | null
|
|
71
|
+
toString(): string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Object path reference for routing. */
|
|
75
|
+
export interface TObjectPath {
|
|
76
|
+
register: string
|
|
77
|
+
schema: string
|
|
78
|
+
objectId?: string
|
|
79
|
+
}
|
package/src/types/source.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenRegister Source entity type.
|
|
3
|
-
*
|
|
4
|
-
* A data source backing a register (database connection).
|
|
5
|
-
*/
|
|
6
|
-
export interface TSource {
|
|
7
|
-
id?: string | number
|
|
8
|
-
title: string
|
|
9
|
-
description: string
|
|
10
|
-
databaseUrl: string
|
|
11
|
-
type: 'internal' | 'mongodb'
|
|
12
|
-
updated: string
|
|
13
|
-
created: string
|
|
14
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* OpenRegister Source entity type.
|
|
3
|
+
*
|
|
4
|
+
* A data source backing a register (database connection).
|
|
5
|
+
*/
|
|
6
|
+
export interface TSource {
|
|
7
|
+
id?: string | number
|
|
8
|
+
title: string
|
|
9
|
+
description: string
|
|
10
|
+
databaseUrl: string
|
|
11
|
+
type: 'internal' | 'mongodb'
|
|
12
|
+
updated: string
|
|
13
|
+
created: string
|
|
14
|
+
}
|
package/src/types/task.d.ts
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenRegister Task entity type.
|
|
3
|
-
*
|
|
4
|
-
* Tasks are CalDAV-based (iCalendar VTODO) and linked to objects.
|
|
5
|
-
* Fetched via the /tasks sub-resource endpoint and normalized from
|
|
6
|
-
* iCal format to this frontend-friendly shape.
|
|
7
|
-
*/
|
|
8
|
-
export interface TTask {
|
|
9
|
-
id: string
|
|
10
|
-
uid?: string
|
|
11
|
-
calendarId?: string
|
|
12
|
-
taskUri?: string
|
|
13
|
-
title: string
|
|
14
|
-
description?: string
|
|
15
|
-
reference?: string
|
|
16
|
-
objectUuid?: string
|
|
17
|
-
deadline?: string | null
|
|
18
|
-
daysText?: string
|
|
19
|
-
isOverdue?: boolean
|
|
20
|
-
isCompleted?: boolean
|
|
21
|
-
priority: TTaskPriority
|
|
22
|
-
status: TTaskStatus
|
|
23
|
-
created?: string
|
|
24
|
-
updated?: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** CalDAV task priority levels (mapped from iCal 0-9 range). */
|
|
28
|
-
export type TTaskPriority = 'urgent' | 'high' | 'normal' | 'low'
|
|
29
|
-
|
|
30
|
-
/** CalDAV task status values. */
|
|
31
|
-
export type TTaskStatus = 'needs-action' | 'in-process' | 'completed' | 'cancelled'
|
|
1
|
+
/**
|
|
2
|
+
* OpenRegister Task entity type.
|
|
3
|
+
*
|
|
4
|
+
* Tasks are CalDAV-based (iCalendar VTODO) and linked to objects.
|
|
5
|
+
* Fetched via the /tasks sub-resource endpoint and normalized from
|
|
6
|
+
* iCal format to this frontend-friendly shape.
|
|
7
|
+
*/
|
|
8
|
+
export interface TTask {
|
|
9
|
+
id: string
|
|
10
|
+
uid?: string
|
|
11
|
+
calendarId?: string
|
|
12
|
+
taskUri?: string
|
|
13
|
+
title: string
|
|
14
|
+
description?: string
|
|
15
|
+
reference?: string
|
|
16
|
+
objectUuid?: string
|
|
17
|
+
deadline?: string | null
|
|
18
|
+
daysText?: string
|
|
19
|
+
isOverdue?: boolean
|
|
20
|
+
isCompleted?: boolean
|
|
21
|
+
priority: TTaskPriority
|
|
22
|
+
status: TTaskStatus
|
|
23
|
+
created?: string
|
|
24
|
+
updated?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** CalDAV task priority levels (mapped from iCal 0-9 range). */
|
|
28
|
+
export type TTaskPriority = 'urgent' | 'high' | 'normal' | 'low'
|
|
29
|
+
|
|
30
|
+
/** CalDAV task status values. */
|
|
31
|
+
export type TTaskStatus = 'needs-action' | 'in-process' | 'completed' | 'cancelled'
|
package/src/utils/errors.js
CHANGED
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unified error shape returned by all store actions.
|
|
3
|
-
*
|
|
4
|
-
* @typedef {object} ApiError
|
|
5
|
-
* @property {number} status HTTP status code (0 for network errors)
|
|
6
|
-
* @property {string} message Human-readable error message
|
|
7
|
-
* @property {object|null} details Validation errors or additional details
|
|
8
|
-
* @property {boolean} isValidation Whether this is a validation error (400/422)
|
|
9
|
-
* @property {object|null} fields Per-field validation errors
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Parse an HTTP error response into a unified ApiError shape.
|
|
14
|
-
*
|
|
15
|
-
* Merges the best of Pipelinq's _parseResponseError (field extraction)
|
|
16
|
-
* and Procest's _parseError (status-specific messages, isValidation flag).
|
|
17
|
-
*
|
|
18
|
-
* @param {Response} response The fetch Response object
|
|
19
|
-
* @param {string} type The object type slug (used in error messages)
|
|
20
|
-
* @return {Promise<ApiError>} Parsed error object
|
|
21
|
-
*/
|
|
22
|
-
export async function parseResponseError(response, type) {
|
|
23
|
-
const status = response.status
|
|
24
|
-
let details = null
|
|
25
|
-
let fields = null
|
|
26
|
-
let message = ''
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const body = await response.json()
|
|
30
|
-
details = body.errors || body.error || body.message || null
|
|
31
|
-
fields = body.validationErrors || body.errors || null
|
|
32
|
-
} catch {
|
|
33
|
-
// Response body is not JSON
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
switch (true) {
|
|
37
|
-
case status === 400 || status === 422:
|
|
38
|
-
message = details && typeof details === 'string'
|
|
39
|
-
? details
|
|
40
|
-
: `Validation failed for ${type}`
|
|
41
|
-
return { status, message, details, isValidation: true, fields, toString() { return this.message } }
|
|
42
|
-
case status === 401:
|
|
43
|
-
message = 'Session expired, please log in again'
|
|
44
|
-
break
|
|
45
|
-
case status === 403:
|
|
46
|
-
message = 'You do not have permission to perform this action'
|
|
47
|
-
break
|
|
48
|
-
case status === 404:
|
|
49
|
-
message = `The requested ${type} could not be found`
|
|
50
|
-
break
|
|
51
|
-
case status === 409:
|
|
52
|
-
message = `This ${type} was modified by another user. Please reload.`
|
|
53
|
-
break
|
|
54
|
-
case status >= 500:
|
|
55
|
-
message = 'An unexpected server error occurred. Please try again.'
|
|
56
|
-
break
|
|
57
|
-
default:
|
|
58
|
-
message = response.statusText || 'An unexpected error occurred'
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return { status, message, details, isValidation: false, fields, toString() { return this.message } }
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Create a network error object for fetch failures (no response).
|
|
66
|
-
*
|
|
67
|
-
* @param {Error} error The caught error
|
|
68
|
-
* @return {ApiError} Network error object
|
|
69
|
-
*/
|
|
70
|
-
export function networkError(error) {
|
|
71
|
-
return {
|
|
72
|
-
status: 0,
|
|
73
|
-
message: error.message || 'A network error occurred. Check your connection and try again.',
|
|
74
|
-
details: null,
|
|
75
|
-
isValidation: false,
|
|
76
|
-
fields: null,
|
|
77
|
-
toString() { return this.message },
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Create a generic error object from a caught exception.
|
|
83
|
-
*
|
|
84
|
-
* @param {Error} error The caught error
|
|
85
|
-
* @return {ApiError} Generic error object
|
|
86
|
-
*/
|
|
87
|
-
export function genericError(error) {
|
|
88
|
-
return {
|
|
89
|
-
status: null,
|
|
90
|
-
message: error.message,
|
|
91
|
-
details: null,
|
|
92
|
-
isValidation: false,
|
|
93
|
-
fields: null,
|
|
94
|
-
toString() { return this.message },
|
|
95
|
-
}
|
|
96
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Unified error shape returned by all store actions.
|
|
3
|
+
*
|
|
4
|
+
* @typedef {object} ApiError
|
|
5
|
+
* @property {number} status HTTP status code (0 for network errors)
|
|
6
|
+
* @property {string} message Human-readable error message
|
|
7
|
+
* @property {object|null} details Validation errors or additional details
|
|
8
|
+
* @property {boolean} isValidation Whether this is a validation error (400/422)
|
|
9
|
+
* @property {object|null} fields Per-field validation errors
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parse an HTTP error response into a unified ApiError shape.
|
|
14
|
+
*
|
|
15
|
+
* Merges the best of Pipelinq's _parseResponseError (field extraction)
|
|
16
|
+
* and Procest's _parseError (status-specific messages, isValidation flag).
|
|
17
|
+
*
|
|
18
|
+
* @param {Response} response The fetch Response object
|
|
19
|
+
* @param {string} type The object type slug (used in error messages)
|
|
20
|
+
* @return {Promise<ApiError>} Parsed error object
|
|
21
|
+
*/
|
|
22
|
+
export async function parseResponseError(response, type) {
|
|
23
|
+
const status = response.status
|
|
24
|
+
let details = null
|
|
25
|
+
let fields = null
|
|
26
|
+
let message = ''
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const body = await response.json()
|
|
30
|
+
details = body.errors || body.error || body.message || null
|
|
31
|
+
fields = body.validationErrors || body.errors || null
|
|
32
|
+
} catch {
|
|
33
|
+
// Response body is not JSON
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
switch (true) {
|
|
37
|
+
case status === 400 || status === 422:
|
|
38
|
+
message = details && typeof details === 'string'
|
|
39
|
+
? details
|
|
40
|
+
: `Validation failed for ${type}`
|
|
41
|
+
return { status, message, details, isValidation: true, fields, toString() { return this.message } }
|
|
42
|
+
case status === 401:
|
|
43
|
+
message = 'Session expired, please log in again'
|
|
44
|
+
break
|
|
45
|
+
case status === 403:
|
|
46
|
+
message = 'You do not have permission to perform this action'
|
|
47
|
+
break
|
|
48
|
+
case status === 404:
|
|
49
|
+
message = `The requested ${type} could not be found`
|
|
50
|
+
break
|
|
51
|
+
case status === 409:
|
|
52
|
+
message = `This ${type} was modified by another user. Please reload.`
|
|
53
|
+
break
|
|
54
|
+
case status >= 500:
|
|
55
|
+
message = 'An unexpected server error occurred. Please try again.'
|
|
56
|
+
break
|
|
57
|
+
default:
|
|
58
|
+
message = response.statusText || 'An unexpected error occurred'
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { status, message, details, isValidation: false, fields, toString() { return this.message } }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create a network error object for fetch failures (no response).
|
|
66
|
+
*
|
|
67
|
+
* @param {Error} error The caught error
|
|
68
|
+
* @return {ApiError} Network error object
|
|
69
|
+
*/
|
|
70
|
+
export function networkError(error) {
|
|
71
|
+
return {
|
|
72
|
+
status: 0,
|
|
73
|
+
message: error.message || 'A network error occurred. Check your connection and try again.',
|
|
74
|
+
details: null,
|
|
75
|
+
isValidation: false,
|
|
76
|
+
fields: null,
|
|
77
|
+
toString() { return this.message },
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Create a generic error object from a caught exception.
|
|
83
|
+
*
|
|
84
|
+
* @param {Error} error The caught error
|
|
85
|
+
* @return {ApiError} Generic error object
|
|
86
|
+
*/
|
|
87
|
+
export function genericError(error) {
|
|
88
|
+
return {
|
|
89
|
+
status: null,
|
|
90
|
+
message: error.message,
|
|
91
|
+
details: null,
|
|
92
|
+
isValidation: false,
|
|
93
|
+
fields: null,
|
|
94
|
+
toString() { return this.message },
|
|
95
|
+
}
|
|
96
|
+
}
|
package/src/utils/headers.js
CHANGED
|
@@ -1,50 +1,68 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* @
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Prepend `/index.php` to a URL path when the current page is served via index.php.
|
|
3
|
+
* Unless the given path already contains `/index.php`.
|
|
4
|
+
*
|
|
5
|
+
* Nextcloud can be hosted with or without index.php in the URL. API calls must
|
|
6
|
+
* use the same prefix as the page, otherwise the request is rejected.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} path URL path (e.g. '/apps/openregister/api/objects')
|
|
9
|
+
* @return {string} Path with optional /index.php prefix
|
|
10
|
+
*/
|
|
11
|
+
export function prefixUrl(path) {
|
|
12
|
+
if (path.startsWith('/index.php')) return path
|
|
13
|
+
if (typeof window !== 'undefined' && window.location.pathname.includes('/index.php')) {
|
|
14
|
+
return `/index.php${path}`
|
|
15
|
+
}
|
|
16
|
+
return path
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build standard Nextcloud request headers for API calls.
|
|
21
|
+
*
|
|
22
|
+
* Includes the CSRF request token and OCS API request header
|
|
23
|
+
* required by Nextcloud's API layer.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} [contentType] Content-Type header value
|
|
26
|
+
* @return {object} Headers object for use with fetch()
|
|
27
|
+
*/
|
|
28
|
+
export function buildHeaders(contentType = 'application/json') {
|
|
29
|
+
const headers = {
|
|
30
|
+
requesttoken: OC.requestToken,
|
|
31
|
+
'OCS-APIREQUEST': 'true',
|
|
32
|
+
}
|
|
33
|
+
if (contentType) {
|
|
34
|
+
headers['Content-Type'] = contentType
|
|
35
|
+
}
|
|
36
|
+
return headers
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build a query string from a params object.
|
|
41
|
+
*
|
|
42
|
+
* Handles _order serialization (JSON.stringify for objects) and skips
|
|
43
|
+
* null/undefined/empty values.
|
|
44
|
+
*
|
|
45
|
+
* @param {object} params Key-value pairs for query parameters
|
|
46
|
+
* @return {string} Query string including leading '?' or empty string
|
|
47
|
+
*/
|
|
48
|
+
export function buildQueryString(params = {}) {
|
|
49
|
+
const queryParams = new URLSearchParams()
|
|
50
|
+
|
|
51
|
+
for (const [key, value] of Object.entries(params)) {
|
|
52
|
+
if (value === undefined || value === null || value === '') continue
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
for (const item of value) {
|
|
55
|
+
if (item !== undefined && item !== null && item !== '') {
|
|
56
|
+
queryParams.append(key, String(item))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} else if (key === '_order' && typeof value === 'object') {
|
|
60
|
+
queryParams.set(key, JSON.stringify(value))
|
|
61
|
+
} else {
|
|
62
|
+
queryParams.set(key, String(value))
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const str = queryParams.toString()
|
|
67
|
+
return str ? '?' + str : ''
|
|
68
|
+
}
|
package/src/utils/id.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract the ID from a given value.
|
|
3
|
+
*
|
|
4
|
+
* primarily supports objects with a id or \@self.id property
|
|
5
|
+
* @param {string|object} value - object value
|
|
6
|
+
* @return {string} id
|
|
7
|
+
*/
|
|
8
|
+
export const extractId = (value) => {
|
|
9
|
+
if (typeof value === 'string') return value
|
|
10
|
+
if (typeof value === 'object' && value?.id) return value.id
|
|
11
|
+
if (typeof value === 'object' && value?.['@self']?.id) { return value['@self'].id }
|
|
12
|
+
return value
|
|
13
|
+
}
|
package/src/utils/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { buildHeaders, buildQueryString } from './headers.js'
|
|
2
|
-
export { parseResponseError, networkError, genericError } from './errors.js'
|
|
3
|
-
export { columnsFromSchema, formatValue, filtersFromSchema, fieldsFromSchema } from './schema.js'
|
|
1
|
+
export { buildHeaders, buildQueryString } from './headers.js'
|
|
2
|
+
export { parseResponseError, networkError, genericError } from './errors.js'
|
|
3
|
+
export { columnsFromSchema, formatValue, filtersFromSchema, fieldsFromSchema } from './schema.js'
|