@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.
Files changed (152) hide show
  1. package/README.md +226 -226
  2. package/dist/nextcloud-vue.cjs.js +60455 -8755
  3. package/dist/nextcloud-vue.cjs.js.map +1 -1
  4. package/dist/nextcloud-vue.css +2062 -528
  5. package/dist/nextcloud-vue.esm.js +60411 -8731
  6. package/dist/nextcloud-vue.esm.js.map +1 -1
  7. package/package.json +75 -62
  8. package/src/components/CnActionsBar/CnActionsBar.vue +235 -225
  9. package/src/components/CnActionsBar/index.js +1 -1
  10. package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +579 -0
  11. package/src/components/CnAdvancedFormDialog/CnDataTab.vue +217 -0
  12. package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +121 -0
  13. package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +418 -0
  14. package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +247 -0
  15. package/src/components/CnAdvancedFormDialog/index.js +1 -0
  16. package/src/components/CnCardGrid/CnCardGrid.vue +152 -152
  17. package/src/components/CnCardGrid/index.js +1 -1
  18. package/src/components/CnCellRenderer/CnCellRenderer.vue +132 -132
  19. package/src/components/CnCellRenderer/index.js +1 -1
  20. package/src/components/CnChartWidget/CnChartWidget.vue +320 -0
  21. package/src/components/CnChartWidget/index.js +1 -0
  22. package/src/components/CnConfigurationCard/CnConfigurationCard.vue +77 -77
  23. package/src/components/CnConfigurationCard/index.js +1 -1
  24. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +225 -0
  25. package/src/components/CnDashboardGrid/index.js +1 -0
  26. package/src/components/CnDashboardPage/CnDashboardPage.vue +390 -0
  27. package/src/components/CnDashboardPage/index.js +1 -0
  28. package/src/components/CnDataTable/CnDataTable.vue +349 -349
  29. package/src/components/CnDataTable/index.js +1 -1
  30. package/src/components/CnDetailCard/CnDetailCard.vue +214 -0
  31. package/src/components/CnDetailCard/index.js +1 -0
  32. package/src/components/CnDetailPage/CnDetailPage.vue +281 -0
  33. package/src/components/CnDetailPage/index.js +1 -0
  34. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +231 -223
  35. package/src/components/CnFacetSidebar/index.js +1 -1
  36. package/src/components/CnFilterBar/CnFilterBar.vue +152 -152
  37. package/src/components/CnFilterBar/index.js +1 -1
  38. package/src/components/CnIcon/CnIcon.vue +89 -89
  39. package/src/components/CnIcon/index.js +1 -1
  40. package/src/components/CnIndexPage/CnIndexPage.vue +874 -816
  41. package/src/components/CnIndexPage/index.js +1 -1
  42. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +503 -484
  43. package/src/components/CnIndexSidebar/index.js +1 -1
  44. package/src/components/CnItemCard/CnItemCard.vue +132 -0
  45. package/src/components/CnItemCard/index.js +1 -0
  46. package/src/components/CnKpiGrid/CnKpiGrid.vue +89 -89
  47. package/src/components/CnKpiGrid/index.js +1 -1
  48. package/src/components/CnMassActionBar/CnMassActionBar.vue +160 -160
  49. package/src/components/CnMassActionBar/index.js +1 -1
  50. package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +320 -320
  51. package/src/components/CnMassCopyDialog/index.js +1 -1
  52. package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +238 -238
  53. package/src/components/CnMassDeleteDialog/index.js +1 -1
  54. package/src/components/CnMassExportDialog/CnMassExportDialog.vue +190 -190
  55. package/src/components/CnMassExportDialog/index.js +1 -1
  56. package/src/components/CnMassImportDialog/CnMassImportDialog.vue +491 -491
  57. package/src/components/CnMassImportDialog/index.js +1 -1
  58. package/src/components/CnNoteCard/CnNoteCard.vue +149 -0
  59. package/src/components/CnNoteCard/index.js +1 -0
  60. package/src/components/CnNotesCard/CnNotesCard.vue +413 -0
  61. package/src/components/CnNotesCard/index.js +1 -0
  62. package/src/components/CnObjectCard/CnObjectCard.vue +292 -292
  63. package/src/components/CnObjectCard/index.js +1 -1
  64. package/src/components/CnObjectSidebar/CnObjectSidebar.vue +876 -0
  65. package/src/components/CnObjectSidebar/index.js +1 -0
  66. package/src/components/CnPageHeader/CnPageHeader.vue +57 -57
  67. package/src/components/CnPageHeader/index.js +1 -1
  68. package/src/components/CnPagination/CnPagination.vue +252 -252
  69. package/src/components/CnPagination/index.js +1 -1
  70. package/src/components/CnRowActions/CnRowActions.vue +73 -73
  71. package/src/components/CnRowActions/index.js +1 -1
  72. package/src/components/CnSchemaFormDialog/CnSchemaConfigurationTab.vue +226 -0
  73. package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +787 -0
  74. package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +305 -0
  75. package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +1398 -0
  76. package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +236 -0
  77. package/src/components/CnSchemaFormDialog/index.js +1 -0
  78. package/src/components/CnSettingsCard/CnSettingsCard.vue +92 -92
  79. package/src/components/CnSettingsCard/index.js +1 -1
  80. package/src/components/CnSettingsSection/CnSettingsSection.vue +266 -266
  81. package/src/components/CnSettingsSection/index.js +1 -1
  82. package/src/components/CnStatsBlock/CnStatsBlock.vue +420 -366
  83. package/src/components/CnStatsBlock/index.js +1 -1
  84. package/src/components/CnStatusBadge/CnStatusBadge.vue +77 -77
  85. package/src/components/CnStatusBadge/index.js +1 -1
  86. package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +540 -0
  87. package/src/components/CnTabbedFormDialog/index.js +1 -0
  88. package/src/components/CnTasksCard/CnTasksCard.vue +373 -0
  89. package/src/components/CnTasksCard/index.js +1 -0
  90. package/src/components/CnTileWidget/CnTileWidget.vue +159 -0
  91. package/src/components/CnTileWidget/index.js +1 -0
  92. package/src/components/CnTimelineStages/CnTimelineStages.vue +292 -0
  93. package/src/components/CnTimelineStages/index.js +1 -0
  94. package/src/components/CnUserActionMenu/CnUserActionMenu.vue +435 -0
  95. package/src/components/CnUserActionMenu/index.js +1 -0
  96. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +312 -312
  97. package/src/components/CnVersionInfoCard/index.js +1 -1
  98. package/src/components/CnWidgetRenderer/CnWidgetRenderer.vue +180 -0
  99. package/src/components/CnWidgetRenderer/index.js +1 -0
  100. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +211 -0
  101. package/src/components/CnWidgetWrapper/index.js +1 -0
  102. package/src/components/index.js +43 -29
  103. package/src/composables/index.js +4 -3
  104. package/src/composables/useDashboardView.js +240 -0
  105. package/src/composables/useDetailView.js +289 -132
  106. package/src/composables/useListView.js +363 -362
  107. package/src/composables/useSubResource.js +142 -142
  108. package/src/constants/metadata.js +30 -30
  109. package/src/css/CnSchemaFormDialog.css +546 -0
  110. package/src/css/__sample_nextcloud_tokens.css +110 -0
  111. package/src/css/actions-bar.css +48 -48
  112. package/src/css/badge.css +51 -51
  113. package/src/css/card.css +128 -128
  114. package/src/css/dashboard.css +70 -0
  115. package/src/css/detail-page.css +168 -0
  116. package/src/css/detail.css +68 -68
  117. package/src/css/index-page.css +44 -32
  118. package/src/css/index-sidebar.css +193 -187
  119. package/src/css/index.css +16 -12
  120. package/src/css/layout.css +90 -90
  121. package/src/css/page-header.css +33 -33
  122. package/src/css/pagination.css +72 -72
  123. package/src/css/table.css +142 -142
  124. package/src/css/timeline-stages.css +218 -0
  125. package/src/css/utilities.css +46 -46
  126. package/src/index.js +72 -53
  127. package/src/store/createSubResourcePlugin.js +135 -135
  128. package/src/store/index.js +3 -3
  129. package/src/store/plugins/auditTrails.js +17 -17
  130. package/src/store/plugins/files.js +250 -186
  131. package/src/store/plugins/index.js +7 -5
  132. package/src/store/plugins/lifecycle.js +180 -180
  133. package/src/store/plugins/relations.js +68 -68
  134. package/src/store/plugins/search.js +372 -0
  135. package/src/store/plugins/selection.js +104 -0
  136. package/src/store/useObjectStore.js +829 -686
  137. package/src/types/auditTrail.d.ts +32 -32
  138. package/src/types/file.d.ts +23 -23
  139. package/src/types/index.d.ts +35 -35
  140. package/src/types/notification.d.ts +36 -36
  141. package/src/types/object.d.ts +40 -40
  142. package/src/types/organisation.d.ts +41 -41
  143. package/src/types/register.d.ts +25 -25
  144. package/src/types/schema.d.ts +39 -39
  145. package/src/types/shared.d.ts +79 -79
  146. package/src/types/source.d.ts +14 -14
  147. package/src/types/task.d.ts +31 -31
  148. package/src/utils/errors.js +96 -96
  149. package/src/utils/headers.js +68 -50
  150. package/src/utils/id.js +13 -0
  151. package/src/utils/index.js +3 -3
  152. package/src/utils/schema.js +422 -419
