@wix/bex-core 2.328.0 → 2.330.0

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 (254) hide show
  1. package/dist/cjs/assets/locale/messages_ar.json +12 -0
  2. package/dist/cjs/assets/locale/messages_bg.json +12 -0
  3. package/dist/cjs/assets/locale/messages_ca.json +12 -0
  4. package/dist/cjs/assets/locale/messages_cs.json +12 -0
  5. package/dist/cjs/assets/locale/messages_da.json +17 -5
  6. package/dist/cjs/assets/locale/messages_de.json +12 -0
  7. package/dist/cjs/assets/locale/messages_el.json +12 -0
  8. package/dist/cjs/assets/locale/messages_en.json +12 -0
  9. package/dist/cjs/assets/locale/messages_es.json +16 -4
  10. package/dist/cjs/assets/locale/messages_fi.json +12 -0
  11. package/dist/cjs/assets/locale/messages_fr.json +12 -0
  12. package/dist/cjs/assets/locale/messages_he.json +12 -0
  13. package/dist/cjs/assets/locale/messages_hi.json +17 -5
  14. package/dist/cjs/assets/locale/messages_hu.json +12 -0
  15. package/dist/cjs/assets/locale/messages_id.json +17 -5
  16. package/dist/cjs/assets/locale/messages_it.json +17 -5
  17. package/dist/cjs/assets/locale/messages_ja.json +12 -0
  18. package/dist/cjs/assets/locale/messages_ko.json +17 -5
  19. package/dist/cjs/assets/locale/messages_lt.json +12 -0
  20. package/dist/cjs/assets/locale/messages_ms.json +12 -0
  21. package/dist/cjs/assets/locale/messages_nl.json +16 -4
  22. package/dist/cjs/assets/locale/messages_no.json +12 -0
  23. package/dist/cjs/assets/locale/messages_pl.json +12 -0
  24. package/dist/cjs/assets/locale/messages_pt.json +16 -4
  25. package/dist/cjs/assets/locale/messages_ro.json +12 -0
  26. package/dist/cjs/assets/locale/messages_ru.json +12 -0
  27. package/dist/cjs/assets/locale/messages_sk.json +12 -0
  28. package/dist/cjs/assets/locale/messages_sl.json +12 -0
  29. package/dist/cjs/assets/locale/messages_sv.json +17 -5
  30. package/dist/cjs/assets/locale/messages_th.json +17 -5
  31. package/dist/cjs/assets/locale/messages_tl.json +12 -0
  32. package/dist/cjs/assets/locale/messages_tr.json +17 -5
  33. package/dist/cjs/assets/locale/messages_uk.json +12 -0
  34. package/dist/cjs/assets/locale/messages_vi.json +17 -5
  35. package/dist/cjs/assets/locale/messages_zh.json +12 -0
  36. package/dist/cjs/assets/locale/version_file +1 -1
  37. package/dist/cjs/assets/visual-e2e-locale/messages_visuale2e.json +5 -1
  38. package/dist/cjs/hooks/useImport.js +7 -5
  39. package/dist/cjs/hooks/useImport.js.map +1 -1
  40. package/dist/cjs/hooks/useImportConfig.js.map +1 -1
  41. package/dist/cjs/index.js +5 -2
  42. package/dist/cjs/index.js.map +1 -1
  43. package/dist/cjs/services/DataExtensionFieldOperationsService.js +3 -3
  44. package/dist/cjs/services/DataExtensionFieldOperationsService.js.map +1 -1
  45. package/dist/cjs/services/translations.js.map +1 -1
  46. package/dist/cjs/state/ArchiveFieldModalState.js +5 -8
  47. package/dist/cjs/state/ArchiveFieldModalState.js.map +1 -1
  48. package/dist/cjs/state/CustomFieldModalBiReporter.js +5 -9
  49. package/dist/cjs/state/CustomFieldModalBiReporter.js.map +1 -1
  50. package/dist/cjs/state/CustomFieldModalState.js +32 -71
  51. package/dist/cjs/state/CustomFieldModalState.js.map +1 -1
  52. package/dist/cjs/state/DataExtensionState.js +0 -12
  53. package/dist/cjs/state/DataExtensionState.js.map +1 -1
  54. package/dist/cjs/state/DeleteFieldModalState.js +9 -11
  55. package/dist/cjs/state/DeleteFieldModalState.js.map +1 -1
  56. package/dist/cjs/state/{DataExtensionStateBIReporter.js → FieldOperationsBIReporter.js} +7 -32
  57. package/dist/cjs/state/FieldOperationsBIReporter.js.map +1 -0
  58. package/dist/cjs/state/ImportState/ImportState.js +383 -109
  59. package/dist/cjs/state/ImportState/ImportState.js.map +1 -1
  60. package/dist/cjs/state/ImportState/__tests__/ImportState.engine.spec.js +610 -0
  61. package/dist/cjs/state/ImportState/__tests__/ImportState.engine.spec.js.map +1 -0
  62. package/dist/cjs/state/ImportState/__tests__/hubJobClient.spec.js +209 -0
  63. package/dist/cjs/state/ImportState/__tests__/hubJobClient.spec.js.map +1 -0
  64. package/dist/cjs/state/ImportState/__tests__/importJobPoller.spec.js +86 -0
  65. package/dist/cjs/state/ImportState/__tests__/importJobPoller.spec.js.map +1 -0
  66. package/dist/cjs/state/ImportState/__tests__/parseCsv.spec.js +52 -0
  67. package/dist/cjs/state/ImportState/__tests__/parseCsv.spec.js.map +1 -0
  68. package/dist/cjs/state/ImportState/__tests__/rewriteCsv.spec.js +81 -0
  69. package/dist/cjs/state/ImportState/__tests__/rewriteCsv.spec.js.map +1 -0
  70. package/dist/cjs/state/ImportState/fieldEdit.js +46 -0
  71. package/dist/cjs/state/ImportState/fieldEdit.js.map +1 -0
  72. package/dist/cjs/state/ImportState/hubJobClient.js +101 -0
  73. package/dist/cjs/state/ImportState/hubJobClient.js.map +1 -0
  74. package/dist/cjs/state/ImportState/importJobPoller.js +44 -0
  75. package/dist/cjs/state/ImportState/importJobPoller.js.map +1 -0
  76. package/dist/cjs/state/ImportState/index.js +12 -0
  77. package/dist/cjs/state/ImportState/index.js.map +1 -1
  78. package/dist/cjs/state/ImportState/parseCsv.js +39 -10
  79. package/dist/cjs/state/ImportState/parseCsv.js.map +1 -1
  80. package/dist/cjs/state/ImportState/rewriteCsv.js +46 -0
  81. package/dist/cjs/state/ImportState/rewriteCsv.js.map +1 -0
  82. package/dist/cjs/state/formatPermissionsForBi.js +36 -0
  83. package/dist/cjs/state/formatPermissionsForBi.js.map +1 -0
  84. package/dist/cjs/testkit/WixPatternsContainer.driver.js +4 -1
  85. package/dist/cjs/testkit/WixPatternsContainer.driver.js.map +1 -1
  86. package/dist/cjs/testkit/client.js +6 -0
  87. package/dist/cjs/testkit/client.js.map +1 -1
  88. package/dist/cjs/testkit/hubJobImportMocks.js +166 -0
  89. package/dist/cjs/testkit/hubJobImportMocks.js.map +1 -0
  90. package/dist/cjs/testkit/index.js +6 -0
  91. package/dist/cjs/testkit/index.js.map +1 -1
  92. package/dist/cjs/types/FieldsSource.js +4 -0
  93. package/dist/cjs/types/FieldsSource.js.map +1 -0
  94. package/dist/cjs/types/Schema.js.map +1 -1
  95. package/dist/cjs/types/SchemaSource.js +81 -0
  96. package/dist/cjs/types/SchemaSource.js.map +1 -0
  97. package/dist/esm/assets/locale/messages_ar.json +12 -0
  98. package/dist/esm/assets/locale/messages_bg.json +12 -0
  99. package/dist/esm/assets/locale/messages_ca.json +12 -0
  100. package/dist/esm/assets/locale/messages_cs.json +12 -0
  101. package/dist/esm/assets/locale/messages_da.json +17 -5
  102. package/dist/esm/assets/locale/messages_de.json +12 -0
  103. package/dist/esm/assets/locale/messages_el.json +12 -0
  104. package/dist/esm/assets/locale/messages_en.json +12 -0
  105. package/dist/esm/assets/locale/messages_es.json +16 -4
  106. package/dist/esm/assets/locale/messages_fi.json +12 -0
  107. package/dist/esm/assets/locale/messages_fr.json +12 -0
  108. package/dist/esm/assets/locale/messages_he.json +12 -0
  109. package/dist/esm/assets/locale/messages_hi.json +17 -5
  110. package/dist/esm/assets/locale/messages_hu.json +12 -0
  111. package/dist/esm/assets/locale/messages_id.json +17 -5
  112. package/dist/esm/assets/locale/messages_it.json +17 -5
  113. package/dist/esm/assets/locale/messages_ja.json +12 -0
  114. package/dist/esm/assets/locale/messages_ko.json +17 -5
  115. package/dist/esm/assets/locale/messages_lt.json +12 -0
  116. package/dist/esm/assets/locale/messages_ms.json +12 -0
  117. package/dist/esm/assets/locale/messages_nl.json +16 -4
  118. package/dist/esm/assets/locale/messages_no.json +12 -0
  119. package/dist/esm/assets/locale/messages_pl.json +12 -0
  120. package/dist/esm/assets/locale/messages_pt.json +16 -4
  121. package/dist/esm/assets/locale/messages_ro.json +12 -0
  122. package/dist/esm/assets/locale/messages_ru.json +12 -0
  123. package/dist/esm/assets/locale/messages_sk.json +12 -0
  124. package/dist/esm/assets/locale/messages_sl.json +12 -0
  125. package/dist/esm/assets/locale/messages_sv.json +17 -5
  126. package/dist/esm/assets/locale/messages_th.json +17 -5
  127. package/dist/esm/assets/locale/messages_tl.json +12 -0
  128. package/dist/esm/assets/locale/messages_tr.json +17 -5
  129. package/dist/esm/assets/locale/messages_uk.json +12 -0
  130. package/dist/esm/assets/locale/messages_vi.json +17 -5
  131. package/dist/esm/assets/locale/messages_zh.json +12 -0
  132. package/dist/esm/assets/locale/version_file +1 -1
  133. package/dist/esm/assets/visual-e2e-locale/messages_visuale2e.json +5 -1
  134. package/dist/esm/hooks/useImport.js +5 -4
  135. package/dist/esm/hooks/useImport.js.map +1 -1
  136. package/dist/esm/hooks/useImportConfig.js.map +1 -1
  137. package/dist/esm/index.js +1 -0
  138. package/dist/esm/index.js.map +1 -1
  139. package/dist/esm/services/DataExtensionFieldOperationsService.js +3 -3
  140. package/dist/esm/services/DataExtensionFieldOperationsService.js.map +1 -1
  141. package/dist/esm/state/ArchiveFieldModalState.js +4 -7
  142. package/dist/esm/state/ArchiveFieldModalState.js.map +1 -1
  143. package/dist/esm/state/CustomFieldModalBiReporter.js +5 -8
  144. package/dist/esm/state/CustomFieldModalBiReporter.js.map +1 -1
  145. package/dist/esm/state/CustomFieldModalState.js +28 -52
  146. package/dist/esm/state/CustomFieldModalState.js.map +1 -1
  147. package/dist/esm/state/DataExtensionState.js +0 -10
  148. package/dist/esm/state/DataExtensionState.js.map +1 -1
  149. package/dist/esm/state/DeleteFieldModalState.js +7 -10
  150. package/dist/esm/state/DeleteFieldModalState.js.map +1 -1
  151. package/dist/esm/state/{DataExtensionStateBIReporter.js → FieldOperationsBIReporter.js} +5 -25
  152. package/dist/esm/state/FieldOperationsBIReporter.js.map +1 -0
  153. package/dist/esm/state/ImportState/ImportState.js +314 -101
  154. package/dist/esm/state/ImportState/ImportState.js.map +1 -1
  155. package/dist/esm/state/ImportState/__tests__/ImportState.engine.spec.js +434 -0
  156. package/dist/esm/state/ImportState/__tests__/ImportState.engine.spec.js.map +1 -0
  157. package/dist/esm/state/ImportState/__tests__/hubJobClient.spec.js +139 -0
  158. package/dist/esm/state/ImportState/__tests__/hubJobClient.spec.js.map +1 -0
  159. package/dist/esm/state/ImportState/__tests__/importJobPoller.spec.js +62 -0
  160. package/dist/esm/state/ImportState/__tests__/importJobPoller.spec.js.map +1 -0
  161. package/dist/esm/state/ImportState/__tests__/parseCsv.spec.js +59 -0
  162. package/dist/esm/state/ImportState/__tests__/parseCsv.spec.js.map +1 -0
  163. package/dist/esm/state/ImportState/__tests__/rewriteCsv.spec.js +68 -0
  164. package/dist/esm/state/ImportState/__tests__/rewriteCsv.spec.js.map +1 -0
  165. package/dist/esm/state/ImportState/fieldEdit.js +33 -0
  166. package/dist/esm/state/ImportState/fieldEdit.js.map +1 -0
  167. package/dist/esm/state/ImportState/hubJobClient.js +43 -0
  168. package/dist/esm/state/ImportState/hubJobClient.js.map +1 -0
  169. package/dist/esm/state/ImportState/importJobPoller.js +34 -0
  170. package/dist/esm/state/ImportState/importJobPoller.js.map +1 -0
  171. package/dist/esm/state/ImportState/index.js +2 -0
  172. package/dist/esm/state/ImportState/index.js.map +1 -1
  173. package/dist/esm/state/ImportState/parseCsv.js +34 -10
  174. package/dist/esm/state/ImportState/parseCsv.js.map +1 -1
  175. package/dist/esm/state/ImportState/rewriteCsv.js +39 -0
  176. package/dist/esm/state/ImportState/rewriteCsv.js.map +1 -0
  177. package/dist/esm/state/formatPermissionsForBi.js +26 -0
  178. package/dist/esm/state/formatPermissionsForBi.js.map +1 -0
  179. package/dist/esm/testkit/WixPatternsContainer.driver.js +4 -1
  180. package/dist/esm/testkit/WixPatternsContainer.driver.js.map +1 -1
  181. package/dist/esm/testkit/client.js +1 -0
  182. package/dist/esm/testkit/client.js.map +1 -1
  183. package/dist/esm/testkit/hubJobImportMocks.js +173 -0
  184. package/dist/esm/testkit/hubJobImportMocks.js.map +1 -0
  185. package/dist/esm/testkit/index.js +1 -0
  186. package/dist/esm/testkit/index.js.map +1 -1
  187. package/dist/esm/types/FieldsSource.js +2 -0
  188. package/dist/esm/types/FieldsSource.js.map +1 -0
  189. package/dist/esm/types/SchemaSource.js +52 -0
  190. package/dist/esm/types/SchemaSource.js.map +1 -0
  191. package/dist/types/hooks/useImport.d.ts +5 -3
  192. package/dist/types/hooks/useImport.d.ts.map +1 -1
  193. package/dist/types/hooks/useImportConfig.d.ts +2 -3
  194. package/dist/types/hooks/useImportConfig.d.ts.map +1 -1
  195. package/dist/types/index.d.ts +4 -1
  196. package/dist/types/index.d.ts.map +1 -1
  197. package/dist/types/services/translations.d.ts +1 -1
  198. package/dist/types/services/translations.d.ts.map +1 -1
  199. package/dist/types/state/ArchiveFieldModalState.d.ts +7 -5
  200. package/dist/types/state/ArchiveFieldModalState.d.ts.map +1 -1
  201. package/dist/types/state/CustomFieldModalBiReporter.d.ts +4 -3
  202. package/dist/types/state/CustomFieldModalBiReporter.d.ts.map +1 -1
  203. package/dist/types/state/CustomFieldModalState.d.ts +12 -6
  204. package/dist/types/state/CustomFieldModalState.d.ts.map +1 -1
  205. package/dist/types/state/DataExtensionState.d.ts +0 -2
  206. package/dist/types/state/DataExtensionState.d.ts.map +1 -1
  207. package/dist/types/state/DeleteFieldModalState.d.ts +8 -5
  208. package/dist/types/state/DeleteFieldModalState.d.ts.map +1 -1
  209. package/dist/types/state/{DataExtensionStateBIReporter.d.ts → FieldOperationsBIReporter.d.ts} +4 -5
  210. package/dist/types/state/FieldOperationsBIReporter.d.ts.map +1 -0
  211. package/dist/types/state/ImportState/ImportState.d.ts +120 -20
  212. package/dist/types/state/ImportState/ImportState.d.ts.map +1 -1
  213. package/dist/types/state/ImportState/__tests__/ImportState.engine.spec.d.ts +2 -0
  214. package/dist/types/state/ImportState/__tests__/ImportState.engine.spec.d.ts.map +1 -0
  215. package/dist/types/state/ImportState/__tests__/hubJobClient.spec.d.ts +2 -0
  216. package/dist/types/state/ImportState/__tests__/hubJobClient.spec.d.ts.map +1 -0
  217. package/dist/types/state/ImportState/__tests__/importJobPoller.spec.d.ts +2 -0
  218. package/dist/types/state/ImportState/__tests__/importJobPoller.spec.d.ts.map +1 -0
  219. package/dist/types/state/ImportState/__tests__/parseCsv.spec.d.ts +2 -0
  220. package/dist/types/state/ImportState/__tests__/parseCsv.spec.d.ts.map +1 -0
  221. package/dist/types/state/ImportState/__tests__/rewriteCsv.spec.d.ts +2 -0
  222. package/dist/types/state/ImportState/__tests__/rewriteCsv.spec.d.ts.map +1 -0
  223. package/dist/types/state/ImportState/fieldEdit.d.ts +10 -0
  224. package/dist/types/state/ImportState/fieldEdit.d.ts.map +1 -0
  225. package/dist/types/state/ImportState/hubJobClient.d.ts +47 -0
  226. package/dist/types/state/ImportState/hubJobClient.d.ts.map +1 -0
  227. package/dist/types/state/ImportState/importJobPoller.d.ts +20 -0
  228. package/dist/types/state/ImportState/importJobPoller.d.ts.map +1 -0
  229. package/dist/types/state/ImportState/index.d.ts +2 -0
  230. package/dist/types/state/ImportState/index.d.ts.map +1 -1
  231. package/dist/types/state/ImportState/parseCsv.d.ts +7 -0
  232. package/dist/types/state/ImportState/parseCsv.d.ts.map +1 -1
  233. package/dist/types/state/ImportState/rewriteCsv.d.ts +25 -0
  234. package/dist/types/state/ImportState/rewriteCsv.d.ts.map +1 -0
  235. package/dist/types/state/formatPermissionsForBi.d.ts +7 -0
  236. package/dist/types/state/formatPermissionsForBi.d.ts.map +1 -0
  237. package/dist/types/testkit/WixPatternsContainer.driver.d.ts +22 -0
  238. package/dist/types/testkit/WixPatternsContainer.driver.d.ts.map +1 -1
  239. package/dist/types/testkit/client.d.ts +1 -0
  240. package/dist/types/testkit/client.d.ts.map +1 -1
  241. package/dist/types/testkit/hubJobImportMocks.d.ts +40 -0
  242. package/dist/types/testkit/hubJobImportMocks.d.ts.map +1 -0
  243. package/dist/types/testkit/index.d.ts +1 -0
  244. package/dist/types/testkit/index.d.ts.map +1 -1
  245. package/dist/types/types/FieldsSource.d.ts +116 -0
  246. package/dist/types/types/FieldsSource.d.ts.map +1 -0
  247. package/dist/types/types/Schema.d.ts +28 -11
  248. package/dist/types/types/Schema.d.ts.map +1 -1
  249. package/dist/types/types/SchemaSource.d.ts +60 -0
  250. package/dist/types/types/SchemaSource.d.ts.map +1 -0
  251. package/package.json +4 -4
  252. package/dist/cjs/state/DataExtensionStateBIReporter.js.map +0 -1
  253. package/dist/esm/state/DataExtensionStateBIReporter.js.map +0 -1
  254. package/dist/types/state/DataExtensionStateBIReporter.d.ts.map +0 -1
