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