@commercetools-frontend-extensions/operations 0.0.0-canary-20251209161906

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 (180) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +178 -0
  3. package/babel.config.js +6 -0
  4. package/dist/commercetools-frontend-extensions-operations.cjs.d.ts +2 -0
  5. package/dist/commercetools-frontend-extensions-operations.cjs.dev.js +3273 -0
  6. package/dist/commercetools-frontend-extensions-operations.cjs.js +7 -0
  7. package/dist/commercetools-frontend-extensions-operations.cjs.prod.js +3265 -0
  8. package/dist/commercetools-frontend-extensions-operations.esm.js +3074 -0
  9. package/dist/declarations/src/@api/export-operations.d.ts +5 -0
  10. package/dist/declarations/src/@api/fetcher.d.ts +17 -0
  11. package/dist/declarations/src/@api/file-import-jobs.d.ts +7 -0
  12. package/dist/declarations/src/@api/file-upload.d.ts +3 -0
  13. package/dist/declarations/src/@api/import-containers.d.ts +35 -0
  14. package/dist/declarations/src/@api/import-operations.d.ts +6 -0
  15. package/dist/declarations/src/@api/index.d.ts +9 -0
  16. package/dist/declarations/src/@api/process-file.d.ts +3 -0
  17. package/dist/declarations/src/@api/test-fixtures.d.ts +279 -0
  18. package/dist/declarations/src/@api/urls.d.ts +74 -0
  19. package/dist/declarations/src/@components/file-drop-area/active-drag-drop-area.d.ts +10 -0
  20. package/dist/declarations/src/@components/file-drop-area/disabled-drop-area.d.ts +5 -0
  21. package/dist/declarations/src/@components/file-drop-area/drop-area-wrapper.d.ts +11 -0
  22. package/dist/declarations/src/@components/file-drop-area/enabled-drop-area.d.ts +7 -0
  23. package/dist/declarations/src/@components/file-drop-area/file-drop-area.d.ts +14 -0
  24. package/dist/declarations/src/@components/file-drop-area/file-dropped-area.d.ts +6 -0
  25. package/dist/declarations/src/@components/file-drop-area/index.d.ts +7 -0
  26. package/dist/declarations/src/@components/file-drop-area/styles.d.ts +9 -0
  27. package/dist/declarations/src/@components/icons/file-icon.d.ts +2 -0
  28. package/dist/declarations/src/@components/icons/index.d.ts +2 -0
  29. package/dist/declarations/src/@components/icons/lock-icon.d.ts +2 -0
  30. package/dist/declarations/src/@components/index.d.ts +6 -0
  31. package/dist/declarations/src/@components/info-box/index.d.ts +1 -0
  32. package/dist/declarations/src/@components/info-box/info-box.d.ts +7 -0
  33. package/dist/declarations/src/@components/upload-separator/index.d.ts +1 -0
  34. package/dist/declarations/src/@components/upload-separator/upload-separator.d.ts +12 -0
  35. package/dist/declarations/src/@components/upload-settings/index.d.ts +1 -0
  36. package/dist/declarations/src/@components/upload-settings/upload-settings.d.ts +11 -0
  37. package/dist/declarations/src/@components/uploading-modal/index.d.ts +1 -0
  38. package/dist/declarations/src/@components/uploading-modal/uploading-modal.d.ts +13 -0
  39. package/dist/declarations/src/@constants/delimiters.d.ts +8 -0
  40. package/dist/declarations/src/@constants/file-import-job.d.ts +1 -0
  41. package/dist/declarations/src/@constants/import-limits.d.ts +6 -0
  42. package/dist/declarations/src/@constants/import-tags.d.ts +7 -0
  43. package/dist/declarations/src/@constants/index.d.ts +5 -0
  44. package/dist/declarations/src/@constants/resource-links.d.ts +10 -0
  45. package/dist/declarations/src/@errors/http-error.d.ts +6 -0
  46. package/dist/declarations/src/@errors/index.d.ts +9 -0
  47. package/dist/declarations/src/@errors/invalid-response-error.d.ts +3 -0
  48. package/dist/declarations/src/@errors/no-resources-to-export-error.d.ts +3 -0
  49. package/dist/declarations/src/@errors/polling-aborted-error.d.ts +3 -0
  50. package/dist/declarations/src/@errors/project-key-not-available-error.d.ts +3 -0
  51. package/dist/declarations/src/@errors/query-predicate-error.d.ts +4 -0
  52. package/dist/declarations/src/@errors/unexpected-column-error.d.ts +3 -0
  53. package/dist/declarations/src/@errors/unexpected-operation-state-error.d.ts +4 -0
  54. package/dist/declarations/src/@errors/unexpected-resource-type-error.d.ts +3 -0
  55. package/dist/declarations/src/@hooks/index.d.ts +8 -0
  56. package/dist/declarations/src/@hooks/use-fetch-export-operations.d.ts +15 -0
  57. package/dist/declarations/src/@hooks/use-fetch-file-import-job.d.ts +17 -0
  58. package/dist/declarations/src/@hooks/use-fetch-import-container-details.d.ts +15 -0
  59. package/dist/declarations/src/@hooks/use-fetch-import-operations.d.ts +16 -0
  60. package/dist/declarations/src/@hooks/use-fetch-import-summaries.d.ts +20 -0
  61. package/dist/declarations/src/@hooks/use-file-import-job-upload.d.ts +18 -0
  62. package/dist/declarations/src/@hooks/use-file-upload.d.ts +28 -0
  63. package/dist/declarations/src/@hooks/use-import-container-upload.d.ts +19 -0
  64. package/dist/declarations/src/@types/api.d.ts +13 -0
  65. package/dist/declarations/src/@types/basic-error-data-type.d.ts +5 -0
  66. package/dist/declarations/src/@types/export-operation.d.ts +97 -0
  67. package/dist/declarations/src/@types/file-import-job.d.ts +99 -0
  68. package/dist/declarations/src/@types/file-upload-result.d.ts +21 -0
  69. package/dist/declarations/src/@types/file-upload.d.ts +63 -0
  70. package/dist/declarations/src/@types/import-container.d.ts +53 -0
  71. package/dist/declarations/src/@types/import-operation.d.ts +13 -0
  72. package/dist/declarations/src/@types/import-states.d.ts +9 -0
  73. package/dist/declarations/src/@types/import-summary.d.ts +15 -0
  74. package/dist/declarations/src/@types/index.d.ts +11 -0
  75. package/dist/declarations/src/@types/shared.d.ts +7 -0
  76. package/dist/declarations/src/@utils/error-mapping.d.ts +19 -0
  77. package/dist/declarations/src/@utils/file-import-job-helpers.d.ts +12 -0
  78. package/dist/declarations/src/@utils/file-upload.d.ts +54 -0
  79. package/dist/declarations/src/@utils/form.d.ts +1 -0
  80. package/dist/declarations/src/@utils/format.d.ts +5 -0
  81. package/dist/declarations/src/@utils/import-container.d.ts +8 -0
  82. package/dist/declarations/src/@utils/index.d.ts +8 -0
  83. package/dist/declarations/src/@utils/poll-job-until-validated.d.ts +11 -0
  84. package/dist/declarations/src/@utils/url.d.ts +6 -0
  85. package/dist/declarations/src/index.d.ts +26 -0
  86. package/index.js +1 -0
  87. package/jest.test.config.js +11 -0
  88. package/package.json +62 -0
  89. package/src/@api/export-operations.ts +26 -0
  90. package/src/@api/fetcher.spec.ts +51 -0
  91. package/src/@api/fetcher.ts +137 -0
  92. package/src/@api/file-import-jobs.ts +217 -0
  93. package/src/@api/file-upload.spec.ts +85 -0
  94. package/src/@api/file-upload.ts +46 -0
  95. package/src/@api/import-containers.ts +256 -0
  96. package/src/@api/import-operations.ts +33 -0
  97. package/src/@api/index.ts +9 -0
  98. package/src/@api/process-file.spec.ts +74 -0
  99. package/src/@api/process-file.ts +53 -0
  100. package/src/@api/test-fixtures.ts +894 -0
  101. package/src/@api/urls.ts +194 -0
  102. package/src/@components/file-drop-area/active-drag-drop-area.tsx +33 -0
  103. package/src/@components/file-drop-area/disabled-drop-area.tsx +17 -0
  104. package/src/@components/file-drop-area/drop-area-wrapper.tsx +38 -0
  105. package/src/@components/file-drop-area/enabled-drop-area.tsx +27 -0
  106. package/src/@components/file-drop-area/file-drop-area.tsx +74 -0
  107. package/src/@components/file-drop-area/file-dropped-area.tsx +29 -0
  108. package/src/@components/file-drop-area/index.ts +7 -0
  109. package/src/@components/file-drop-area/styles.ts +67 -0
  110. package/src/@components/icons/file-icon.tsx +30 -0
  111. package/src/@components/icons/index.ts +2 -0
  112. package/src/@components/icons/lock-icon.tsx +34 -0
  113. package/src/@components/index.ts +6 -0
  114. package/src/@components/info-box/index.ts +1 -0
  115. package/src/@components/info-box/info-box.tsx +23 -0
  116. package/src/@components/upload-separator/index.ts +1 -0
  117. package/src/@components/upload-separator/upload-separator.tsx +61 -0
  118. package/src/@components/upload-settings/index.ts +1 -0
  119. package/src/@components/upload-settings/upload-settings.tsx +36 -0
  120. package/src/@components/uploading-modal/index.ts +1 -0
  121. package/src/@components/uploading-modal/uploading-modal.tsx +66 -0
  122. package/src/@constants/delimiters.ts +14 -0
  123. package/src/@constants/file-import-job.ts +1 -0
  124. package/src/@constants/import-limits.ts +13 -0
  125. package/src/@constants/import-tags.ts +9 -0
  126. package/src/@constants/index.ts +5 -0
  127. package/src/@constants/resource-links.ts +61 -0
  128. package/src/@errors/http-error.ts +17 -0
  129. package/src/@errors/index.ts +9 -0
  130. package/src/@errors/invalid-response-error.ts +6 -0
  131. package/src/@errors/no-resources-to-export-error.ts +6 -0
  132. package/src/@errors/polling-aborted-error.ts +6 -0
  133. package/src/@errors/project-key-not-available-error.ts +6 -0
  134. package/src/@errors/query-predicate-error.ts +10 -0
  135. package/src/@errors/unexpected-column-error.ts +6 -0
  136. package/src/@errors/unexpected-operation-state-error.ts +8 -0
  137. package/src/@errors/unexpected-resource-type-error.ts +6 -0
  138. package/src/@hooks/index.ts +8 -0
  139. package/src/@hooks/use-fetch-export-operations.ts +34 -0
  140. package/src/@hooks/use-fetch-file-import-job.spec.ts +131 -0
  141. package/src/@hooks/use-fetch-file-import-job.ts +38 -0
  142. package/src/@hooks/use-fetch-import-container-details.ts +31 -0
  143. package/src/@hooks/use-fetch-import-operations.ts +42 -0
  144. package/src/@hooks/use-fetch-import-summaries.ts +47 -0
  145. package/src/@hooks/use-fetch.spec.ts +68 -0
  146. package/src/@hooks/use-fetch.ts +76 -0
  147. package/src/@hooks/use-file-import-job-upload.spec.ts +273 -0
  148. package/src/@hooks/use-file-import-job-upload.ts +101 -0
  149. package/src/@hooks/use-file-upload.ts +223 -0
  150. package/src/@hooks/use-import-container-upload.spec.ts +297 -0
  151. package/src/@hooks/use-import-container-upload.ts +130 -0
  152. package/src/@types/api.ts +14 -0
  153. package/src/@types/basic-error-data-type.ts +5 -0
  154. package/src/@types/export-operation.ts +147 -0
  155. package/src/@types/file-import-job.ts +165 -0
  156. package/src/@types/file-upload-result.ts +23 -0
  157. package/src/@types/file-upload.ts +81 -0
  158. package/src/@types/import-container.ts +104 -0
  159. package/src/@types/import-operation.ts +31 -0
  160. package/src/@types/import-states.ts +9 -0
  161. package/src/@types/import-summary.ts +22 -0
  162. package/src/@types/index.ts +11 -0
  163. package/src/@types/shared.ts +52 -0
  164. package/src/@utils/error-mapping.spec.ts +126 -0
  165. package/src/@utils/error-mapping.ts +40 -0
  166. package/src/@utils/file-import-job-helpers.spec.ts +147 -0
  167. package/src/@utils/file-import-job-helpers.ts +47 -0
  168. package/src/@utils/file-upload.spec.ts +151 -0
  169. package/src/@utils/file-upload.ts +189 -0
  170. package/src/@utils/form.ts +20 -0
  171. package/src/@utils/format.spec.ts +62 -0
  172. package/src/@utils/format.ts +53 -0
  173. package/src/@utils/import-container.spec.ts +26 -0
  174. package/src/@utils/import-container.ts +34 -0
  175. package/src/@utils/index.ts +8 -0
  176. package/src/@utils/poll-job-until-validated.ts +76 -0
  177. package/src/@utils/url.spec.ts +75 -0
  178. package/src/@utils/url.ts +18 -0
  179. package/src/index.ts +27 -0
  180. package/tsconfig.json +9 -0