@@ -7,16 +7,60 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
7
7
  var _mobx = require("mobx");
8
8
  var _model = require("../../model");
9
9
  var _ConditionalModalState = require("../ConditionalModalState");
10
+ var _types = require("@wix/bex-utils/@wix/ambassador-hub-v1-job/types");
11
+ exports.WritePolicy = _types.WritePolicy;
10
12
  var _parseCsv = require("./parseCsv");
13
+ var _rewriteCsv = require("./rewriteCsv");
14
+ var _hubJobClient = require("./hubJobClient");
15
+ var _importJobPoller = require("./importJobPoller");
11
16
  var _detectFieldType = require("./detectFieldType");
12
- var _set = _interopRequireDefault(require("lodash/set"));
17
+ var _fieldEdit = require("./fieldEdit");
18
+ /** Outcome of a server-side import job, derived from the job's progress. */
19
+
20
+ /**
21
+ * Optional snapshot taken before the import runs, returning a `restore` used to
22
+ * undo the import on cancel. See {@link BackupResult.restore} for the revert
23
+ * contract under upsert.
24
+ */
25
+
26
+ /**
27
+ * Consumer hook for resolving a failed import job's message. An import runs
28
+ * server-side, so a failure arrives on a 200 (the poll succeeds; the job
29
+ * doesn't) with no HTTP error to carry the application error. Cairo hands the
30
+ * consumer `run` — an operation that rejects with a synthetic, HTTP-error-shaped
31
+ * error carrying the job's application error — and the consumer wraps it with
32
+ * their own `withErrorHandler` + error-code map (their codes, their
33
+ * translations), keeping the map coupled to the `withErrorHandler` call as
34
+ * everywhere else (e.g. `fetchData`). The map is keyed by application-error code
35
+ * (the v2 errorHandler shape, like `getExportAsyncJobErrors`):
36
+ *
37
+ * onJobError: (run) =>
38
+ * errorHandler.withErrorHandler(run, {
39
+ * [jobErrorCode]: (error) => ({ message: translate(...) }),
40
+ * })
41
+ *
42
+ * `withErrorHandler` attaches the map to the error object and re-rejects it, so
43
+ * the progress step resolves a consumer-specific message via `getResolvedError`.
44
+ * When omitted, the error still surfaces but resolves to the generic message.
45
+ */
46
+
13
47
  // `NewFieldType`, `detectColumnType`, etc. are re-exported from the package via
