@open-mercato/core 0.6.2-develop.3330.1.8ad2b3b84c → 0.6.2-develop.3366.1.4692954e44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/sync_excel_upload/index.js +37 -0
- package/dist/generated/entities/sync_excel_upload/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +4 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +19 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/customers/search.js +3 -2
- package/dist/modules/customers/search.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +16 -0
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/di.js +2 -1
- package/dist/modules/data_sync/di.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-engine.js +178 -54
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-run-service.js +23 -0
- package/dist/modules/data_sync/lib/sync-run-service.js.map +3 -3
- package/dist/modules/integrations/backend/integrations/[id]/page.js +241 -55
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +3 -3
- package/dist/modules/sync_excel/acl.js +10 -0
- package/dist/modules/sync_excel/acl.js.map +7 -0
- package/dist/modules/sync_excel/api/import/route.js +164 -0
- package/dist/modules/sync_excel/api/import/route.js.map +7 -0
- package/dist/modules/sync_excel/api/preview/route.js +84 -0
- package/dist/modules/sync_excel/api/preview/route.js.map +7 -0
- package/dist/modules/sync_excel/api/upload/route.js +131 -0
- package/dist/modules/sync_excel/api/upload/route.js.map +7 -0
- package/dist/modules/sync_excel/data/entities.js +79 -0
- package/dist/modules/sync_excel/data/entities.js.map +7 -0
- package/dist/modules/sync_excel/data/validators.js +58 -0
- package/dist/modules/sync_excel/data/validators.js.map +7 -0
- package/dist/modules/sync_excel/di.js +11 -0
- package/dist/modules/sync_excel/di.js.map +7 -0
- package/dist/modules/sync_excel/index.js +11 -0
- package/dist/modules/sync_excel/index.js.map +7 -0
- package/dist/modules/sync_excel/integration.js +36 -0
- package/dist/modules/sync_excel/integration.js.map +7 -0
- package/dist/modules/sync_excel/lib/adapters/customers.js +852 -0
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +7 -0
- package/dist/modules/sync_excel/lib/column-detector.js +77 -0
- package/dist/modules/sync_excel/lib/column-detector.js.map +7 -0
- package/dist/modules/sync_excel/lib/mapping.js +16 -0
- package/dist/modules/sync_excel/lib/mapping.js.map +7 -0
- package/dist/modules/sync_excel/lib/parser.js +125 -0
- package/dist/modules/sync_excel/lib/parser.js.map +7 -0
- package/dist/modules/sync_excel/lib/scope.js +44 -0
- package/dist/modules/sync_excel/lib/scope.js.map +7 -0
- package/dist/modules/sync_excel/lib/upload-storage.js +63 -0
- package/dist/modules/sync_excel/lib/upload-storage.js.map +7 -0
- package/dist/modules/sync_excel/migrations/Migration20260329181120_sync_excel.js +11 -0
- package/dist/modules/sync_excel/migrations/Migration20260329181120_sync_excel.js.map +7 -0
- package/dist/modules/sync_excel/setup.js +19 -0
- package/dist/modules/sync_excel/setup.js.map +7 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/target-options.js +293 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/target-options.js.map +7 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/widget.client.js +934 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/widget.client.js.map +7 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/widget.js +15 -0
- package/dist/modules/sync_excel/widgets/injection/upload-config/widget.js.map +7 -0
- package/dist/modules/sync_excel/widgets/injection-table.js +17 -0
- package/dist/modules/sync_excel/widgets/injection-table.js.map +7 -0
- package/generated/entities/sync_excel_upload/index.ts +17 -0
- package/generated/entities.ids.generated.ts +4 -0
- package/generated/entity-fields-registry.ts +19 -0
- package/package.json +7 -7
- package/src/modules/customers/search.ts +3 -2
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +18 -0
- package/src/modules/data_sync/di.ts +4 -1
- package/src/modules/data_sync/lib/adapter.ts +15 -0
- package/src/modules/data_sync/lib/sync-engine.ts +205 -52
- package/src/modules/data_sync/lib/sync-run-service.ts +24 -0
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +220 -15
- package/src/modules/integrations/i18n/de.json +9 -0
- package/src/modules/integrations/i18n/en.json +9 -0
- package/src/modules/integrations/i18n/es.json +9 -0
- package/src/modules/integrations/i18n/pl.json +9 -0
- package/src/modules/sync_excel/acl.ts +6 -0
- package/src/modules/sync_excel/api/import/route.ts +182 -0
- package/src/modules/sync_excel/api/preview/route.ts +89 -0
- package/src/modules/sync_excel/api/upload/route.ts +144 -0
- package/src/modules/sync_excel/data/entities.ts +61 -0
- package/src/modules/sync_excel/data/validators.ts +58 -0
- package/src/modules/sync_excel/di.ts +9 -0
- package/src/modules/sync_excel/i18n/de.json +107 -0
- package/src/modules/sync_excel/i18n/en.json +107 -0
- package/src/modules/sync_excel/i18n/es.json +107 -0
- package/src/modules/sync_excel/i18n/pl.json +107 -0
- package/src/modules/sync_excel/index.ts +7 -0
- package/src/modules/sync_excel/integration.ts +31 -0
- package/src/modules/sync_excel/lib/adapters/customers.ts +1097 -0
- package/src/modules/sync_excel/lib/column-detector.ts +91 -0
- package/src/modules/sync_excel/lib/mapping.ts +19 -0
- package/src/modules/sync_excel/lib/parser.ts +165 -0
- package/src/modules/sync_excel/lib/scope.ts +63 -0
- package/src/modules/sync_excel/lib/upload-storage.ts +79 -0
- package/src/modules/sync_excel/migrations/.snapshot-open-mercato.json +199 -0
- package/src/modules/sync_excel/migrations/Migration20260329181120_sync_excel.ts +10 -0
- package/src/modules/sync_excel/setup.ts +20 -0
- package/src/modules/sync_excel/widgets/injection/upload-config/target-options.ts +351 -0
- package/src/modules/sync_excel/widgets/injection/upload-config/widget.client.tsx +1224 -0
- package/src/modules/sync_excel/widgets/injection/upload-config/widget.ts +30 -0
- package/src/modules/sync_excel/widgets/injection-table.ts +15 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import type { CustomFieldDefDto } from '@open-mercato/ui/backend/utils/customFieldDefs'
|
|
2
|
+
import type { FieldMapping, FieldMappingDedupeRole, FieldMappingKind } from '../../../../data_sync/lib/adapter'
|
|
3
|
+
|
|
4
|
+
export const SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS = [
|
|
5
|
+
'customers:customer_entity',
|
|
6
|
+
'customers:customer_person_profile',
|
|
7
|
+
] as const
|
|
8
|
+
|
|
9
|
+
export type SuggestedMapping = {
|
|
10
|
+
entityType: 'customers.person'
|
|
11
|
+
matchStrategy: 'externalId' | 'email' | 'custom'
|
|
12
|
+
matchField?: string
|
|
13
|
+
fields: FieldMapping[]
|
|
14
|
+
unmappedColumns: string[]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type MappingTargetOption = {
|
|
18
|
+
value: string
|
|
19
|
+
labelKey?: string
|
|
20
|
+
fallback: string
|
|
21
|
+
mappingKind: FieldMappingKind
|
|
22
|
+
dedupeRole?: FieldMappingDedupeRole
|
|
23
|
+
matchTokens: string[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const CORE_TARGET_OPTIONS: MappingTargetOption[] = [
|
|
27
|
+
{
|
|
28
|
+
value: 'person.externalId',
|
|
29
|
+
labelKey: 'sync_excel.mapping.targets.externalId',
|
|
30
|
+
fallback: 'External ID',
|
|
31
|
+
mappingKind: 'external_id',
|
|
32
|
+
dedupeRole: 'primary',
|
|
33
|
+
matchTokens: ['external id', 'record id', 'lead id'],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
value: 'person.firstName',
|
|
37
|
+
labelKey: 'sync_excel.mapping.targets.firstName',
|
|
38
|
+
fallback: 'First name',
|
|
39
|
+
mappingKind: 'core',
|
|
40
|
+
matchTokens: ['first name', 'firstname', 'given name'],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
value: 'person.lastName',
|
|
44
|
+
labelKey: 'sync_excel.mapping.targets.lastName',
|
|
45
|
+
fallback: 'Last name',
|
|
46
|
+
mappingKind: 'core',
|
|
47
|
+
matchTokens: ['last name', 'lastname', 'surname', 'family name'],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
value: 'person.displayName',
|
|
51
|
+
labelKey: 'sync_excel.mapping.targets.displayName',
|
|
52
|
+
fallback: 'Display name',
|
|
53
|
+
mappingKind: 'core',
|
|
54
|
+
matchTokens: ['display name', 'lead name', 'full name', 'name'],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
value: 'person.primaryEmail',
|
|
58
|
+
labelKey: 'sync_excel.mapping.targets.primaryEmail',
|
|
59
|
+
fallback: 'Primary email',
|
|
60
|
+
mappingKind: 'core',
|
|
61
|
+
dedupeRole: 'secondary',
|
|
62
|
+
matchTokens: ['email', 'primary email', 'email address'],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
value: 'person.primaryPhone',
|
|
66
|
+
labelKey: 'sync_excel.mapping.targets.primaryPhone',
|
|
67
|
+
fallback: 'Primary phone',
|
|
68
|
+
mappingKind: 'core',
|
|
69
|
+
matchTokens: ['phone', 'primary phone', 'mobile', 'mobile phone', 'telephone'],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
value: 'person.jobTitle',
|
|
73
|
+
labelKey: 'sync_excel.mapping.targets.jobTitle',
|
|
74
|
+
fallback: 'Job title',
|
|
75
|
+
mappingKind: 'core',
|
|
76
|
+
matchTokens: ['job title', 'title', 'position'],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
value: 'person.status',
|
|
80
|
+
labelKey: 'sync_excel.mapping.targets.status',
|
|
81
|
+
fallback: 'Status',
|
|
82
|
+
mappingKind: 'core',
|
|
83
|
+
matchTokens: ['status', 'lead status'],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
value: 'person.source',
|
|
87
|
+
labelKey: 'sync_excel.mapping.targets.source',
|
|
88
|
+
fallback: 'Source',
|
|
89
|
+
mappingKind: 'core',
|
|
90
|
+
matchTokens: ['source', 'lead source'],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
value: 'person.description',
|
|
94
|
+
labelKey: 'sync_excel.mapping.targets.description',
|
|
95
|
+
fallback: 'Description',
|
|
96
|
+
mappingKind: 'core',
|
|
97
|
+
matchTokens: ['description', 'notes', 'comment'],
|
|
98
|
+
},
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
const ADDRESS_TARGET_OPTIONS: MappingTargetOption[] = [
|
|
102
|
+
{
|
|
103
|
+
value: 'address.name',
|
|
104
|
+
labelKey: 'sync_excel.mapping.targets.addressName',
|
|
105
|
+
fallback: 'Address label',
|
|
106
|
+
mappingKind: 'core',
|
|
107
|
+
matchTokens: ['address label', 'address name', 'label'],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
value: 'address.purpose',
|
|
111
|
+
labelKey: 'sync_excel.mapping.targets.addressPurpose',
|
|
112
|
+
fallback: 'Address purpose',
|
|
113
|
+
mappingKind: 'core',
|
|
114
|
+
matchTokens: ['address purpose', 'address type'],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
value: 'address.companyName',
|
|
118
|
+
labelKey: 'sync_excel.mapping.targets.addressCompanyName',
|
|
119
|
+
fallback: 'Address company',
|
|
120
|
+
mappingKind: 'core',
|
|
121
|
+
matchTokens: ['address company', 'address company name'],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
value: 'address.addressLine1',
|
|
125
|
+
labelKey: 'sync_excel.mapping.targets.addressLine1',
|
|
126
|
+
fallback: 'Address line 1',
|
|
127
|
+
mappingKind: 'core',
|
|
128
|
+
matchTokens: ['address line 1', 'street address', 'street', 'street 1'],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
value: 'address.addressLine2',
|
|
132
|
+
labelKey: 'sync_excel.mapping.targets.addressLine2',
|
|
133
|
+
fallback: 'Address line 2',
|
|
134
|
+
mappingKind: 'core',
|
|
135
|
+
matchTokens: ['address line 2', 'street 2'],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
value: 'address.buildingNumber',
|
|
139
|
+
labelKey: 'sync_excel.mapping.targets.buildingNumber',
|
|
140
|
+
fallback: 'Building number',
|
|
141
|
+
mappingKind: 'core',
|
|
142
|
+
matchTokens: ['building number', 'building no', 'house number'],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
value: 'address.flatNumber',
|
|
146
|
+
labelKey: 'sync_excel.mapping.targets.flatNumber',
|
|
147
|
+
fallback: 'Flat number',
|
|
148
|
+
mappingKind: 'core',
|
|
149
|
+
matchTokens: ['flat number', 'apartment number', 'unit number'],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
value: 'address.city',
|
|
153
|
+
labelKey: 'sync_excel.mapping.targets.city',
|
|
154
|
+
fallback: 'City',
|
|
155
|
+
mappingKind: 'core',
|
|
156
|
+
matchTokens: ['city', 'town'],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
value: 'address.region',
|
|
160
|
+
labelKey: 'sync_excel.mapping.targets.region',
|
|
161
|
+
fallback: 'Region / State',
|
|
162
|
+
mappingKind: 'core',
|
|
163
|
+
matchTokens: ['region', 'state', 'province'],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
value: 'address.postalCode',
|
|
167
|
+
labelKey: 'sync_excel.mapping.targets.postalCode',
|
|
168
|
+
fallback: 'Postal code',
|
|
169
|
+
mappingKind: 'core',
|
|
170
|
+
matchTokens: ['postal code', 'zip code', 'postcode'],
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
value: 'address.country',
|
|
174
|
+
labelKey: 'sync_excel.mapping.targets.country',
|
|
175
|
+
fallback: 'Country',
|
|
176
|
+
mappingKind: 'core',
|
|
177
|
+
matchTokens: ['country'],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
value: 'address.latitude',
|
|
181
|
+
labelKey: 'sync_excel.mapping.targets.latitude',
|
|
182
|
+
fallback: 'Latitude',
|
|
183
|
+
mappingKind: 'core',
|
|
184
|
+
matchTokens: ['latitude', 'lat'],
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
value: 'address.longitude',
|
|
188
|
+
labelKey: 'sync_excel.mapping.targets.longitude',
|
|
189
|
+
fallback: 'Longitude',
|
|
190
|
+
mappingKind: 'core',
|
|
191
|
+
matchTokens: ['longitude', 'lng', 'lon'],
|
|
192
|
+
},
|
|
193
|
+
]
|
|
194
|
+
|
|
195
|
+
function normalizeMatchToken(value: string): string {
|
|
196
|
+
return value
|
|
197
|
+
.trim()
|
|
198
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
199
|
+
.toLowerCase()
|
|
200
|
+
.replace(/[_-]+/g, ' ')
|
|
201
|
+
.replace(/\s+/g, ' ')
|
|
202
|
+
.trim()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function titleizeKey(key: string): string {
|
|
206
|
+
return key
|
|
207
|
+
.split('_')
|
|
208
|
+
.map((part) => (part.length > 0 ? part[0].toUpperCase() + part.slice(1) : part))
|
|
209
|
+
.join(' ')
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function scoreCustomFieldDefinition(def: CustomFieldDefDto, entityIndex: number): {
|
|
213
|
+
base: number
|
|
214
|
+
penalty: number
|
|
215
|
+
entityIndex: number
|
|
216
|
+
} {
|
|
217
|
+
const listVisibleScore = def.listVisible === false ? 0 : 1
|
|
218
|
+
const formEditableScore = def.formEditable === false ? 0 : 1
|
|
219
|
+
const filterableScore = def.filterable ? 1 : 0
|
|
220
|
+
const kindScore = (() => {
|
|
221
|
+
switch (def.kind) {
|
|
222
|
+
case 'dictionary':
|
|
223
|
+
return 8
|
|
224
|
+
case 'relation':
|
|
225
|
+
return 6
|
|
226
|
+
case 'select':
|
|
227
|
+
return 4
|
|
228
|
+
case 'multiline':
|
|
229
|
+
return 3
|
|
230
|
+
case 'boolean':
|
|
231
|
+
case 'integer':
|
|
232
|
+
case 'float':
|
|
233
|
+
return 2
|
|
234
|
+
default:
|
|
235
|
+
return 1
|
|
236
|
+
}
|
|
237
|
+
})()
|
|
238
|
+
const optionsBonus = Array.isArray(def.options) && def.options.length > 0 ? 2 : 0
|
|
239
|
+
const dictionaryBonus = typeof def.dictionaryId === 'string' && def.dictionaryId.trim().length > 0 ? 5 : 0
|
|
240
|
+
return {
|
|
241
|
+
base: (listVisibleScore * 16) + (formEditableScore * 8) + (filterableScore * 4) + kindScore + optionsBonus + dictionaryBonus,
|
|
242
|
+
penalty: typeof def.priority === 'number' ? def.priority : 0,
|
|
243
|
+
entityIndex,
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function buildCustomFieldOption(def: CustomFieldDefDto): MappingTargetOption {
|
|
248
|
+
const fallback = typeof def.label === 'string' && def.label.trim().length > 0
|
|
249
|
+
? def.label.trim()
|
|
250
|
+
: titleizeKey(def.key)
|
|
251
|
+
const normalizedTokens = Array.from(new Set([
|
|
252
|
+
normalizeMatchToken(def.key),
|
|
253
|
+
normalizeMatchToken(fallback),
|
|
254
|
+
].filter((value) => value.length > 0)))
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
value: `cf:${def.key}`,
|
|
258
|
+
fallback,
|
|
259
|
+
mappingKind: 'custom_field',
|
|
260
|
+
matchTokens: normalizedTokens,
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function selectPreferredCustomFieldDefs(customFieldDefs: CustomFieldDefDto[]): CustomFieldDefDto[] {
|
|
265
|
+
const entityOrder = new Map<string, number>()
|
|
266
|
+
SYNC_EXCEL_PEOPLE_CUSTOM_FIELD_ENTITY_IDS.forEach((entityId, index) => entityOrder.set(entityId, index))
|
|
267
|
+
const bestByKey = new Map<string, { def: CustomFieldDefDto; score: { base: number; penalty: number; entityIndex: number } }>()
|
|
268
|
+
|
|
269
|
+
for (const def of customFieldDefs) {
|
|
270
|
+
if (typeof def.key !== 'string' || def.key.trim().length === 0) continue
|
|
271
|
+
const score = scoreCustomFieldDefinition(def, entityOrder.get(def.entityId ?? '') ?? Number.MAX_SAFE_INTEGER)
|
|
272
|
+
const existing = bestByKey.get(def.key)
|
|
273
|
+
const isBetter = !existing
|
|
274
|
+
|| score.base > existing.score.base
|
|
275
|
+
|| (
|
|
276
|
+
score.base === existing.score.base
|
|
277
|
+
&& (score.penalty < existing.score.penalty
|
|
278
|
+
|| (score.penalty === existing.score.penalty && score.entityIndex < existing.score.entityIndex))
|
|
279
|
+
)
|
|
280
|
+
if (isBetter) {
|
|
281
|
+
bestByKey.set(def.key, { def, score })
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return Array.from(bestByKey.values()).map((entry) => entry.def)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function buildPeopleTargetOptions(customFieldDefs: CustomFieldDefDto[]): MappingTargetOption[] {
|
|
289
|
+
const customOptions = selectPreferredCustomFieldDefs(customFieldDefs).map(buildCustomFieldOption)
|
|
290
|
+
return [...CORE_TARGET_OPTIONS, ...ADDRESS_TARGET_OPTIONS, ...customOptions]
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function buildPeopleSuggestedMapping(
|
|
294
|
+
headers: string[],
|
|
295
|
+
suggestedMapping: SuggestedMapping,
|
|
296
|
+
customFieldDefs: CustomFieldDefDto[],
|
|
297
|
+
): SuggestedMapping {
|
|
298
|
+
const fields = [...suggestedMapping.fields]
|
|
299
|
+
const usedExternalFields = new Set(fields.map((field) => field.externalField))
|
|
300
|
+
const usedTargetFields = new Set(fields.map((field) => field.localField))
|
|
301
|
+
const supplementalTargetOptions = buildPeopleTargetOptions(customFieldDefs).filter(
|
|
302
|
+
(option) => option.mappingKind === 'custom_field' || option.value.startsWith('address.'),
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
for (const header of headers) {
|
|
306
|
+
if (usedExternalFields.has(header)) continue
|
|
307
|
+
const normalizedHeader = normalizeMatchToken(header)
|
|
308
|
+
const matchedOption = supplementalTargetOptions.find((option) => {
|
|
309
|
+
if (usedTargetFields.has(option.value)) return false
|
|
310
|
+
return option.matchTokens.includes(normalizedHeader)
|
|
311
|
+
})
|
|
312
|
+
if (!matchedOption) continue
|
|
313
|
+
|
|
314
|
+
fields.push({
|
|
315
|
+
externalField: header,
|
|
316
|
+
localField: matchedOption.value,
|
|
317
|
+
mappingKind: matchedOption.mappingKind,
|
|
318
|
+
})
|
|
319
|
+
usedExternalFields.add(header)
|
|
320
|
+
usedTargetFields.add(matchedOption.value)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
...suggestedMapping,
|
|
325
|
+
fields,
|
|
326
|
+
unmappedColumns: headers.filter((header) => !usedExternalFields.has(header)),
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function buildSuggestedMappingSignature(headers: string[], suggestedMapping: SuggestedMapping): string {
|
|
331
|
+
return JSON.stringify({
|
|
332
|
+
headers,
|
|
333
|
+
matchStrategy: suggestedMapping.matchStrategy,
|
|
334
|
+
matchField: suggestedMapping.matchField ?? null,
|
|
335
|
+
fields: suggestedMapping.fields.map((field) => ({
|
|
336
|
+
externalField: field.externalField,
|
|
337
|
+
localField: field.localField,
|
|
338
|
+
mappingKind: field.mappingKind ?? null,
|
|
339
|
+
dedupeRole: field.dedupeRole ?? null,
|
|
340
|
+
})),
|
|
341
|
+
})
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function findMappingTargetOption(
|
|
345
|
+
targetOptions: MappingTargetOption[],
|
|
346
|
+
targetField: string,
|
|
347
|
+
): MappingTargetOption | undefined {
|
|
348
|
+
return targetOptions.find((option) => option.value === targetField)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export { normalizeMatchToken }
|