@@ -0,0 +1,20 @@
1
+ export const mapFormikErrors = (
2
+ error: string | string[] | undefined | Record<string, boolean>
3
+ ): Record<string, boolean> => {
4
+ if (typeof error === 'string') {
5
+ return { [error]: true }
6
+ }
7
+
8
+ if (Array.isArray(error)) {
9
+ return error.reduce(
10
+ (errorAggregator, currentError) => ({
11
+ ...errorAggregator,
12
+ [currentError]: true,
13
+ }),
14
+ {}
15
+ )
16
+ }
17
+
18
+ if (!error) return {}
19
+ return error
20
+ }
@@ -0,0 +1,62 @@
1
+ import { formatErrorCode, appendCsvOrJsonExtensionIfAbsent } from './format'
2
+
3
+ describe('formatErrorCode', () => {
4
+ it('should format camel case correctly', () => {
5
+ expect(formatErrorCode('ReferencedResourceNotFound')).toBe(
6
+ 'Referenced resource not found'
7
+ )
8
+ })
9
+ it('should handle consecutive uppercase letters correctly', () => {
10
+ expect(formatErrorCode('InvalidJSONInput')).toBe('Invalid JSON input')
11
+ })
12
+ it('should handle single word correctly', () => {
13
+ expect(formatErrorCode('Error')).toBe('Error')
14
+ })
15
+ it('should handle already formatted string correctly', () => {
16
+ expect(formatErrorCode('Already formatted string')).toBe(
17
+ 'Already formatted string'
18
+ )
19
+ })
20
+ it('should return the original code if formatting fails', () => {
21
+ const invalidCode = null as any
22
+ expect(formatErrorCode(invalidCode)).toBe(invalidCode)
23
+ })
24
+ it('should handle empty string correctly', () => {
25
+ expect(formatErrorCode('')).toBe('')
26
+ })
27
+ it('should uppercase specific words correctly', () => {
28
+ expect(formatErrorCode('InvalidAPIResponse')).toBe('Invalid API response')
29
+ expect(formatErrorCode('HttpRequestFailed')).toBe('HTTP request failed')
30
+ })
31
+ })
32
+
33
+ describe('appendCsvOrJsonExtensionIfAbsent', () => {
34
+ it('should append .csv if no extension is present', () => {
35
+ expect(appendCsvOrJsonExtensionIfAbsent('file', 'csv')).toBe('file.csv')
36
+ })
37
+ it('should append .json if no extension is present', () => {
38
+ expect(appendCsvOrJsonExtensionIfAbsent('file', 'json')).toBe('file.json')
39
+ })
40
+ it('should not append .csv if .csv is already present', () => {
41
+ expect(appendCsvOrJsonExtensionIfAbsent('file.csv', 'csv')).toBe('file.csv')
42
+ })
43
+ it('should not append .json if .json is already present', () => {
44
+ expect(appendCsvOrJsonExtensionIfAbsent('file.json', 'json')).toBe(
45
+ 'file.json'
46
+ )
47
+ })
48
+ it('should be case insensitive to extensions', () => {
49
+ expect(appendCsvOrJsonExtensionIfAbsent('file.JSON', 'json')).toBe(
50
+ 'file.JSON'
51
+ )
52
+ expect(appendCsvOrJsonExtensionIfAbsent('file.CSV', 'csv')).toBe('file.CSV')
53
+ })
54
+ it('should append .csv or .json extension if a different extension is present', () => {
55
+ expect(appendCsvOrJsonExtensionIfAbsent('file.txt', 'csv')).toBe(
56
+ 'file.txt.csv'
57
+ )
58
+ expect(appendCsvOrJsonExtensionIfAbsent('file.txt', 'json')).toBe(
59
+ 'file.txt.json'
60
+ )
61
+ })
62
+ })
@@ -0,0 +1,53 @@
1
+ export function formatErrorCode(code: string): string {
2
+ try {
3
+ const formattedCode = code
4
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
5
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1 $2')
6
+ .toLowerCase()
7
+ let result = formattedCode.charAt(0).toUpperCase() + formattedCode.slice(1)
8
+
9
+ const specialWords = ['json', 'api', 'http']
10
+ specialWords.forEach((word) => {
11
+ const regex = new RegExp(`\\b${word}\\b`, 'gi')
12
+ result = result.replace(regex, word.toUpperCase())
13
+ })
14
+
15
+ return result
16
+ } catch (error) {
17
+ return code
18
+ }
19
+ }
20
+
21
+ export function extractErrorDescriptionFromValidationMessage(
22
+ message: string
23
+ ): string | undefined {
24
+ return message.split('"').pop()
25
+ }
26
+
27
+ export function formatKeys(
28
+ obj: Record<string, unknown>,
29
+ conjunction: string = 'and'
30
+ ): string {
31
+ if (typeof obj !== 'object' || obj === null) return ''
32
+ const keys = Object.keys(obj).map((key) =>
33
+ key.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase()
34
+ )
35
+ if (keys.length === 0) return ''
36
+ if (keys.length === 1) return keys[0]
37
+ return `${keys.slice(0, -1).join(', ')} ${conjunction} ${
38
+ keys[keys.length - 1]
39
+ }`
40
+ }
41
+
42
+ export function appendCsvOrJsonExtensionIfAbsent(
43
+ fileName: string,
44
+ fileFormat: 'csv' | 'json'
45
+ ): string {
46
+ const extensionRegex = /\.(csv|json)$/i
47
+ if (!extensionRegex.test(fileName)) return `${fileName}.${fileFormat}`
48
+ return fileName
49
+ }
50
+
51
+ export function convertFileSizeToKB(sizeInBytes: number): number {
52
+ return sizeInBytes / 1000
53
+ }
@@ -0,0 +1,26 @@
1
+ import {
2
+ decodeFileNameFromImportContainerKey,
3
+ encodeFileNameWithTimestampToContainerKey,
4
+ } from './import-container'
5
+
6
+ describe('decodeFileNameFromImportContainerKey', () => {
7
+ it('should decode the file name from a encoded container key', () => {
8
+ const fileName = 'categories.csv'
9
+ const encodedKey =
10
+ 'eyJ0aW1lc3RhbXAiOiAxNzA1MDExMTQ5MTgxLCAiZmlsZU5hbWUiOiAiY2F0ZWdvcmllcy5jc3YifQ'
11
+ expect(decodeFileNameFromImportContainerKey(encodedKey)).toBe(fileName)
12
+ })
13
+ it('should return empty string if decoding fails', () => {
14
+ const invalidKey = 'notBase64Strng'
15
+ expect(decodeFileNameFromImportContainerKey(invalidKey)).toBe('')
16
+ })
17
+ })
18
+ describe('encodeFileNameWithTimestampToContainerKey', () => {
19
+ it('should encode the file name and time stamp into a container key', () => {
20
+ const fileName = 'products.csv'
21
+ const encodedKey = encodeFileNameWithTimestampToContainerKey(fileName)
22
+ const decoded = JSON.parse(atob(encodedKey))
23
+ expect(decoded.fileName).toBe(fileName)
24
+ expect(typeof decoded.timestamp).toBe('number')
25
+ })
26
+ })
@@ -0,0 +1,34 @@
1
+ interface ImportInfo {
2
+ fileName: string
3
+ timestamp: number
4
+ }
5
+
6
+ export const encodeFileNameWithTimestampToContainerKey = (fileName: string) => {
7
+ return btoa(
8
+ JSON.stringify({
9
+ timestamp: new Date().getTime(),
10
+ fileName,
11
+ })
12
+ ).replace(/=+$/g, '')
13
+ }
14
+
15
+ /**
16
+ * Check if an import container is a file upload import
17
+ * @param tags - Array of tags from the import container
18
+ * @returns True if the tags indicate this is a file upload import
19
+ */
20
+ export const checkIfFileUploadImport = (tags?: string[]): boolean => {
21
+ return tags?.some((tag) => tag.startsWith('source:file-upload')) ?? false
22
+ }
23
+
24
+ export const decodeFileNameFromImportContainerKey = (
25
+ importContainerKey: string
26
+ ) => {
27
+ try {
28
+ const decodedImportContainerKey = atob(importContainerKey)
29
+ const importInfo: ImportInfo = JSON.parse(decodedImportContainerKey)
30
+ return importInfo.fileName
31
+ } catch {
32
+ return ''
33
+ }
34
+ }
@@ -0,0 +1,8 @@
1
+ export * from './error-mapping'
2
+ export * from './file-import-job-helpers'
3
+ export * from './file-upload'
4
+ export * from './form'
5
+ export * from './format'
6
+ export * from './import-container'
7
+ export * from './poll-job-until-validated'
8
+ export * from './url'
@@ -0,0 +1,76 @@
1
+ import { getFileImportJob } from '../@api'
2
+ import { PollingAbortedError } from '../@errors'
3
+ import type { FileImportJob } from '../@types'
4
+ import { isImportJobTerminal } from './file-import-job-helpers'
5
+
6
+ export type PollJobUntilValidatedConfig = {
7
+ projectKey: string
8
+ jobId: string
9
+ importContainerKey: string
10
+ pollingInterval?: number
11
+ maxAttempts?: number
12
+ onJobUpdate?: (job: FileImportJob) => void
13
+ abortSignal?: AbortSignal
14
+ }
15
+
16
+ export const pollJobUntilValidated = async ({
17
+ projectKey,
18
+ jobId,
19
+ importContainerKey,
20
+ pollingInterval = 5000,
21
+ maxAttempts = 120,
22
+ onJobUpdate,
23
+ abortSignal,
24
+ }: PollJobUntilValidatedConfig): Promise<FileImportJob> => {
25
+ let attempts = 0
26
+
27
+ while (attempts < maxAttempts) {
28
+ if (abortSignal?.aborted) {
29
+ throw new PollingAbortedError()
30
+ }
31
+
32
+ const job = await getFileImportJob({
33
+ projectKey,
34
+ importContainerKey,
35
+ jobId,
36
+ })
37
+
38
+ if (abortSignal?.aborted) {
39
+ throw new PollingAbortedError()
40
+ }
41
+
42
+ onJobUpdate?.(job)
43
+
44
+ if (isImportJobTerminal(job)) {
45
+ return job
46
+ }
47
+
48
+ await new Promise<void>((resolve, reject) => {
49
+ let timeoutId: ReturnType<typeof setTimeout>
50
+
51
+ const onAbort = () => {
52
+ clearTimeout(timeoutId)
53
+ reject(new PollingAbortedError())
54
+ }
55
+
56
+ if (abortSignal?.aborted) {
57
+ reject(new PollingAbortedError())
58
+ return
59
+ }
60
+
61
+ timeoutId = setTimeout(() => {
62
+ abortSignal?.removeEventListener('abort', onAbort)
63
+ resolve()
64
+ }, pollingInterval)
65
+
66
+ abortSignal?.addEventListener('abort', onAbort)
67
+ })
68
+ attempts++
69
+ }
70
+
71
+ throw new Error(
72
+ `Job validation timeout after ${maxAttempts} attempts (${
73
+ (maxAttempts * pollingInterval) / 1000
74
+ }s)`
75
+ )
76
+ }
@@ -0,0 +1,75 @@
1
+ import { formatQueryString } from './url'
2
+
3
+ describe('formatQueryString', () => {
4
+ it('should return empty string with no params', () => {
5
+ const queryParam: Record<string, unknown> = {}
6
+ expect(formatQueryString(queryParam)).toEqual('')
7
+ })
8
+
9
+ it('should format multiple params', () => {
10
+ const queryParam: Record<string, unknown> = {
11
+ limit: 20,
12
+ offset: 10,
13
+ }
14
+ expect(formatQueryString(queryParam)).toEqual('?limit=20&offset=10')
15
+ })
16
+
17
+ it('should handle offset zero', () => {
18
+ const queryParam: Record<string, unknown> = {
19
+ limit: 20,
20
+ offset: 0,
21
+ }
22
+ expect(formatQueryString(queryParam)).toEqual('?limit=20&offset=0')
23
+ })
24
+
25
+ it('should handle array values', () => {
26
+ const queryParam: Record<string, unknown> = {
27
+ state: ['imported', 'validationFailed'],
28
+ }
29
+ expect(formatQueryString(queryParam)).toEqual(
30
+ '?state=imported&state=validationFailed'
31
+ )
32
+ })
33
+
34
+ it('should handle undefined values', () => {
35
+ const queryParam: Record<string, unknown> = {
36
+ state: undefined,
37
+ }
38
+ expect(formatQueryString(queryParam)).toEqual('')
39
+ })
40
+
41
+ it('should handle null values', () => {
42
+ const queryParam: Record<string, unknown> = {
43
+ state: null,
44
+ }
45
+ expect(formatQueryString(queryParam)).toEqual('')
46
+ })
47
+
48
+ it('should handle boolean values', () => {
49
+ const queryParam: Record<string, unknown> = {
50
+ debug: true,
51
+ verbose: false,
52
+ }
53
+ expect(formatQueryString(queryParam)).toEqual('?debug=true&verbose=false')
54
+ })
55
+
56
+ it('should handle numeric values', () => {
57
+ const queryParam: Record<string, unknown> = {
58
+ limit: 100,
59
+ offset: 50,
60
+ }
61
+ expect(formatQueryString(queryParam)).toEqual('?limit=100&offset=50')
62
+ })
63
+
64
+ it('should handle mixed types', () => {
65
+ const queryParam: Record<string, unknown> = {
66
+ limit: 20,
67
+ offset: 10,
68
+ state: ['imported', 'validationFailed'],
69
+ debug: true,
70
+ }
71
+ expect(formatQueryString(queryParam)).toEqual(
72
+ '?limit=20&offset=10&state=imported&state=validationFailed&debug=true'
73
+ )
74
+ })
75
+ })
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Formats an object into a URL query string
3
+ * @param queryParams - Object containing query parameters
4
+ * @returns Formatted query string with leading '?' or empty string
5
+ */
6
+ export function formatQueryString(queryParams: Record<string, unknown> = {}) {
7
+ const queryString = new URLSearchParams()
8
+ for (const [key, value] of Object.entries(queryParams)) {
9
+ if (value !== undefined && value !== null) {
10
+ if (Array.isArray(value)) {
11
+ value.forEach((v) => queryString.append(key, String(v)))
12
+ } else {
13
+ queryString.append(key, String(value))
14
+ }
15
+ }
16
+ }
17
+ return queryString.toString() ? `?${queryString.toString()}` : ''
18
+ }
package/src/index.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared package for operations
3
+ *
4
+ * This package provides shared functionality for import/export operations across:
5
+ * - application-operations
6
+ * - import-resources-modal
7
+ * - export-resources-modal
8
+ * - delete-resources-modal
9
+ * - unpublish-products-modal
10
+ *
11
+ * Public API includes:
12
+ * - Hooks: useImportContainerUpload, useFetch*, etc.
13
+ * - Components: FileDropArea, UploadingModal, UploadSettings, etc.
14
+ * - Utils: File validation, error mapping, formatting, etc.
15
+ * - Constants: Delimiters, upload limits, resource links, etc.
16
+ * - Types: All TypeScript types and interfaces
17
+ * - Errors: Custom error classes
18
+ * - API: Functions for interacting with import/export APIs
19
+ */
20
+
21
+ export * from './@api'
22
+ export * from './@components'
23
+ export * from './@constants'
24
+ export * from './@errors'
25
+ export * from './@hooks'
26
+ export * from './@types'
27
+ export * from './@utils'
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "isolatedModules": true,
5
+ "skipLibCheck": true,
6
+ "baseUrl": "src"
7
+ },
8
+ "include": ["src/**/*"]
9
+ }