14
48
  // the `ImportState` barrel (`./index`), which also exports `./detectFieldType`.
15
49
 
50
+ // Re-export the write-policy enum so consumers (and the cairo UI) reference it
51
+ // from `@wix/bex-core` rather than the bundled ambassador path.
52
+
53
+ // Per-type configuration for a new field, captured from the "Edit field" editor
54
+ // (CAIRO-4398). All optional — an unedited field carries none and is created
55
+ // from detected defaults only. `displayName` is intentionally not here: the
56
+ // (possibly user-overridden) name is deduped and surfaced as
57
+ // `NewFieldDefinition.displayName`.
58
+
16
59
  // A new field to add to the collection schema during import. `key` is decided by
17
- // Cairo (derived from the column header, deduped) and is also the key the
18
- // column's values are written under, so the consumer must add the field under
19
- // exactly this key.
60
+ // Cairo (derived from the column header or the user's name override — deduped)
61
+ // and is also the key the column's values are written under, so the consumer
62
+ // must add the field under exactly this key. `config` carries any per-type
63
+ // configuration the field editor produced; create the field with it.
20
64
 
21
65
  /**
22
66
  * Consumer-supplied callback that adds the given new fields to the collection
@@ -30,17 +74,23 @@ var _set = _interopRequireDefault(require("lodash/set"));
30
74
  /**
31
75
  * How a CSV column is mapped:
32
76
  * - `existing` — into an existing collection field by id
33
- * - `new` — into a new field created during import (always text for now)
77
+ * - `new` — into a new field created during import. `fieldType` is the detected
78
+ * type, or the type the user picked in the field editor. `displayName` and
79
+ * `config` are present only once the user edited the pending field via the
80
+ * "Edit field" editor — `displayName` overrides the header-derived name and
81
+ * `config` carries the per-type configuration.
34
82
  * - `null` — don't import (skip)
35
83
  */