@@ -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
+ }
@@ -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
+ }
@@ -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'
@@ -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
+ }
@@ -1,50 +1,68 @@
1
- /**
2
- * Build standard Nextcloud request headers for API calls.
3
- *
4
- * Includes the CSRF request token and OCS API request header
5
- * required by Nextcloud's API layer.
6
- *
7
- * @param {string} [contentType='application/json'] Content-Type header value
8
- * @return {object} Headers object for use with fetch()
9
- */
10
- export function buildHeaders(contentType = 'application/json') {
11
- const headers = {
12
- requesttoken: OC.requestToken,
13
- 'OCS-APIREQUEST': 'true',
14
- }
15
- if (contentType) {
16
- headers['Content-Type'] = contentType
17
- }
18
- return headers
19
- }
20
-
21
- /**
22
- * Build a query string from a params object.
23
- *
24
- * Handles _order serialization (JSON.stringify for objects) and skips
25
- * null/undefined/empty values.
26
- *
27
- * @param {object} params Key-value pairs for query parameters
28
- * @return {string} Query string including leading '?' or empty string
29
- */
30
- export function buildQueryString(params = {}) {
31
- const queryParams = new URLSearchParams()
32
-
33
- for (const [key, value] of Object.entries(params)) {
34
- if (value === undefined || value === null || value === '') continue
35
- if (Array.isArray(value)) {
36
- for (const item of value) {
37
- if (item !== undefined && item !== null && item !== '') {
38
- queryParams.append(key, String(item))
39
- }
40
- }
41
- } else if (key === '_order' && typeof value === 'object') {
42
- queryParams.set(key, JSON.stringify(value))
43
- } else {
44
- queryParams.set(key, String(value))
45
- }
46
- }
47
-
48
- const str = queryParams.toString()
49
- return str ? '?' + str : ''
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
+ }
@@ -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
+ }
@@ -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'