36
84
 
37
- const DEFAULT_BATCH_SIZE = 100;
38
-
39
85
  // How long to keep the completed progress bar (100%) on screen before closing
40
86
  // the modal on a clean import. Without it, an import of very few rows finishes
41
87
  // near-instantly and the modal appears to vanish without ever showing the bar
42
88
  // reaching completion.
43
89
  const COMPLETION_LINGER_MS = 600;
90
+
91
+ // The Wix Data collection's id field key. A CSV column mapped to this field lets
92
+ // the user choose how rows that collide on `_id` are written (writePolicy).
93
+ const ID_FIELD_KEY = '_id';
44
94
  function normalize(value) {
45
95
  return value.trim().toLowerCase();
46
96
  }
@@ -76,23 +126,10 @@ function uniqueFieldNameAndKey(header, usedNames, usedKeys) {
76
126
  displayName = `${header}-${suffix}`;
77
127
  }
78
128
  }
79
- function toMappedRows(rows, headers, mappingByHeader) {
80
- return rows.map(row => {
81
- const mapped = {};
82
- headers.forEach((header, index) => {
83
- const mappedField = mappingByHeader[header];
84
- if (mappedField == null) {
85
- return;
86
- }
87
- (0, _set.default)(mapped, mappedField, row[index] ?? '');
88
- });
89
- return mapped;
90
- });
91
- }
92
129
  class ImportState {
93
130
  constructor(props) {
94
131
  (0, _defineProperty2.default)(this, "fields", void 0);
95
- (0, _defineProperty2.default)(this, "bulkUploadItems", void 0);
132
+ (0, _defineProperty2.default)(this, "collectionId", void 0);
96
133
  (0, _defineProperty2.default)(this, "backup", void 0);
97
134
  (0, _defineProperty2.default)(this, "updateSchema", void 0);
98
135
  (0, _defineProperty2.default)(this, "collection", void 0);
@@ -101,7 +138,8 @@ class ImportState {
101
138
  (0, _defineProperty2.default)(this, "errorHandler", void 0);
102
139
  (0, _defineProperty2.default)(this, "showToast", void 0);
103
140
  (0, _defineProperty2.default)(this, "downloadCsv", void 0);
104
- (0, _defineProperty2.default)(this, "batchSize", void 0);
141
+ (0, _defineProperty2.default)(this, "onJobError", void 0);
142
+ (0, _defineProperty2.default)(this, "hubJobClient", void 0);
105
143
  (0, _defineProperty2.default)(this, "_detectColumnType", void 0);
106
144
  (0, _defineProperty2.default)(this, "discardChangesModal", new _ConditionalModalState.ConditionalModalState());
107
145
  (0, _defineProperty2.default)(this, "currentStep", 'upload');
@@ -111,26 +149,43 @@ class ImportState {
111
149
  (0, _defineProperty2.default)(this, "fileName", '');
112
150
  (0, _defineProperty2.default)(this, "csvHeaders", []);
113
151
  (0, _defineProperty2.default)(this, "csvRows", []);
152
+ // Header display order for the configuration step, snapshotted when the file
153
+ // is parsed. The column auto-mapped to `_id` floats to the top (CAIRO-4418);
154
+ // the order then stays fixed while the user edits mappings, so clearing or
155
+ // remapping the `_id` column can't reorder the row under the user's cursor.
156
+ // Recomputed only on a new file (`applyParsedCsv`) or a full `reset()`.
157
+ (0, _defineProperty2.default)(this, "orderedHeaders", []);
114
158
  (0, _defineProperty2.default)(this, "mappingByHeader", {});
159
+ // How rows that collide on `_id` are written. Only meaningful (and only sent
160
+ // to the server) when a CSV column is mapped to the `_id` field — see
161
+ // `idMappedHeader` and `_destination`. Defaults to the server default
162
+ // (`OVERWRITE` = upsert by `_id`).
163
+ //
164
+ // Retention: the choice is deliberately kept while editing mappings within a
165
+ // file — remapping `_id` away and back keeps the user's selection rather than
166
+ // silently reverting it. It resets to the default only on a *new file* upload
167
+ // (`applyParsedCsv`) or a full `reset()`.
168
+ (0, _defineProperty2.default)(this, "writePolicy", _types.WritePolicy.OVERWRITE);
115
169
  (0, _defineProperty2.default)(this, "processedCount", 0);
116
170
  (0, _defineProperty2.default)(this, "totalCount", 0);
117
171
  (0, _defineProperty2.default)(this, "result", {
118
172
  succeeded: 0,
119
- failed: 0,
120
- errors: []
173
+ failed: 0
121
174
  });
122
175
  (0, _defineProperty2.default)(this, "isBackingUp", false);
123
176
  (0, _defineProperty2.default)(this, "isRestoring", false);
124
177
  (0, _defineProperty2.default)(this, "restoreFn", null);
125
178
  (0, _defineProperty2.default)(this, "_cancelled", false);
126
179
  (0, _defineProperty2.default)(this, "_abortController", null);
127
- // Global indices (into `csvRows`) of every row that was not imported: rows
128
- // the server rejected via a batch's returned `errors`, plus when a batch
129
- // throws after some rows succeeded — the throwing batch and all un-attempted
130
- // rows. Offered to the user as a downloadable CSV via the partial toast.
131
- (0, _defineProperty2.default)(this, "_failedRowIndices", []);
180
+ // Id of the running data-movement job, set once submitted used to cancel it
181
+ // (`terminateJob`) if the user closes mid-import.
182
+ (0, _defineProperty2.default)(this, "_jobId", null);
183
+ // The column whose pending new field the field editor is currently editing.
184
+ // Not observable — transient wiring between `beginFieldEdit` and the editor's
185
+ // `applyFieldEdit` callback, never rendered.
186
+ (0, _defineProperty2.default)(this, "_editingHeader", null);
132
187
  this.fields = props.fields ?? [];
133
- this.bulkUploadItems = props.bulkUploadItems;
188
+ this.collectionId = props.collectionId;
134
189
  this.backup = props.backup;
135
190
  this.updateSchema = props.updateSchema;
136
191
  this.collection = props.collection;
@@ -139,15 +194,21 @@ class ImportState {
139
194
  this.errorHandler = props.errorHandler;
140
195
  this.showToast = props.showToast;
141
196
  this.downloadCsv = props.downloadCsv;
142
- this.batchSize = props.batchSize ?? DEFAULT_BATCH_SIZE;
197
+ this.onJobError = props.onJobError;
143
198
  this._detectColumnType = props.detectColumnType ?? _detectFieldType.detectColumnType;
199
+ this.hubJobClient = (0, _hubJobClient.createHubJobClient)({
200
+ httpClient: props.httpClient,
201
+ errorHandler: props.errorHandler
202
+ });
144
203
  (0, _mobx.makeObservable)(this, {
145
204
  currentStep: _mobx.observable.ref,
146
205
  importStatus: _mobx.observable.ref,
147
206
  fileName: _mobx.observable.ref,
148
207
  csvHeaders: _mobx.observable.ref,
149
208
  csvRows: _mobx.observable.ref,
209
+ orderedHeaders: _mobx.observable.ref,
150
210
  mappingByHeader: _mobx.observable.ref,
211
+ writePolicy: _mobx.observable.ref,
151
212
  processedCount: _mobx.observable.ref,
152
213
  totalCount: _mobx.observable.ref,
153
214
  result: _mobx.observable.ref,
@@ -157,10 +218,15 @@ class ImportState {
157
218
  hasMappings: _mobx.computed,
158
219
  hasNoMappedData: _mobx.computed,
159
220
  selectedFieldIds: _mobx.computed,
221
+ idMappedHeader: _mobx.computed,
222
+ setWritePolicy: _mobx.action.bound,
160
223
  onUploadContinue: _mobx.action.bound,
161
224
  goToUpload: _mobx.action.bound,
162
225
  setMapping: _mobx.action.bound,
163
226
  setNewFieldMapping: _mobx.action.bound,
227
+ setNewFieldConfig: _mobx.action.bound,
228
+ beginFieldEdit: _mobx.action.bound,
229
+ applyFieldEdit: _mobx.action.bound,
164
230
  close: _mobx.action.bound,
165
231
  onFileSelected: _mobx.action.bound,
166
232
  startImport: _mobx.action.bound,
@@ -168,7 +234,15 @@ class ImportState {
168
234
  });
169
235
  }
170
236
  init() {
171
- return () => undefined;
237
+ return () => {
238
+ // Unmounting mid-import stops the poll loop (it checks `_cancelled`) so it
239
+ // doesn't run to terminal holding references. We deliberately do NOT abort
240
+ // the AbortController: during a cancel-triggered restore it holds the
241
+ // restore controller, and aborting it would cancel an in-progress restore.
242
+ // The server-side job is also left running — an unmount isn't an explicit
243
+ // user cancel.
244
+ this._cancelled = true;
245
+ };
172
246
  }
173
247
  get hasParsedRows() {
174
248
  return this.csvHeaders.length > 0 && this.csvRows.length > 0;
@@ -200,6 +274,20 @@ class ImportState {
200
274
  return new Set(Object.values(this.mappingByHeader).filter(m => (m == null ? void 0 : m.kind) === 'existing').map(m => m.fieldId));
201
275
  }
202
276
 
277
+ /**
278
+ * The CSV header currently mapped to the collection's `_id` field, or `null`
279
+ * when no column targets it. Drives both the "When items share the same ID"
280
+ * control (only shown on this row) and whether `writePolicy` is sent at all —
281
+ * with no `_id` mapped, every row is a fresh insert and collisions can't occur.
282
+ */
283
+ get idMappedHeader() {
284
+ const entry = Object.entries(this.mappingByHeader).find(([, mapping]) => (mapping == null ? void 0 : mapping.kind) === 'existing' && mapping.fieldId === ID_FIELD_KEY);
285
+ return entry ? entry[0] : null;
286
+ }
287
+ setWritePolicy(policy) {
288
+ this.writePolicy = policy;
289
+ }
290
+
203
291
  /**
204
292
  * Whether columns may be mapped to a brand-new field created during import.
205
293
  * Driven purely by the presence of the `updateSchema` callback — providing the
@@ -220,25 +308,42 @@ class ImportState {
220
308
  this.fileName = '';
221
309
  this.csvHeaders = [];
222
310
  this.csvRows = [];
311
+ this.orderedHeaders = [];
223
312
  this.mappingByHeader = {};
313
+ this.writePolicy = _types.WritePolicy.OVERWRITE;
224
314
  this.processedCount = 0;
225
315
  this.totalCount = 0;
226
316
  this.result = {
227
317
  succeeded: 0,
228
- failed: 0,
229
- errors: []
318
+ failed: 0
230
319
  };
231
320
  this.isBackingUp = false;
232
321
  this.isRestoring = false;
233
322
  this.restoreFn = null;
234
323
  this._cancelled = false;
235
- this._failedRowIndices = [];
324
+ this._jobId = null;
236
325
  }
237
326
  async close() {
238
- if (this.currentStep === 'progress') {
327
+ // While on the progress step we treat close as a cancel — terminate the job
328
+ // and restore the backup — UNLESS the import already succeeded. On the
329
+ // success path `startImport` lingers ~600ms on the completed bar with the
330
+ // step still 'progress'; closing during that window must NOT restore (it
331
+ // would undo a clean import). A successful close just closes the modal and
332
+ // lets the success path do its collection refresh.
333
+ if (this.currentStep === 'progress' && !this.importStatus.isSuccess) {
239
334
  var _this$_abortControlle2;
240
335
  this._cancelled = true;
241
336
  (_this$_abortControlle2 = this._abortController) == null || _this$_abortControlle2.abort();
337
+
338
+ // Stop the server-side job. It does not roll back rows already written —
339
+ // that's what the backup restore below is for.
340
+ if (this._jobId) {
341
+ try {
342
+ await this.hubJobClient.terminateJob(this._jobId);
343
+ } catch (error) {
344
+ console.error('Terminate import job failed', error);
345
+ }
346
+ }
242
347
  if (!this.isBackingUp) {
243
348
  await this._restoreBackup();
244
349
  }
@@ -325,11 +430,26 @@ class ImportState {
325
430
  _columnValues(columnIndex) {
326
431
  return this.csvRows.slice(0, _detectFieldType.TYPE_SAMPLE_SIZE).map(row => row[columnIndex] ?? '');
327
432
  }
433
+
434
+ // Puts the `_id`-mapped column first (CAIRO-4418), preserving CSV order for the
435
+ // rest. Returns a fresh array so the snapshot doesn't alias `csvHeaders`.
436
+ _orderHeadersWithIdFirst(headers) {
437
+ const idHeader = this.idMappedHeader;
438
+ return idHeader != null ? [idHeader, ...headers.filter(header => header !== idHeader)] : [...headers];
439
+ }
328
440
  applyParsedCsv(fileName, parsedCsv) {
329
441
  this.fileName = fileName;
330
442
  this.csvHeaders = parsedCsv.headers;
331
443
  this.csvRows = parsedCsv.rows;
332
444
  this.mappingByHeader = this.getDefaultMapping(parsedCsv.headers);
445
+ // Snapshot the row order for this file with the auto-mapped `_id` column
446
+ // first. Reads `idMappedHeader`, so it must run after `mappingByHeader` is
447
+ // set. Kept stable until the next file so mapping edits don't reorder rows.
448
+ this.orderedHeaders = this._orderHeadersWithIdFirst(parsedCsv.headers);
449
+ // A new file is a fresh import: start from the default policy so a prior
450
+ // file's Skip choice doesn't silently carry over (only the new-file path
451
+ // resets — remapping `_id` within the same file keeps the user's choice).
452
+ this.writePolicy = _types.WritePolicy.OVERWRITE;
333
453
  this.currentStep = 'configuration';
334
454
  }
335
455
  async onFileSelected(file) {
@@ -382,6 +502,7 @@ class ImportState {
382
502
  this.fileName = file.name;
383
503
  this.csvHeaders = [];
384
504
  this.csvRows = [];
505
+ this.orderedHeaders = [];
385
506
  this.mappingByHeader = {};
386
507
  this.currentStep = 'upload';
387
508
  });
@@ -404,12 +525,11 @@ class ImportState {
404
525
  }
405
526
  _showPartialSuccessToast() {
406
527
  var _this$showToast3;
407
- const failed = this._failedRowIndices.length;
408
528
  (_this$showToast3 = this.showToast) == null || _this$showToast3.call(this, {
409
529
  type: 'WARNING',
410
530
  message: this.translate('cairo.import.partialSuccessToast', {
411
- failed: String(failed),
412
- imported: String(this.totalCount - failed)
531
+ failed: String(this.result.failed),
532
+ imported: String(this.result.succeeded)
413
533
  }),
414
534
  action: {
415
535
  text: this.translate('cairo.import.downloadFailedRows'),
@@ -418,12 +538,70 @@ class ImportState {
418
538
  }
419
539
  });
420
540
  }
421
- _downloadFailedRows() {
541
+
542
+ /**
543
+ * Builds a plain, re-importable CSV of the rows the server rejected — the
544
+ * original headers plus just the failed rows — and hands it to `downloadCsv`.
545
+ * The failed rows come from the job's movement logs (which row failed), mapped
546
+ * back to the parsed `csvRows` we still hold in memory.
547
+ */
548
+ async _downloadFailedRows() {
422
549
  var _this$downloadCsv;
423
- const failedRows = this._failedRowIndices.map(index => this.csvRows[index]);
550
+ const jobId = this._jobId;
551
+ if (!jobId) {
552
+ return;
553
+ }
554
+ let indices;
555
+ try {
556
+ indices = await this._fetchFailedRowIndices(jobId);
557
+ } catch (error) {
558
+ console.error('Fetching failed rows report failed', error);
559
+ return;
560
+ }
561
+ const failedRows = indices.map(index => this.csvRows[index]).filter(row => row != null);
562
+ if (failedRows.length === 0) {
563
+ return;
564
+ }
424
565
  const content = (0, _parseCsv.unparseCsv)(this.csvHeaders, failedRows);
425
566
  (_this$downloadCsv = this.downloadCsv) == null || _this$downloadCsv.call(this, content, `failed-${this.fileName || 'import.csv'}`);
426
567
  }
568
+
569
+ // Pages through the job's movement logs and returns the `csvRows` indices of
570
+ // the rows that failed. data-hub's CSV decoder
571
+ // (wix-data-hub: packages/wix-data-hub-executor-c/src/plugins/formats/csv/
572
+ // csv-binary-decoder.ts) parses with the `csv-parse` library (`columns: true`)
573
+ // and sets `sourceItemId = "row ${++index + 1}"`, incrementing once **per
574
+ // record** — not per physical line. So N is the 1-based record number with
575
+ // the header consumed as columns: the first data record is `"row 2"` →
576
+ // `csvRows[0]`, i.e. `csvRows[N - 2]`. Because `csv-parse` is RFC-4180,
577
+ // quoted cells containing embedded newlines are a single record, so they do
578
+ // NOT shift the numbering. Out-of-range indices are guarded below.
579
+ async _fetchFailedRowIndices(jobId) {
580
+ const indices = [];
581
+ let cursor;
582
+ do {
583
+ var _pagingMetadata$curso;
584
+ const {
585
+ logs,
586
+ pagingMetadata
587
+ } = await this.hubJobClient.listMovementLogs(jobId, cursor ? {
588
+ cursor
589
+ } : undefined);
590
+ for (const log of logs ?? []) {
591
+ var _exec;
592
+ if (!log.failure || log.sourceItemId == null) {
593
+ continue;
594
+ }
595
+ const fileLine = Number((_exec = /\d+/.exec(log.sourceItemId)) == null ? void 0 : _exec[0]);
596
+ const index = fileLine - 2;
597
+ if (Number.isInteger(index) && index >= 0 && index < this.csvRows.length) {
598
+ indices.push(index);
599
+ }
600
+ }
601
+ cursor = (pagingMetadata == null ? void 0 : pagingMetadata.hasNext) && ((_pagingMetadata$curso = pagingMetadata.cursors) == null ? void 0 : _pagingMetadata$curso.next) || undefined;
602
+ } while (cursor);
603
+ return indices;
604
+ }
427
605
  setMapping(header, fieldId) {
428
606
  this.mappingByHeader = {
429
607
  ...this.mappingByHeader,
@@ -444,56 +622,158 @@ class ImportState {
444
622
  }
445
623
  };
446
624
  }
447
- async _uploadBatches(batches) {
448
- let processedRows = 0;
449
- let receivedOkResponse = false;
450
- const failedRowIndices = [];
451
- for (const batch of batches) {
452
- if (this._cancelled) {
453
- break;
625
+
626
+ /**
627
+ * Persists the result of the "Edit field" editor for a column's pending new
628
+ * field: the (possibly overridden) type, an optional display-name override,
629
+ * and the per-type configuration. No-op if the column isn't currently mapped
630
+ * to a new field.
631
+ */
632
+ setNewFieldConfig(header, edit) {
633
+ const mapping = this.mappingByHeader[header];
634
+ if ((mapping == null ? void 0 : mapping.kind) !== 'new') {
635
+ return;
636
+ }
637
+ this.mappingByHeader = {
638
+ ...this.mappingByHeader,
639
+ [header]: {
640
+ kind: 'new',
641
+ fieldType: edit.fieldType,
642
+ ...(edit.displayName != null && {
643
+ displayName: edit.displayName
644
+ }),
645
+ ...(edit.config && {
646
+ config: edit.config
647
+ })
454
648
  }
455
- const batchStart = processedRows;
456
- try {
457
- const batchResult = await this.bulkUploadItems(batch);
458
- receivedOkResponse = true;
459
- this.result = {
460
- succeeded: this.result.succeeded + batchResult.succeeded,
461
- failed: this.result.failed + batchResult.failed,
462
- errors: [...this.result.errors, ...batchResult.errors]
463
- };
649
+ };
650
+ }
464
651
 
465
- // Rows the server rejected within an otherwise-OK response. `rowIndex`
466
- // is relative to the batch, so offset it to a global `csvRows` index.
467
- for (const {
468
- rowIndex
469
- } of batchResult.errors) {
470
- failedRowIndices.push(batchStart + rowIndex);
471
- }
472
- } catch (error) {
473
- // Full batch failure: a batch threw before any OK response came back.
474
- // Surface it as an error state with a "Try again" action by rejecting
475
- // up to `startImport`, instead of reporting a partial success.
476
- if (!receivedOkResponse) {
477
- throw error;
478
- }
652
+ /**
653
+ * Marks which column's pending new field the field editor is currently
654
+ * editing, so a later `applyFieldEdit` (the editor's `onSubmit`) knows where to
655
+ * write the result.
656
+ */
657
+ beginFieldEdit(header) {
658
+ this._editingHeader = header;
659
+ }
479
660
 
480
- // A batch threw after some rows were already imported. Every row from
481
- // the throwing batch onward (including the batches we won't attempt)
482
- // counts as failed, on top of any rows already rejected via returned
483
- // errors in earlier batches.
484
- for (let index = batchStart; index < this.totalCount; index++) {
485
- failedRowIndices.push(index);
661
+ /**
662
+ * `onSubmit` handler for the field editor: maps the edited field spec into the
663
+ * column being edited (set via `beginFieldEdit`). No-op if nothing is being
664
+ * edited.
665
+ */
666
+ applyFieldEdit(field) {
667
+ if (this._editingHeader == null) {
668
+ return;
669
+ }
670
+ this.setNewFieldConfig(this._editingHeader, (0, _fieldEdit.toFieldEdit)(field));
671
+ }
672
+
673
+ /**
674
+ * Runs the server-side import: align the CSV to the resolved field keys,
675
+ * upload it, submit a data-movement job, then poll until terminal. Throws on
676
+ * a failed job so `startImport` surfaces the error state; partial success is
677
+ * reported via `result.failed` and the partial toast.
678
+ */
679
+ async _runImportJob(resolvedMapping) {
680
+ const csv = (0, _rewriteCsv.rewriteParsedCsvForImport)(this.csvHeaders, this.csvRows, resolvedMapping);
681
+ const file = new File([csv], this.fileName || 'import.csv', {
682
+ type: 'text/csv'
683
+ });
684
+ const fileId = await this.hubJobClient.uploadFile(file);
685
+ if (this._cancelled) {
686
+ return this.result;
687
+ }
688
+ const jobId = await this.hubJobClient.submitImportJob({
689
+ fileId,
690
+ name: this.fileName,
691
+ destination: this._destination()
692
+ });
693
+ (0, _mobx.runInAction)(() => {
694
+ this._jobId = jobId;
695
+ });
696
+ if (this._cancelled) {
697
+ // The user cancelled while `submitImportJob` was in flight, so `close()`
698
+ // saw no `_jobId` and couldn't terminate it. Terminate it here, otherwise
699
+ // the job keeps importing server-side after cancel (and would overwrite a
700
+ // restored backup).
701
+ await this.hubJobClient.terminateJob(jobId).catch(() => undefined);
702
+ return this.result;
703
+ }
704
+ const job = await (0, _importJobPoller.pollImportJob)(id => this.hubJobClient.getJob(id), jobId, {
705
+ isCancelled: () => this._cancelled,
706
+ onProgress: polled => this._applyJobProgress(polled)
707
+ });
708
+
709
+ // `null` means cancelled mid-poll — leave whatever progress we have.
710
+ if (!job) {
711
+ return this.result;
712
+ }
713
+ if (job.status === _types.JobStatus.FAILED) {
714
+ var _job$error;
715
+ // The job ran server-side, so a failure arrives on a 200 (the poll
716
+ // succeeded; the job didn't) with no HTTP error to carry the application
717
+ // error. Build a synthetic, HTTP-error-shaped error (the error handler
718
+ // reads the code from `response.data.details.applicationError`) and hand
719
+ // the throwing operation to the consumer's `onJobError` so THEY wrap it
720
+ // with their own `withErrorHandler` + error-code map — `job.error` is the
721
+ // consumer's application error (their import business logic), so only they
722
+ // can map its codes, and the map stays coupled to the `withErrorHandler`
723
+ // call as everywhere else (e.g. `fetchData`). The map is attached to the
724
+ // error object, which the progress step resolves via `getResolvedError`.
725
+ // Without a handler the same error still throws, falling back to the
726
+ // generic message.
727
+ const error = Object.assign(new Error(`ImportJob.${((_job$error = job.error) == null ? void 0 : _job$error.code) ?? 'failed'}`), {
728
+ response: {
729
+ data: {
730
+ details: {
731
+ applicationError: job.error
732
+ }
733
+ }
486
734
  }
487
- break;
488
- }
489
- processedRows += batch.length;
490
- (0, _mobx.runInAction)(() => {
491
- this.processedCount = Math.min(processedRows, this.totalCount);
492
735
  });
736
+ const run = () => Promise.reject(error);
737
+ if (this.onJobError) {
738
+ // The consumer wraps `run` with `withErrorHandler`; it re-rejects with
739
+ // `error` (now carrying their map), which propagates from here.
740
+ await this.onJobError(run);
741
+ }
742
+ throw error;
493
743
  }
494
- this._failedRowIndices = failedRowIndices;
744
+ this._applyJobProgress(job);
495
745
  return this.result;
496
746
  }
747
+
748
+ // Destination: the consumer's Wix Data collection. `writePolicy` is sent only
749
+ // when a CSV column is mapped to `_id` (the only case collisions can occur);
750
+ // otherwise it's omitted and the server applies its default (upsert by `_id`).
751
+ _destination() {
752
+ return {
753
+ wixDataCollection: {
754
+ collectionId: this.collectionId,
755
+ ...(this.idMappedHeader != null && {
756
+ writePolicy: this.writePolicy
757
+ })
758
+ }
759
+ };
760
+ }
761
+ _applyJobProgress(job) {
762
+ const progress = job.progress;
763
+ const succeeded = (progress == null ? void 0 : progress.itemsSuccessful) ?? 0;
764
+ const failed = (progress == null ? void 0 : progress.itemsRejected) ?? 0;
765
+ const current = (progress == null ? void 0 : progress.current) ?? succeeded + failed;
766
+
767
+ // `totalCount` is fixed up front in `startImport` from the CSV row count
768
+ // (one uploaded row per CSV row); polling only advances `processedCount`.
769
+ (0, _mobx.runInAction)(() => {
770
+ this.processedCount = Math.min(current, this.totalCount);
771
+ this.result = {
772
+ succeeded,
773
+ failed
774
+ };
775
+ });
776
+ }
497
777
  async _runBackup() {
498
778
  const backup = this.backup;
499
779
  if (!backup) {
@@ -550,16 +830,23 @@ class ImportState {
550
830
  } else if (mapping.kind === 'existing') {
551
831
  resolved[header] = mapping.fieldId;
552
832
  } else if (this.updateSchema) {
833
+ var _mapping$displayName;
834
+ // The user's name override (from the field editor) seeds the unique
835
+ // name/key derivation; otherwise fall back to the column header.
836
+ const baseName = ((_mapping$displayName = mapping.displayName) == null ? void 0 : _mapping$displayName.trim()) || header;
553
837
  const {
554
838
  displayName,
555
839
  key
556
- } = uniqueFieldNameAndKey(header, usedNames, usedKeys);
840
+ } = uniqueFieldNameAndKey(baseName, usedNames, usedKeys);
557
841
  usedKeys.add(key);
558
842
  usedNames.add(displayName);
559
843
  newFields.push({
560
844
  key,
561
845
  type: mapping.fieldType,
562
- displayName
846
+ displayName,
847
+ ...(mapping.config && {
848
+ config: mapping.config
849
+ })
563
850
  });
564
851
  resolved[header] = key;
565
852
  } else {
@@ -572,15 +859,6 @@ class ImportState {
572
859
  }
573
860
  return resolved;
574
861
  }
575
- _createRowsBatches(rows) {
576
- return rows.reduce((acc, row, index) => {
577
- if (index % this.batchSize === 0) {
578
- acc.push([]);
579
- }
580
- acc[acc.length - 1].push(row);
581
- return acc;
582
- }, []);
583
- }
584
862
  async startImport() {
585
863
  if (!this.hasMappings) {
586
864
  return;
@@ -588,8 +866,8 @@ class ImportState {
588
866
  this._cancelled = false;
589
867
  this._abortController = new AbortController();
590
868
 
591
- // One mapped row is produced per CSV row, so the total is known up front and
592
- // doesn't depend on field creation below.
869
+ // One uploaded row per CSV row (the rewrite drops columns, not rows), so the
870
+ // total is known up front from the parsed CSV and stays fixed through polling.
593
871
  const totalCount = this.csvRows.length;
594
872
  (0, _mobx.runInAction)(() => {
595
873
  this.currentStep = 'progress';
@@ -603,10 +881,9 @@ class ImportState {
603
881
  };
604
882
  this.result = {
605
883
  succeeded: 0,
606
- failed: 0,
607
- errors: []
884
+ failed: 0
608
885
  };
609
- this._failedRowIndices = [];
886
+ this._jobId = null;
610
887
  });
611
888
  const backupFailed = await this._runBackup();
612
889
  if (backupFailed) {
@@ -617,8 +894,8 @@ class ImportState {
617
894
  }
618
895
 
619
896
  // Create any new fields and resolve every column to its destination key
620
- // before building the payload. A creation failure surfaces as the error
621
- // state with a "Try again" CTA, like a first-batch upload failure.
897
+ // before uploading. A creation failure surfaces as the error state with a
898
+ // "Try again" CTA, like an upload/job failure.
622
899
  let resolvedMapping;
623
900
  try {
624
901
  resolvedMapping = await this._resolveFieldMapping();
@@ -634,9 +911,7 @@ class ImportState {
634
911
  if (this._cancelled) {
635
912
  return;
636
913
  }
637
- const rows = toMappedRows(this.csvRows, this.csvHeaders, resolvedMapping);
638
- const batches = this._createRowsBatches(rows);
639
- const promise = this._uploadBatches(batches);
914
+ const promise = this._runImportJob(resolvedMapping);
640
915
  this.importStatus = {
641
916
  ..._model.queryStatus.loading,
642
917
  promise
@@ -657,17 +932,16 @@ class ImportState {
657
932
  // briefly so the completion is perceptible even for very few rows. Skip
658
933
  // the wait when there are failed rows — that path closes and surfaces a
659
934
  // toast instead.
660
- if (this._failedRowIndices.length === 0) {
935
+ if (this.result.failed === 0) {
661
936
  await this._lingerOnCompletion();
662
937
  if (this._cancelled) {
663
938
  return;
664
939
  }
665
940
  }
666
941
  (0, _mobx.runInAction)(() => {
667
- // Any rows that didn't make it in whether rejected via a batch's
668
- // returned `errors` or left unimported when a batch threw — are
669
- // reported via a toast that offers them as a downloadable CSV.
670
- if (this._failedRowIndices.length > 0) {
942
+ // Rows the server rejected are reported via a toast (the downloadable
943
+ // failed-rows report is added by the failed-rows-report task).
944
+ if (this.result.failed > 0) {
671
945
  this._showPartialSuccessToast();
672
946
  }
673
947
  this.importModalState.close();