@bagelink/vue 1.14.15 → 1.15.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.
- package/dist/components/Alert.vue.d.ts.map +1 -1
- package/dist/components/Badge.vue.d.ts.map +1 -1
- package/dist/components/Btn.vue.d.ts.map +1 -1
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/Image.vue.d.ts.map +1 -1
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/MapEmbed/Index.vue.d.ts.map +1 -1
- package/dist/components/Pagination.vue.d.ts.map +1 -1
- package/dist/components/Swiper.vue.d.ts.map +1 -1
- package/dist/components/Toast.vue.d.ts.map +1 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/AppContent.vue.d.ts.map +1 -1
- package/dist/components/layout/AppLayout.vue.d.ts.map +1 -1
- package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -1
- package/dist/components/layout/Panel.vue.d.ts.map +1 -1
- package/dist/components/layout/Resizable.vue.d.ts.map +1 -1
- package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
- package/dist/components/layout/appLayoutContext.d.ts +24 -0
- package/dist/components/layout/appLayoutContext.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useDevice.d.ts.map +1 -1
- package/dist/composables/useEscapeKey.d.ts +12 -0
- package/dist/composables/useEscapeKey.d.ts.map +1 -0
- package/dist/composables/useSchemaField.d.ts.map +1 -1
- package/dist/composables/useTheme.d.ts.map +1 -1
- package/dist/form-flow/FormFlow.vue.d.ts.map +1 -1
- package/dist/form-flow/form-flow.d.ts.map +1 -1
- package/dist/index.cjs +203 -207
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +25819 -28870
- package/dist/style.css +1 -1
- package/dist/types/BagelForm.d.ts.map +1 -1
- package/dist/types/BtnOptions.d.ts.map +1 -1
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +3 -6
- package/src/components/Alert.vue +34 -14
- package/src/components/Badge.vue +145 -22
- package/src/components/Btn.vue +43 -31
- package/src/components/Dropdown.vue +5 -12
- package/src/components/FilterQuery.vue +1 -1
- package/src/components/Image.vue +3 -2
- package/src/components/JSONSchema.vue +2 -2
- package/src/components/JsonBuilder.vue +1 -1
- package/src/components/ListItem.vue +1 -3
- package/src/components/MapEmbed/Index.vue +10 -9
- package/src/components/NavBar.vue +2 -2
- package/src/components/Spreadsheet/Index.vue +1 -1
- package/src/components/Swiper.vue +3 -1
- package/src/components/Toast.vue +23 -8
- package/src/components/calendar/Index.vue +4 -4
- package/src/components/calendar/views/MonthView.vue +3 -3
- package/src/components/form/index.ts +0 -4
- package/src/components/form/inputs/EmailInput.vue +1 -1
- package/src/components/form/inputs/NumberInput.vue +1 -1
- package/src/components/form/inputs/OTP.vue +2 -2
- package/src/components/form/inputs/SelectInput.vue +3 -3
- package/src/components/form/inputs/TelInput.vue +2 -2
- package/src/components/form/inputs/TextInput.vue +1 -1
- package/src/components/form/inputs/Upload/upload.css +2 -2
- package/src/components/index.ts +2 -6
- package/src/components/layout/AppContent.vue +5 -19
- package/src/components/layout/AppLayout.vue +47 -18
- package/src/components/layout/AppSidebar.vue +16 -33
- package/src/components/layout/Resizable.vue +5 -2
- package/src/components/layout/TabsNav.vue +5 -5
- package/src/components/layout/appLayoutContext.ts +44 -0
- package/src/components/layout/index.ts +2 -0
- package/src/components/lightbox/Lightbox.vue +3 -9
- package/src/composables/index.ts +1 -0
- package/src/composables/useDevice.ts +2 -1
- package/src/composables/useEscapeKey.ts +56 -0
- package/src/composables/useSchemaField.ts +2 -17
- package/src/composables/useTheme.ts +23 -19
- package/src/form-flow/FormFlow.vue +2 -0
- package/src/form-flow/form-flow.ts +7 -0
- package/src/index.ts +0 -2
- package/src/styles/inputs.css +1 -1
- package/src/types/BagelForm.ts +46 -151
- package/src/types/BtnOptions.ts +5 -3
- package/src/utils/constants.ts +7 -0
- package/src/utils/index.ts +19 -3
- package/src/utils/sizeParsing.ts +5 -5
- package/vite.config.ts +5 -1
- package/src/components/Carousel.vue +0 -724
- package/src/components/ImportData.vue +0 -1749
- package/src/components/Pill.vue +0 -150
- package/src/components/Slider.vue +0 -1446
- package/src/components/Title.vue +0 -23
- package/src/components/ToolBar.vue +0 -9
- package/src/components/form/BagelForm.vue +0 -219
- package/src/components/form/BglFieldSet.vue +0 -14
- package/src/components/form/BglMultiStepForm.vue +0 -469
- package/src/components/form/FieldArray.vue +0 -422
- package/src/components/form/useBagelFormState.ts +0 -76
- package/src/composables/useFormField.ts +0 -38
- package/src/dialog/DialogOLD.vue +0 -358
- package/src/utils/BagelFormUtils.ts +0 -684
|
@@ -1,1749 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts" generic="T">
|
|
2
|
-
import type { BglFormSchemaT, Option } from '@bagelink/vue'
|
|
3
|
-
import type { MaybeRefOrGetter } from 'vue'
|
|
4
|
-
import type { Field } from '../types/BagelForm'
|
|
5
|
-
import {
|
|
6
|
-
Btn,
|
|
7
|
-
Card,
|
|
8
|
-
CheckInput,
|
|
9
|
-
Dialog,
|
|
10
|
-
Icon,
|
|
11
|
-
Pill,
|
|
12
|
-
SelectInput,
|
|
13
|
-
Spreadsheet,
|
|
14
|
-
useExcel,
|
|
15
|
-
DragOver,
|
|
16
|
-
useI18n,
|
|
17
|
-
} from '@bagelink/vue'
|
|
18
|
-
import { computed, reactive, ref, watch, watchEffect, toValue } from 'vue'
|
|
19
|
-
import { useSchemaField } from '../composables/useSchemaField'
|
|
20
|
-
import { formatString } from '../utils/strings'
|
|
21
|
-
import TextInput from './form/inputs/TextInput.vue'
|
|
22
|
-
|
|
23
|
-
const props = defineProps<{
|
|
24
|
-
schema?: MaybeRefOrGetter<BglFormSchemaT<T>>
|
|
25
|
-
title?: string
|
|
26
|
-
}>()
|
|
27
|
-
|
|
28
|
-
const emit = defineEmits<{
|
|
29
|
-
(e: 'processedData', data: T[]): void
|
|
30
|
-
}>()
|
|
31
|
-
|
|
32
|
-
const { $t } = useI18n()
|
|
33
|
-
|
|
34
|
-
interface SchemaItem {
|
|
35
|
-
id: string
|
|
36
|
-
label: string
|
|
37
|
-
$el?: string
|
|
38
|
-
required?: boolean
|
|
39
|
-
isArrayField?: boolean
|
|
40
|
-
parentField?: string
|
|
41
|
-
disabled?: boolean
|
|
42
|
-
disabledReason?: string
|
|
43
|
-
options?: Option[] // For enum options
|
|
44
|
-
dataType?: string // Add dataType field
|
|
45
|
-
attrs?: {
|
|
46
|
-
required?: boolean
|
|
47
|
-
schema?: SchemaItem[]
|
|
48
|
-
attrs?: {
|
|
49
|
-
required?: boolean
|
|
50
|
-
}
|
|
51
|
-
options?: Option[] // For enum options
|
|
52
|
-
dataType?: string // Add dataType in attrs
|
|
53
|
-
}
|
|
54
|
-
children?: SchemaItem[]
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Add interface for mapped row data
|
|
58
|
-
interface MappedRow {
|
|
59
|
-
[key: string]: any
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Interface for transformations
|
|
63
|
-
interface Transformation {
|
|
64
|
-
fieldId: string
|
|
65
|
-
sourceValue: any
|
|
66
|
-
targetValue: any
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Get Excel utilities from composable
|
|
70
|
-
const {
|
|
71
|
-
readSheetData,
|
|
72
|
-
getSheetNames,
|
|
73
|
-
isExcelSerialDate,
|
|
74
|
-
excelSerialDateToJSDate,
|
|
75
|
-
formatDate
|
|
76
|
-
} = useExcel()
|
|
77
|
-
|
|
78
|
-
// Data type constants
|
|
79
|
-
const DATA_TYPES = {
|
|
80
|
-
STRING: 'string',
|
|
81
|
-
NUMBER: 'number',
|
|
82
|
-
DATE: 'date',
|
|
83
|
-
DATETIME: 'datetime',
|
|
84
|
-
BOOLEAN: 'boolean'
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Data type options for selection
|
|
88
|
-
const dataTypeOptions = [
|
|
89
|
-
{ value: DATA_TYPES.STRING, label: 'Text (String)' },
|
|
90
|
-
{ value: DATA_TYPES.NUMBER, label: 'Number' },
|
|
91
|
-
{ value: DATA_TYPES.DATE, label: 'Date' },
|
|
92
|
-
{ value: DATA_TYPES.DATETIME, label: 'Date & Time' },
|
|
93
|
-
{ value: DATA_TYPES.BOOLEAN, label: 'Boolean' }
|
|
94
|
-
]
|
|
95
|
-
|
|
96
|
-
const file = ref<File | null>(null)
|
|
97
|
-
const fileData = ref<any[]>([])
|
|
98
|
-
const sheetNames = ref<string[]>([])
|
|
99
|
-
const selectedSheet = ref<string>('')
|
|
100
|
-
const hasHeaders = ref(true)
|
|
101
|
-
const isLoading = ref(false)
|
|
102
|
-
const showPreviewModal = ref(false)
|
|
103
|
-
const previewData = ref<any[]>([])
|
|
104
|
-
const mappingComplete = ref(false)
|
|
105
|
-
const fileHeaders = ref<string[]>([])
|
|
106
|
-
const showTransformDialog = ref(false)
|
|
107
|
-
const selectedTransformField = ref<SchemaItem | null>(null)
|
|
108
|
-
const showRelatedDialog = ref(false)
|
|
109
|
-
const selectedRelationField = ref<SchemaItem | null>(null)
|
|
110
|
-
const selectedSourceValue = ref('')
|
|
111
|
-
const selectedTargetValue = ref('')
|
|
112
|
-
const showRelatedTransformDialog = ref(false)
|
|
113
|
-
const selectedRelatedTransformField = ref<{ parentId: string, field: SchemaItem } | null>(null)
|
|
114
|
-
const selectedRelatedSourceValue = ref('')
|
|
115
|
-
const selectedRelatedTargetValue = ref('')
|
|
116
|
-
|
|
117
|
-
const fieldMapping = reactive<Record<string, string>>({})
|
|
118
|
-
const defaultValues = reactive<Record<string, any>>({})
|
|
119
|
-
const transformations = reactive<Record<string, Transformation[]>>({})
|
|
120
|
-
const fieldDataTypes = reactive<Record<string, string>>({})
|
|
121
|
-
const relatedFiles = reactive<Record<string, File | null>>({})
|
|
122
|
-
const relatedFileData = reactive<Record<string, any[]>>({})
|
|
123
|
-
const relatedFileMappings = reactive<Record<string, Record<string, string>>>({})
|
|
124
|
-
const relatedKeyField = reactive<Record<string, string>>({})
|
|
125
|
-
const parentKeyField = reactive<Record<string, string>>({})
|
|
126
|
-
const relatedFieldDataTypes = reactive<Record<string, string>>({})
|
|
127
|
-
const relatedDefaultValues = reactive<Record<string, Record<string, any>>>({})
|
|
128
|
-
const relatedTransformations = reactive<Record<string, Record<string, Transformation[]>>>({})
|
|
129
|
-
|
|
130
|
-
const formData = ref<any>({})
|
|
131
|
-
const { renderField } = useSchemaField<any>({
|
|
132
|
-
mode: 'form',
|
|
133
|
-
getFormData: () => formData.value,
|
|
134
|
-
onUpdateModelValue: (field: Field<any>, value: any) => {
|
|
135
|
-
if (!field.id) { return }
|
|
136
|
-
|
|
137
|
-
if (field.id.includes('.')) {
|
|
138
|
-
const [parentId, childId] = field.id.split('.')
|
|
139
|
-
if (!relatedDefaultValues[parentId]) {
|
|
140
|
-
relatedDefaultValues[parentId] = {}
|
|
141
|
-
}
|
|
142
|
-
relatedDefaultValues[parentId][childId] = value
|
|
143
|
-
} else {
|
|
144
|
-
defaultValues[field.id] = value
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
function getUniqueSourceValues(fieldId: string): any[] {
|
|
150
|
-
if (!fieldMapping[fieldId] || !fileData.value || fileData.value.length === 0) {
|
|
151
|
-
return []
|
|
152
|
-
}
|
|
153
|
-
const allValues = fileData.value
|
|
154
|
-
.map(row => row[fieldMapping[fieldId]])
|
|
155
|
-
.filter(value => value !== undefined && value !== null && value !== '')
|
|
156
|
-
const uniqueValues = [...new Set(allValues)]
|
|
157
|
-
|
|
158
|
-
return uniqueValues.filter((value) => {
|
|
159
|
-
if (!transformations[fieldId] || transformations[fieldId].length === 0) {
|
|
160
|
-
return true
|
|
161
|
-
}
|
|
162
|
-
return !transformations[fieldId].some(t => t.sourceValue == value || t.sourceValue === value.toString()
|
|
163
|
-
)
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const availableSourceValues = computed(() => {
|
|
168
|
-
if (!selectedTransformField.value || !selectedTransformField.value.id) {
|
|
169
|
-
return []
|
|
170
|
-
}
|
|
171
|
-
return getUniqueSourceValues(selectedTransformField.value.id)
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
const sourceValueOptions = computed(() => {
|
|
175
|
-
return availableSourceValues.value.map(value => ({
|
|
176
|
-
value: String(value),
|
|
177
|
-
label: String(value)
|
|
178
|
-
}))
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
function findMatchingTargetValue(sourceValue: string, options: Option[]): string | null {
|
|
182
|
-
if (!sourceValue || !options || !Array.isArray(options) || options.length === 0) { return null }
|
|
183
|
-
const lowerSourceValue = String(sourceValue).toLowerCase().trim()
|
|
184
|
-
const exactMatch = options.find((option) => {
|
|
185
|
-
const optionObj = typeof option === 'object' && option !== null ? option : { value: String(option), label: String(option) }
|
|
186
|
-
if (!optionObj) { return false }
|
|
187
|
-
const optionLabel = `${optionObj.label}`.toLowerCase().trim()
|
|
188
|
-
return optionLabel === lowerSourceValue
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
if (exactMatch) {
|
|
192
|
-
const optionObj = typeof exactMatch === 'object' && exactMatch !== null
|
|
193
|
-
? exactMatch
|
|
194
|
-
: { value: String(exactMatch), label: String(exactMatch) }
|
|
195
|
-
return String(optionObj.value)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Try more flexible matching if exact match fails
|
|
199
|
-
const fuzzyMatch = options.find((option) => {
|
|
200
|
-
const optionObj = typeof option === 'object' && option !== null ? option : { value: String(option), label: String(option) }
|
|
201
|
-
if (!optionObj || typeof optionObj.label !== 'string') { return false }
|
|
202
|
-
|
|
203
|
-
const optionLabel = `${optionObj.label}`.toLowerCase().trim()
|
|
204
|
-
|
|
205
|
-
return lowerSourceValue.includes(optionLabel) || optionLabel.includes(lowerSourceValue)
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
if (fuzzyMatch) {
|
|
209
|
-
const optionObj = typeof fuzzyMatch === 'object' && fuzzyMatch !== null
|
|
210
|
-
? fuzzyMatch
|
|
211
|
-
: { value: String(fuzzyMatch), label: String(fuzzyMatch) }
|
|
212
|
-
return String(optionObj.value)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return null
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
watch(selectedSourceValue, (newValue) => {
|
|
219
|
-
if (selectedTransformField.value?.options) {
|
|
220
|
-
const matchedValue = findMatchingTargetValue(newValue, selectedTransformField.value.options)
|
|
221
|
-
if (matchedValue) {
|
|
222
|
-
selectedTargetValue.value = matchedValue
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
function addTransformation(fieldId: string) {
|
|
228
|
-
if (!transformations[fieldId]) {
|
|
229
|
-
transformations[fieldId] = []
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (selectedSourceValue.value && selectedTargetValue.value) {
|
|
233
|
-
const existingIndex = transformations[fieldId].findIndex(t => t.sourceValue === selectedSourceValue.value
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
if (existingIndex >= 0) {
|
|
237
|
-
transformations[fieldId][existingIndex].targetValue = selectedTargetValue.value
|
|
238
|
-
} else {
|
|
239
|
-
transformations[fieldId].push({
|
|
240
|
-
fieldId,
|
|
241
|
-
sourceValue: selectedSourceValue.value,
|
|
242
|
-
targetValue: selectedTargetValue.value
|
|
243
|
-
})
|
|
244
|
-
}
|
|
245
|
-
selectedSourceValue.value = ''
|
|
246
|
-
selectedTargetValue.value = ''
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function getAllFields(schema: any[]): any[] {
|
|
251
|
-
if (!schema || !Array.isArray(schema)) { return [] }
|
|
252
|
-
|
|
253
|
-
const allFields: any[] = []
|
|
254
|
-
const seenIds = new Set() // Keep track of field IDs we've already added
|
|
255
|
-
|
|
256
|
-
// Helper to add a field if it hasn't been seen before
|
|
257
|
-
function addFieldIfNew(field: any) {
|
|
258
|
-
if (field && field.id && field.label && !seenIds.has(field.id)) {
|
|
259
|
-
seenIds.add(field.id)
|
|
260
|
-
|
|
261
|
-
// Always create options array if it doesn't exist
|
|
262
|
-
if (!field.options) {
|
|
263
|
-
field.options = []
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Extract options if they exist in attrs
|
|
267
|
-
if (field.attrs && field.attrs.options && field.attrs.options.length > 0) {
|
|
268
|
-
field.options = field.attrs.options
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
allFields.push(field)
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Process each schema item
|
|
276
|
-
schema.forEach((item: any) => {
|
|
277
|
-
// Direct fields (like more_info.session_rate)
|
|
278
|
-
if (item && item.id && item.label) {
|
|
279
|
-
addFieldIfNew(item)
|
|
280
|
-
}
|
|
281
|
-
if (item && item.children && Array.isArray(item.children)) {
|
|
282
|
-
item.children.forEach((child: any) => {
|
|
283
|
-
if (child && child.id && child.label) {
|
|
284
|
-
if (child.$el === 'array' && child.attrs && child.attrs.schema) {
|
|
285
|
-
addFieldIfNew(child)
|
|
286
|
-
if (Array.isArray(child.attrs.schema)) {
|
|
287
|
-
child.attrs.schema.forEach((schemaItem: SchemaItem) => {
|
|
288
|
-
if (schemaItem && schemaItem.id && schemaItem.label) {
|
|
289
|
-
const qualifiedField = {
|
|
290
|
-
...schemaItem,
|
|
291
|
-
id: `${child.id}.${schemaItem.id}`,
|
|
292
|
-
parentField: child.id,
|
|
293
|
-
isArrayField: true
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (schemaItem.options) {
|
|
297
|
-
qualifiedField.options = schemaItem.options
|
|
298
|
-
} else if (schemaItem.attrs && schemaItem.attrs.options) {
|
|
299
|
-
qualifiedField.options = schemaItem.attrs.options
|
|
300
|
-
}
|
|
301
|
-
addFieldIfNew(qualifiedField)
|
|
302
|
-
}
|
|
303
|
-
})
|
|
304
|
-
}
|
|
305
|
-
} else {
|
|
306
|
-
addFieldIfNew(child)
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
})
|
|
310
|
-
}
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
return allFields
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const schemaFields = computed(() => getAllFields(toValue(props.schema) || []))
|
|
317
|
-
|
|
318
|
-
function isFieldRequired(field: any): boolean {
|
|
319
|
-
if (field.isArrayField && field.parentField) {
|
|
320
|
-
return false
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return (field.attrs && field.attrs.required === true)
|
|
324
|
-
|| (field.required === true)
|
|
325
|
-
|| (field.attrs && field.attrs.attrs && field.attrs.attrs.required === true)
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function getFieldDescription(field: any): { description: string, isConditional: boolean } {
|
|
329
|
-
if (field.isArrayField && field.parentField) {
|
|
330
|
-
const parentLabel = schemaFields.value.find(f => f.id === field.parentField)?.label || field.parentField
|
|
331
|
-
return {
|
|
332
|
-
description: `Required only if ${parentLabel} has items`,
|
|
333
|
-
isConditional: true
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
return {
|
|
337
|
-
description: '',
|
|
338
|
-
isConditional: false
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
async function parseFile(file: File) {
|
|
343
|
-
isLoading.value = true
|
|
344
|
-
|
|
345
|
-
try {
|
|
346
|
-
sheetNames.value = await getSheetNames(file)
|
|
347
|
-
selectedSheet.value = sheetNames.value[0]
|
|
348
|
-
|
|
349
|
-
await loadSheetData()
|
|
350
|
-
} catch (error) {
|
|
351
|
-
console.error('Error parsing file:', error)
|
|
352
|
-
} finally {
|
|
353
|
-
isLoading.value = false
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
async function loadSheetData() {
|
|
358
|
-
if (!file.value || !selectedSheet.value) { return }
|
|
359
|
-
isLoading.value = true
|
|
360
|
-
try {
|
|
361
|
-
const { headers, data } = await readSheetData(file.value, selectedSheet.value, hasHeaders.value)
|
|
362
|
-
|
|
363
|
-
fileHeaders.value = headers
|
|
364
|
-
fileData.value = data
|
|
365
|
-
resetMapping()
|
|
366
|
-
setTimeout(() => { guessDataTypes() }, 100)
|
|
367
|
-
} catch (error) {
|
|
368
|
-
console.error('Error loading sheet data:', error)
|
|
369
|
-
} finally {
|
|
370
|
-
isLoading.value = false
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
function checkArrayFieldConflicts() {
|
|
375
|
-
const mappedArrayParents = new Set()
|
|
376
|
-
const mappedArrayChildren = new Map()
|
|
377
|
-
|
|
378
|
-
Object.keys(fieldMapping).forEach((fieldId: string) => {
|
|
379
|
-
const field = schemaFields.value.find(f => f.id === fieldId)
|
|
380
|
-
if (field) {
|
|
381
|
-
if (field.$el === 'array') {
|
|
382
|
-
mappedArrayParents.add(field.id)
|
|
383
|
-
} else if (field.isArrayField && field.parentField) {
|
|
384
|
-
if (!mappedArrayChildren.has(field.parentField)) {
|
|
385
|
-
mappedArrayChildren.set(field.parentField, new Set())
|
|
386
|
-
}
|
|
387
|
-
mappedArrayChildren.get(field.parentField).add(field.id)
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
})
|
|
391
|
-
|
|
392
|
-
for (const parentId of mappedArrayParents) {
|
|
393
|
-
const childFields = schemaFields.value.filter(f => f.parentField === parentId)
|
|
394
|
-
childFields.forEach((child: any) => {
|
|
395
|
-
child.disabled = true
|
|
396
|
-
child.disabledReason = `Parent field "${parentId}" is already mapped`
|
|
397
|
-
})
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
for (const [parentId, childIds] of mappedArrayChildren.entries()) {
|
|
401
|
-
if (childIds.size > 0) {
|
|
402
|
-
const parentField = schemaFields.value.find(f => f.id === parentId)
|
|
403
|
-
if (parentField) {
|
|
404
|
-
parentField.disabled = true
|
|
405
|
-
parentField.disabledReason = `Child field(s) already mapped`
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function resetMapping() {
|
|
412
|
-
Object.keys(fieldMapping).forEach((key: string) => {
|
|
413
|
-
delete fieldMapping[key]
|
|
414
|
-
})
|
|
415
|
-
|
|
416
|
-
schemaFields.value.forEach((field: any) => {
|
|
417
|
-
field.disabled = false
|
|
418
|
-
field.disabledReason = ''
|
|
419
|
-
})
|
|
420
|
-
|
|
421
|
-
// Try to auto-map fields based on similar names
|
|
422
|
-
if (fileHeaders.value.length > 0) {
|
|
423
|
-
schemaFields.value.forEach((field: any) => {
|
|
424
|
-
// Format variations of field id and label for comparison
|
|
425
|
-
const fieldIdCamel = formatString(field.id, 'camel')
|
|
426
|
-
const fieldIdPascal = formatString(field.id, 'pascal')
|
|
427
|
-
const fieldLabelCamel = formatString(field.label.replace(/\s+/g, '_'), 'camel')
|
|
428
|
-
const fieldLabelPascal = formatString(field.label.replace(/\s+/g, '_'), 'pascal')
|
|
429
|
-
|
|
430
|
-
// Look for exact match
|
|
431
|
-
const exactMatch = fileHeaders.value.find(
|
|
432
|
-
header => header.toLowerCase() === field.id.toLowerCase()
|
|
433
|
-
|| header.toLowerCase() === field.label.toLowerCase()
|
|
434
|
-
|| header.toLowerCase() === fieldIdCamel.toLowerCase()
|
|
435
|
-
|| header.toLowerCase() === fieldIdPascal.toLowerCase()
|
|
436
|
-
|| header.toLowerCase() === fieldLabelCamel.toLowerCase()
|
|
437
|
-
|| header.toLowerCase() === fieldLabelPascal.toLowerCase()
|
|
438
|
-
|| formatString(header.replace(/\s+/g, '_'), 'camel').toLowerCase() === fieldIdCamel.toLowerCase()
|
|
439
|
-
|| formatString(header.replace(/\s+/g, '_'), 'camel').toLowerCase() === fieldLabelCamel.toLowerCase()
|
|
440
|
-
)
|
|
441
|
-
|
|
442
|
-
if (exactMatch && !field.disabled) {
|
|
443
|
-
fieldMapping[field.id] = exactMatch
|
|
444
|
-
} else {
|
|
445
|
-
// Look for partial match
|
|
446
|
-
const partialMatch = fileHeaders.value.find(
|
|
447
|
-
header => header.toLowerCase().includes(field.id.toLowerCase())
|
|
448
|
-
|| header.toLowerCase().includes(field.label.toLowerCase())
|
|
449
|
-
|| header.toLowerCase().includes(fieldIdCamel.toLowerCase())
|
|
450
|
-
|| header.toLowerCase().includes(fieldLabelCamel.toLowerCase())
|
|
451
|
-
|| field.id.toLowerCase().includes(header.toLowerCase())
|
|
452
|
-
|| field.label.toLowerCase().includes(header.toLowerCase())
|
|
453
|
-
)
|
|
454
|
-
|
|
455
|
-
if (partialMatch && !field.disabled) {
|
|
456
|
-
fieldMapping[field.id] = partialMatch
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
})
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
checkArrayFieldConflicts()
|
|
463
|
-
checkMappingComplete()
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function checkMappingComplete() {
|
|
467
|
-
if (!file.value || (Object.keys(fieldMapping).length === 0 && Object.keys(defaultValues).length === 0)) {
|
|
468
|
-
mappingComplete.value = false
|
|
469
|
-
return
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
const requiredFields = schemaFields.value.filter((field) => {
|
|
473
|
-
if (field.isArrayField && field.parentField) { return false }
|
|
474
|
-
|
|
475
|
-
return (field.attrs && field.attrs.required === true)
|
|
476
|
-
|| (field.required === true)
|
|
477
|
-
|| (field.attrs && field.attrs.attrs && field.attrs.attrs.required === true)
|
|
478
|
-
})
|
|
479
|
-
|
|
480
|
-
if (requiredFields.length === 0) {
|
|
481
|
-
mappingComplete.value = Object.keys(fieldMapping).some(key => !!fieldMapping[key])
|
|
482
|
-
|| Object.keys(defaultValues).length > 0
|
|
483
|
-
return
|
|
484
|
-
}
|
|
485
|
-
mappingComplete.value = requiredFields.every(field => !!fieldMapping[field.id] || hasDefaultValue(field.id))
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
function showPreview() {
|
|
489
|
-
guessDataTypes()
|
|
490
|
-
const mappedData: MappedRow[] = []
|
|
491
|
-
|
|
492
|
-
for (let i = 0; i < fileData.value.length; i++) {
|
|
493
|
-
const sourceRow = fileData.value[i]
|
|
494
|
-
const mappedRow: MappedRow = {}
|
|
495
|
-
|
|
496
|
-
schemaFields.value.forEach((field) => {
|
|
497
|
-
if (field.isArrayField || field.$el === 'array') { return }
|
|
498
|
-
let value: any = null
|
|
499
|
-
let useDefault = false
|
|
500
|
-
|
|
501
|
-
if (fieldMapping[field.id] && sourceRow[fieldMapping[field.id]] !== undefined) {
|
|
502
|
-
value = sourceRow[fieldMapping[field.id]]
|
|
503
|
-
if (value === '' && defaultValues[field.id] !== undefined) {
|
|
504
|
-
value = defaultValues[field.id]
|
|
505
|
-
useDefault = true
|
|
506
|
-
}
|
|
507
|
-
} else if (defaultValues[field.id] !== undefined) {
|
|
508
|
-
value = defaultValues[field.id]
|
|
509
|
-
useDefault = true
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
if (value === null) { return }
|
|
513
|
-
|
|
514
|
-
if (!useDefault && transformations[field.id] && transformations[field.id].length > 0) {
|
|
515
|
-
const transform = transformations[field.id].find(t => t.sourceValue == value || t.sourceValue === String(value)
|
|
516
|
-
)
|
|
517
|
-
if (transform) {
|
|
518
|
-
value = transform.targetValue
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
const dataType = fieldDataTypes[field.id] || DATA_TYPES.STRING
|
|
523
|
-
value = convertValueByType(value, dataType)
|
|
524
|
-
|
|
525
|
-
if (field.id.includes('.')) {
|
|
526
|
-
const parts = field.id.split('.')
|
|
527
|
-
const rootField = parts[0]
|
|
528
|
-
|
|
529
|
-
if (!mappedRow[rootField]) {
|
|
530
|
-
mappedRow[rootField] = {}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
let current = mappedRow[rootField]
|
|
534
|
-
for (let i = 1; i < parts.length - 1; i++) {
|
|
535
|
-
if (!current[parts[i]]) {
|
|
536
|
-
current[parts[i]] = {}
|
|
537
|
-
}
|
|
538
|
-
current = current[parts[i]]
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
current[parts[parts.length - 1]] = value
|
|
542
|
-
} else {
|
|
543
|
-
mappedRow[field.id] = value
|
|
544
|
-
}
|
|
545
|
-
})
|
|
546
|
-
|
|
547
|
-
const arrayChildFields = schemaFields.value.filter(field => field.isArrayField && field.parentField)
|
|
548
|
-
|
|
549
|
-
arrayChildFields.forEach((childField) => {
|
|
550
|
-
const [parentId, childId] = childField.id.split('.')
|
|
551
|
-
if (!parentId || !childId) { return }
|
|
552
|
-
|
|
553
|
-
let value: any = null
|
|
554
|
-
let useDefault = false
|
|
555
|
-
|
|
556
|
-
if (fieldMapping[childField.id] && sourceRow[fieldMapping[childField.id]] !== undefined) {
|
|
557
|
-
value = sourceRow[fieldMapping[childField.id]]
|
|
558
|
-
if (value === '' && defaultValues[childField.id] !== undefined) {
|
|
559
|
-
value = defaultValues[childField.id]
|
|
560
|
-
useDefault = true
|
|
561
|
-
}
|
|
562
|
-
} else if (defaultValues[childField.id] !== undefined) {
|
|
563
|
-
value = defaultValues[childField.id]
|
|
564
|
-
useDefault = true
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
if (value === null) {
|
|
568
|
-
return
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
if (!mappedRow[parentId]) {
|
|
572
|
-
mappedRow[parentId] = []
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
if (!useDefault && transformations[childField.id] && transformations[childField.id].length > 0) {
|
|
576
|
-
const transform = transformations[childField.id].find(t => t.sourceValue == value || t.sourceValue === String(value)
|
|
577
|
-
)
|
|
578
|
-
if (transform) {
|
|
579
|
-
value = transform.targetValue
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
const dataType = fieldDataTypes[childField.id] || DATA_TYPES.STRING
|
|
584
|
-
value = convertValueByType(value, dataType)
|
|
585
|
-
|
|
586
|
-
mappedRow[parentId].push({
|
|
587
|
-
[childId]: value
|
|
588
|
-
})
|
|
589
|
-
})
|
|
590
|
-
|
|
591
|
-
Object.keys(relatedFiles).forEach((fieldId) => {
|
|
592
|
-
if (!relatedFiles[fieldId] || !relatedKeyField[fieldId] || !parentKeyField[fieldId]) {
|
|
593
|
-
return
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
const parentKeyValue = sourceRow[parentKeyField[fieldId]]
|
|
597
|
-
if (!parentKeyValue) { return }
|
|
598
|
-
|
|
599
|
-
if (!mappedRow[fieldId]) {
|
|
600
|
-
mappedRow[fieldId] = []
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
const matchingRows = relatedFileData[fieldId].filter(relatedRow => relatedRow[relatedKeyField[fieldId]] == parentKeyValue
|
|
604
|
-
|| relatedRow[relatedKeyField[fieldId]] === parentKeyValue.toString()
|
|
605
|
-
)
|
|
606
|
-
|
|
607
|
-
matchingRows.forEach((matchingRow) => {
|
|
608
|
-
const mappedItem: Record<string, any> = {}
|
|
609
|
-
|
|
610
|
-
if (selectedRelationField.value?.attrs?.schema) {
|
|
611
|
-
selectedRelationField.value.attrs.schema.forEach((schemaItem: SchemaItem) => {
|
|
612
|
-
if (!schemaItem.id) { return }
|
|
613
|
-
|
|
614
|
-
let value: any = null
|
|
615
|
-
let useDefault = false
|
|
616
|
-
|
|
617
|
-
if (relatedFileMappings[fieldId][schemaItem.id]
|
|
618
|
-
&& matchingRow[relatedFileMappings[fieldId][schemaItem.id]] !== undefined) {
|
|
619
|
-
value = matchingRow[relatedFileMappings[fieldId][schemaItem.id]]
|
|
620
|
-
if (value === '' && relatedDefaultValues[fieldId][schemaItem.id] !== undefined) {
|
|
621
|
-
value = relatedDefaultValues[fieldId][schemaItem.id]
|
|
622
|
-
useDefault = true
|
|
623
|
-
}
|
|
624
|
-
} else if (relatedDefaultValues[fieldId][schemaItem.id] !== undefined) {
|
|
625
|
-
value = relatedDefaultValues[fieldId][schemaItem.id]
|
|
626
|
-
useDefault = true
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
if (value === null) { return }
|
|
630
|
-
|
|
631
|
-
if (!useDefault
|
|
632
|
-
&& relatedTransformations[fieldId][schemaItem.id]
|
|
633
|
-
&& relatedTransformations[fieldId][schemaItem.id].length > 0) {
|
|
634
|
-
const transform = relatedTransformations[fieldId][schemaItem.id].find(t => t.sourceValue == value || t.sourceValue === String(value)
|
|
635
|
-
)
|
|
636
|
-
if (transform) {
|
|
637
|
-
value = transform.targetValue
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
const fullChildId = `${fieldId}.${schemaItem.id}`
|
|
642
|
-
const dataType = relatedFieldDataTypes[fullChildId] || DATA_TYPES.STRING
|
|
643
|
-
value = convertValueByType(value, dataType)
|
|
644
|
-
|
|
645
|
-
mappedItem[schemaItem.id] = value
|
|
646
|
-
})
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
if (Object.keys(mappedItem).length > 0) {
|
|
650
|
-
mappedRow[fieldId].push(mappedItem)
|
|
651
|
-
}
|
|
652
|
-
})
|
|
653
|
-
})
|
|
654
|
-
|
|
655
|
-
const hasData = Object.values(mappedRow).some((value) => {
|
|
656
|
-
if (value === null || value === undefined) { return false }
|
|
657
|
-
if (value === '') { return false }
|
|
658
|
-
if (Array.isArray(value) && value.length === 0) { return false }
|
|
659
|
-
return true
|
|
660
|
-
})
|
|
661
|
-
|
|
662
|
-
if (hasData) {
|
|
663
|
-
mappedData.push(mappedRow)
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
previewData.value = mappedData
|
|
668
|
-
showPreviewModal.value = true
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
function createSpreadsheetColumns() {
|
|
672
|
-
return schemaFields.value
|
|
673
|
-
.filter((field) => {
|
|
674
|
-
return field.$el !== 'array'
|
|
675
|
-
})
|
|
676
|
-
.map((field) => {
|
|
677
|
-
return {
|
|
678
|
-
key: field.id,
|
|
679
|
-
title: field.label,
|
|
680
|
-
formatter: field.isArrayField ? formatArrayChildValue : undefined
|
|
681
|
-
}
|
|
682
|
-
})
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
function formatArrayChildValue(value: any, row: any, fieldId: string): string {
|
|
686
|
-
const field = schemaFields.value.find(f => f.id === fieldId)
|
|
687
|
-
if (!field || !field.isArrayField || !field.parentField) { return value }
|
|
688
|
-
|
|
689
|
-
const [parentId, childId] = fieldId.split('.')
|
|
690
|
-
const parentArray = row[parentId]
|
|
691
|
-
|
|
692
|
-
if (Array.isArray(parentArray) && parentArray.length > 0) {
|
|
693
|
-
return parentArray
|
|
694
|
-
.map(item => item[childId])
|
|
695
|
-
.filter(val => val !== undefined)
|
|
696
|
-
.join(', ')
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
return ''
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
const spreadsheetColumns = computed(() => createSpreadsheetColumns())
|
|
703
|
-
|
|
704
|
-
function processData() {
|
|
705
|
-
emit('processedData', previewData.value)
|
|
706
|
-
showPreviewModal.value = false
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
function updateFieldMapping(fieldId: string, value: string) {
|
|
710
|
-
const previousValue = fieldMapping[fieldId]
|
|
711
|
-
if (previousValue && previousValue !== value) {
|
|
712
|
-
fieldMapping[fieldId] = ''
|
|
713
|
-
schemaFields.value.forEach((field) => {
|
|
714
|
-
field.disabled = false
|
|
715
|
-
field.disabledReason = ''
|
|
716
|
-
})
|
|
717
|
-
}
|
|
718
|
-
if (value) {
|
|
719
|
-
fieldMapping[fieldId] = value
|
|
720
|
-
const field = schemaFields.value.find(f => f.id === fieldId)
|
|
721
|
-
if (field) {
|
|
722
|
-
if (field.$el === 'array') {
|
|
723
|
-
const childFields = schemaFields.value.filter(f => f.parentField === field.id)
|
|
724
|
-
childFields.forEach((child) => {
|
|
725
|
-
child.disabled = true
|
|
726
|
-
child.disabledReason = `Parent field "${field.id}" is already mapped`
|
|
727
|
-
})
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
if (field.isArrayField && field.parentField) {
|
|
731
|
-
const parentField = schemaFields.value.find(f => f.id === field.parentField)
|
|
732
|
-
if (parentField) {
|
|
733
|
-
parentField.disabled = true
|
|
734
|
-
parentField.disabledReason = `Child field already mapped`
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
checkArrayFieldConflicts()
|
|
740
|
-
checkMappingComplete()
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
function handleSelectChange(event: Event, fieldId: string) {
|
|
744
|
-
const target = event.target as HTMLSelectElement
|
|
745
|
-
if (target) {
|
|
746
|
-
updateFieldMapping(fieldId, target.value)
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
async function handleFilesUploaded(files: FileList) {
|
|
751
|
-
file.value = files[0]
|
|
752
|
-
if (!file.value) { return }
|
|
753
|
-
isLoading.value = true
|
|
754
|
-
|
|
755
|
-
try {
|
|
756
|
-
await parseFile(file.value)
|
|
757
|
-
} catch (error) {
|
|
758
|
-
console.error('Error parsing file:', error)
|
|
759
|
-
} finally {
|
|
760
|
-
isLoading.value = false
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
watchEffect(() => {
|
|
765
|
-
if (selectedSheet.value) {
|
|
766
|
-
loadSheetData()
|
|
767
|
-
}
|
|
768
|
-
})
|
|
769
|
-
|
|
770
|
-
function hasDefaultValue(fieldId: string): boolean {
|
|
771
|
-
return defaultValues[fieldId] !== undefined
|
|
772
|
-
&& defaultValues[fieldId] !== null
|
|
773
|
-
&& defaultValues[fieldId] !== ''
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
function openTransformDialog(field: SchemaItem) {
|
|
777
|
-
try {
|
|
778
|
-
if (!field.options) {
|
|
779
|
-
field.options = []
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
if (field.attrs && field.attrs.options) {
|
|
783
|
-
field.options = field.attrs.options
|
|
784
|
-
}
|
|
785
|
-
selectedTransformField.value = field
|
|
786
|
-
if (!transformations[field.id]) {
|
|
787
|
-
transformations[field.id] = []
|
|
788
|
-
}
|
|
789
|
-
showTransformDialog.value = true
|
|
790
|
-
} catch (error) {
|
|
791
|
-
console.error('Error opening transform dialog:', error)
|
|
792
|
-
alert('An error occurred while opening the transform dialog. See console for details.')
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
function removeTransformation(fieldId: string, index: number) {
|
|
797
|
-
if (transformations[fieldId] && transformations[fieldId].length > index) {
|
|
798
|
-
transformations[fieldId].splice(index, 1)
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
function openRelatedDialog(field: SchemaItem) {
|
|
803
|
-
selectedRelationField.value = field
|
|
804
|
-
showRelatedDialog.value = true
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
async function processRelatedFile(fieldId: string, files: File[] | FileList) {
|
|
808
|
-
if (!files) { return }
|
|
809
|
-
const file = files[0]
|
|
810
|
-
|
|
811
|
-
relatedFiles[fieldId] = file
|
|
812
|
-
|
|
813
|
-
try {
|
|
814
|
-
const sheets = await getSheetNames(file)
|
|
815
|
-
const selectedSheet = sheets.length > 0 ? sheets[0] : ''
|
|
816
|
-
|
|
817
|
-
const { data } = await readSheetData(file, selectedSheet, true)
|
|
818
|
-
relatedFileData[fieldId] = data
|
|
819
|
-
|
|
820
|
-
if (!relatedFileMappings[fieldId]) {
|
|
821
|
-
relatedFileMappings[fieldId] = {}
|
|
822
|
-
}
|
|
823
|
-
} catch (error) {
|
|
824
|
-
console.error('Error processing related file:', error)
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
function autoPopulateTransformations(fieldId: string) {
|
|
829
|
-
try {
|
|
830
|
-
const field = schemaFields.value.find(f => f.id === fieldId)
|
|
831
|
-
if (!field) {
|
|
832
|
-
console.error('Field not found:', fieldId)
|
|
833
|
-
return
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
let fieldOptions = field.options || (field.attrs && field.attrs.options)
|
|
837
|
-
|
|
838
|
-
if (!fieldMapping[fieldId] || !fileData.value || fileData.value.length === 0) {
|
|
839
|
-
console.warn('No data or mapping found for field:', fieldId)
|
|
840
|
-
return
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
if (!fieldOptions) {
|
|
844
|
-
console.warn('No options found for field:', fieldId)
|
|
845
|
-
return
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
if (!Array.isArray(fieldOptions)) {
|
|
849
|
-
console.warn('Options is not an array for field:', fieldId)
|
|
850
|
-
fieldOptions = []
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
const uniqueValues = getUniqueSourceValues(fieldId)
|
|
854
|
-
|
|
855
|
-
if (!transformations[fieldId]) {
|
|
856
|
-
transformations[fieldId] = []
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
let matchCount = 0
|
|
860
|
-
const unmatchedValues: string[] = []
|
|
861
|
-
|
|
862
|
-
uniqueValues.forEach((sourceValue) => {
|
|
863
|
-
const strSourceValue = String(sourceValue)
|
|
864
|
-
const matchedValue = findMatchingTargetValue(strSourceValue, fieldOptions)
|
|
865
|
-
|
|
866
|
-
if (matchedValue) {
|
|
867
|
-
const existingIndex = transformations[fieldId].findIndex(t => t.sourceValue === strSourceValue
|
|
868
|
-
)
|
|
869
|
-
|
|
870
|
-
if (existingIndex >= 0) {
|
|
871
|
-
transformations[fieldId][existingIndex].targetValue = matchedValue
|
|
872
|
-
} else {
|
|
873
|
-
transformations[fieldId].push({
|
|
874
|
-
fieldId,
|
|
875
|
-
sourceValue: strSourceValue,
|
|
876
|
-
targetValue: matchedValue
|
|
877
|
-
})
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
matchCount++
|
|
881
|
-
} else {
|
|
882
|
-
unmatchedValues.push(strSourceValue)
|
|
883
|
-
}
|
|
884
|
-
})
|
|
885
|
-
|
|
886
|
-
if (matchCount === 0) {
|
|
887
|
-
alert(`No automatic matches found. Try creating transformations manually.`)
|
|
888
|
-
} else {
|
|
889
|
-
alert(`Automatically created ${matchCount} transformations by matching source values to target labels.\n\n${unmatchedValues.length} values could not be automatically matched.`)
|
|
890
|
-
}
|
|
891
|
-
} catch (error) {
|
|
892
|
-
console.error('Error auto-populating transformations:', error)
|
|
893
|
-
alert('An error occurred while trying to auto-populate transformations. See console for details.')
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
function detectDateFormat(value: string): RegExp | null {
|
|
898
|
-
const formats = [
|
|
899
|
-
/^\d{4}-\d{2}-\d{2}$/, // YYYY-MM-DD
|
|
900
|
-
/^\d{2}\/\d{2}\/\d{4}$/, // MM/DD/YYYY
|
|
901
|
-
/^\d{2}\.\d{2}\.\d{4}$/, // DD.MM.YYYY
|
|
902
|
-
/^\d{1,2}\s[a-z]{3}\s\d{4}$/i, // D MMM YYYY
|
|
903
|
-
/^\d{1,2}\s[a-z]{3,9}\s\d{4}$/i, // D MMMM YYYY
|
|
904
|
-
]
|
|
905
|
-
|
|
906
|
-
for (const format of formats) {
|
|
907
|
-
if (format.test(value)) {
|
|
908
|
-
return format
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
return null
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
function parseDate(value: string): Date | null {
|
|
915
|
-
if (!value) { return null }
|
|
916
|
-
|
|
917
|
-
const isoDate = new Date(value)
|
|
918
|
-
if (!Number.isNaN(isoDate.getTime())) {
|
|
919
|
-
return isoDate
|
|
920
|
-
}
|
|
921
|
-
const format = detectDateFormat(value)
|
|
922
|
-
if (format) {
|
|
923
|
-
if (/^\d{2}\/\d{2}\/\d{4}$/.test(value)) {
|
|
924
|
-
const [month, day, year] = value.split('/').map(Number)
|
|
925
|
-
return new Date(year, month - 1, day)
|
|
926
|
-
} if (/^\d{2}\.\d{2}\.\d{4}$/.test(value)) {
|
|
927
|
-
const [day, month, year] = value.split('.').map(Number)
|
|
928
|
-
return new Date(year, month - 1, day)
|
|
929
|
-
} else if (/^\d{1,2}\s[a-z]{3,9}\s\d{4}$/i.test(value)) {
|
|
930
|
-
return new Date(value)
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
return null
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
function convertValueByType(value: any, dataType: string): any {
|
|
938
|
-
if (value === null || value === undefined || value === '') {
|
|
939
|
-
return null
|
|
940
|
-
}
|
|
941
|
-
try {
|
|
942
|
-
switch (dataType) {
|
|
943
|
-
case DATA_TYPES.STRING:
|
|
944
|
-
return String(value)
|
|
945
|
-
|
|
946
|
-
case DATA_TYPES.NUMBER: {
|
|
947
|
-
const num = Number(value)
|
|
948
|
-
return Number.isNaN(num) ? null : num
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
case DATA_TYPES.BOOLEAN:
|
|
952
|
-
if (typeof value === 'boolean') { return value }
|
|
953
|
-
if (typeof value === 'string') {
|
|
954
|
-
const lowercased = value.toLowerCase().trim()
|
|
955
|
-
if (['true', 'yes', '1', 'on'].includes(lowercased)) { return true }
|
|
956
|
-
if (['false', 'no', '0', 'off'].includes(lowercased)) { return false }
|
|
957
|
-
}
|
|
958
|
-
return Boolean(value)
|
|
959
|
-
case DATA_TYPES.DATE:
|
|
960
|
-
case DATA_TYPES.DATETIME:
|
|
961
|
-
if (isExcelSerialDate(value)) {
|
|
962
|
-
const date = excelSerialDateToJSDate(value)
|
|
963
|
-
return formatDate(date, dataType === DATA_TYPES.DATETIME)
|
|
964
|
-
}
|
|
965
|
-
if (typeof value === 'string') {
|
|
966
|
-
const dateObj = parseDate(value)
|
|
967
|
-
if (dateObj) {
|
|
968
|
-
return formatDate(dateObj, dataType === DATA_TYPES.DATETIME)
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
if (value instanceof Date) {
|
|
972
|
-
return formatDate(value, dataType === DATA_TYPES.DATETIME)
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
return null
|
|
976
|
-
|
|
977
|
-
default:
|
|
978
|
-
return value
|
|
979
|
-
}
|
|
980
|
-
} catch (error) {
|
|
981
|
-
console.error('Error converting value:', value, 'to type:', dataType, error)
|
|
982
|
-
return null
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
function detectDataType(value: any): string {
|
|
987
|
-
if (value === null || value === undefined) {
|
|
988
|
-
return DATA_TYPES.STRING
|
|
989
|
-
}
|
|
990
|
-
if (typeof value === 'number' || (typeof value === 'string' && !Number.isNaN(Number(value)))) {
|
|
991
|
-
if (isExcelSerialDate(Number(value))) {
|
|
992
|
-
return DATA_TYPES.DATE
|
|
993
|
-
}
|
|
994
|
-
return DATA_TYPES.NUMBER
|
|
995
|
-
}
|
|
996
|
-
if (typeof value === 'boolean' || (typeof value === 'string' && ['true', 'false', 'yes', 'no'].includes(value.toLowerCase()))) {
|
|
997
|
-
return DATA_TYPES.BOOLEAN
|
|
998
|
-
}
|
|
999
|
-
if (value instanceof Date) {
|
|
1000
|
-
return DATA_TYPES.DATETIME
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
if (typeof value === 'string') {
|
|
1004
|
-
if (detectDateFormat(value) || !Number.isNaN(new Date(value).getTime())) {
|
|
1005
|
-
return DATA_TYPES.DATE
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
return DATA_TYPES.STRING
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
function guessDataTypes(): void {
|
|
1012
|
-
schemaFields.value.forEach((field) => {
|
|
1013
|
-
if (!fieldDataTypes[field.id] && fieldMapping[field.id]) {
|
|
1014
|
-
const sampleValues = fileData.value
|
|
1015
|
-
.slice(0, 5)
|
|
1016
|
-
.map(row => row[fieldMapping[field.id]])
|
|
1017
|
-
.filter(value => value !== undefined && value !== null && value !== '')
|
|
1018
|
-
|
|
1019
|
-
if (sampleValues.length > 0) {
|
|
1020
|
-
const types = sampleValues.map(detectDataType)
|
|
1021
|
-
const typeCount: Record<string, number> = {}
|
|
1022
|
-
|
|
1023
|
-
types.forEach((type: any) => {
|
|
1024
|
-
typeCount[type] = (typeCount[type] || 0) + 1
|
|
1025
|
-
})
|
|
1026
|
-
|
|
1027
|
-
let maxCount = 0
|
|
1028
|
-
let mostCommonType = DATA_TYPES.STRING
|
|
1029
|
-
|
|
1030
|
-
Object.entries(typeCount).forEach(([type, count]) => {
|
|
1031
|
-
if (count > maxCount) {
|
|
1032
|
-
maxCount = count
|
|
1033
|
-
mostCommonType = type
|
|
1034
|
-
}
|
|
1035
|
-
})
|
|
1036
|
-
|
|
1037
|
-
fieldDataTypes[field.id] = mostCommonType
|
|
1038
|
-
} else {
|
|
1039
|
-
fieldDataTypes[field.id] = DATA_TYPES.STRING
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
})
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
function openRelatedTransformDialog(parentId: string, field: SchemaItem) {
|
|
1046
|
-
try {
|
|
1047
|
-
console.log('Opening related transform dialog for field:', field.id, 'in parent:', parentId)
|
|
1048
|
-
if (!field.options) {
|
|
1049
|
-
field.options = []
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
if (field.attrs && field.attrs.options) {
|
|
1053
|
-
console.log('Copying options from attrs for related field:', field.id)
|
|
1054
|
-
field.options = Array.isArray(field.attrs.options)
|
|
1055
|
-
? field.attrs.options
|
|
1056
|
-
: []
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
selectedRelatedTransformField.value = { parentId, field }
|
|
1060
|
-
|
|
1061
|
-
if (!relatedTransformations[parentId]) {
|
|
1062
|
-
relatedTransformations[parentId] = {}
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
if (!relatedTransformations[parentId][field.id]) {
|
|
1066
|
-
relatedTransformations[parentId][field.id] = []
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
showRelatedTransformDialog.value = true
|
|
1070
|
-
|
|
1071
|
-
selectedRelatedSourceValue.value = ''
|
|
1072
|
-
selectedRelatedTargetValue.value = ''
|
|
1073
|
-
} catch (error) {
|
|
1074
|
-
console.error('Error opening related transform dialog:', error)
|
|
1075
|
-
alert('An error occurred while opening the related transform dialog. See console for details.')
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
function getRelatedUniqueSourceValues(parentId: string, fieldId: string): any[] {
|
|
1080
|
-
if (!relatedFileMappings[parentId][fieldId]
|
|
1081
|
-
|| !relatedFileData[parentId]
|
|
1082
|
-
|| relatedFileData[parentId].length === 0) {
|
|
1083
|
-
return []
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
const allValues = relatedFileData[parentId]
|
|
1087
|
-
.map(row => row[relatedFileMappings[parentId][fieldId]])
|
|
1088
|
-
.filter(value => value !== undefined && value !== null && value !== '')
|
|
1089
|
-
|
|
1090
|
-
const uniqueValues = [...new Set(allValues)]
|
|
1091
|
-
|
|
1092
|
-
return uniqueValues.filter((value) => {
|
|
1093
|
-
if (!relatedTransformations[parentId][fieldId]
|
|
1094
|
-
|| relatedTransformations[parentId][fieldId].length === 0) {
|
|
1095
|
-
return true
|
|
1096
|
-
}
|
|
1097
|
-
return !relatedTransformations[parentId][fieldId].some(t => t.sourceValue == value || t.sourceValue === value.toString()
|
|
1098
|
-
)
|
|
1099
|
-
})
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
const availableRelatedSourceValues = computed(() => {
|
|
1103
|
-
if (!selectedRelatedTransformField.value) {
|
|
1104
|
-
return []
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
const { parentId, field } = selectedRelatedTransformField.value
|
|
1108
|
-
return getRelatedUniqueSourceValues(parentId, field.id)
|
|
1109
|
-
})
|
|
1110
|
-
|
|
1111
|
-
const relatedSourceValueOptions = computed(() => {
|
|
1112
|
-
return availableRelatedSourceValues.value.map(value => ({
|
|
1113
|
-
value: String(value),
|
|
1114
|
-
label: String(value)
|
|
1115
|
-
}))
|
|
1116
|
-
})
|
|
1117
|
-
|
|
1118
|
-
function addRelatedTransformation(parentId: string, fieldId: string) {
|
|
1119
|
-
if (!relatedTransformations[parentId]) {
|
|
1120
|
-
relatedTransformations[parentId] = {}
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
if (!relatedTransformations[parentId][fieldId]) {
|
|
1124
|
-
relatedTransformations[parentId][fieldId] = []
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
if (selectedRelatedSourceValue.value && selectedRelatedTargetValue.value) {
|
|
1128
|
-
const existingIndex = relatedTransformations[parentId][fieldId].findIndex(
|
|
1129
|
-
t => t.sourceValue === selectedRelatedSourceValue.value
|
|
1130
|
-
)
|
|
1131
|
-
|
|
1132
|
-
if (existingIndex >= 0) {
|
|
1133
|
-
relatedTransformations[parentId][fieldId][existingIndex].targetValue = selectedRelatedTargetValue.value
|
|
1134
|
-
} else {
|
|
1135
|
-
relatedTransformations[parentId][fieldId].push({
|
|
1136
|
-
fieldId,
|
|
1137
|
-
sourceValue: selectedRelatedSourceValue.value,
|
|
1138
|
-
targetValue: selectedRelatedTargetValue.value
|
|
1139
|
-
})
|
|
1140
|
-
}
|
|
1141
|
-
selectedRelatedSourceValue.value = ''
|
|
1142
|
-
selectedRelatedTargetValue.value = ''
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
function removeRelatedTransformation(parentId: string, fieldId: string, index: number) {
|
|
1147
|
-
if (relatedTransformations[parentId][fieldId]
|
|
1148
|
-
&& relatedTransformations[parentId][fieldId].length > index) {
|
|
1149
|
-
relatedTransformations[parentId][fieldId].splice(index, 1)
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
function autoPopulateRelatedTransformations(parentId: string, fieldId: string) {
|
|
1154
|
-
try {
|
|
1155
|
-
const field = selectedRelatedTransformField.value?.field
|
|
1156
|
-
if (!field) {
|
|
1157
|
-
console.error('Field not found for auto-populate')
|
|
1158
|
-
return
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
let fieldOptions = field.options || (field.attrs && field.attrs.options)
|
|
1162
|
-
|
|
1163
|
-
if (!relatedFileMappings[parentId][fieldId]
|
|
1164
|
-
|| !relatedFileData[parentId]
|
|
1165
|
-
|| relatedFileData[parentId].length === 0) {
|
|
1166
|
-
console.warn('No data or mapping found for related field:', fieldId)
|
|
1167
|
-
return
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
if (!fieldOptions) {
|
|
1171
|
-
console.warn('No options found for related field:', fieldId)
|
|
1172
|
-
return
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
if (!Array.isArray(fieldOptions)) {
|
|
1176
|
-
console.warn('Options is not an array for related field:', fieldId)
|
|
1177
|
-
fieldOptions = []
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
const uniqueValues = getRelatedUniqueSourceValues(parentId, fieldId)
|
|
1181
|
-
|
|
1182
|
-
if (!relatedTransformations[parentId]) {
|
|
1183
|
-
relatedTransformations[parentId] = {}
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
if (!relatedTransformations[parentId][fieldId]) {
|
|
1187
|
-
relatedTransformations[parentId][fieldId] = []
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
let matchCount = 0
|
|
1191
|
-
const unmatchedValues: string[] = []
|
|
1192
|
-
|
|
1193
|
-
uniqueValues.forEach((sourceValue) => {
|
|
1194
|
-
const strSourceValue = String(sourceValue)
|
|
1195
|
-
|
|
1196
|
-
const matchedValue = findMatchingTargetValue(strSourceValue, fieldOptions)
|
|
1197
|
-
|
|
1198
|
-
if (matchedValue) {
|
|
1199
|
-
const existingIndex = relatedTransformations[parentId][fieldId].findIndex(
|
|
1200
|
-
t => t.sourceValue === strSourceValue
|
|
1201
|
-
)
|
|
1202
|
-
|
|
1203
|
-
if (existingIndex >= 0) {
|
|
1204
|
-
// Update existing transformation
|
|
1205
|
-
relatedTransformations[parentId][fieldId][existingIndex].targetValue = matchedValue
|
|
1206
|
-
} else {
|
|
1207
|
-
// Add new transformation
|
|
1208
|
-
relatedTransformations[parentId][fieldId].push({
|
|
1209
|
-
fieldId,
|
|
1210
|
-
sourceValue: strSourceValue,
|
|
1211
|
-
targetValue: matchedValue
|
|
1212
|
-
})
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
matchCount++
|
|
1216
|
-
} else {
|
|
1217
|
-
unmatchedValues.push(strSourceValue)
|
|
1218
|
-
}
|
|
1219
|
-
})
|
|
1220
|
-
if (matchCount === 0) {
|
|
1221
|
-
alert(`No automatic matches found. Try creating transformations manually.`)
|
|
1222
|
-
} else {
|
|
1223
|
-
alert(`Automatically created ${matchCount} transformations by matching source values to target labels.\n\n${unmatchedValues.length} values could not be automatically matched.`)
|
|
1224
|
-
}
|
|
1225
|
-
} catch (error) {
|
|
1226
|
-
console.error('Error auto-populating related transformations:', error)
|
|
1227
|
-
alert('An error occurred while trying to auto-populate related transformations. See console for details.')
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
function initDefaultValue(fieldId: string) {
|
|
1232
|
-
if (defaultValues[fieldId] === undefined) {
|
|
1233
|
-
defaultValues[fieldId] = null
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
function initRelatedDefaultValue(parentId: string, fieldId: string) {
|
|
1238
|
-
if (!relatedDefaultValues[parentId]) {
|
|
1239
|
-
relatedDefaultValues[parentId] = {}
|
|
1240
|
-
}
|
|
1241
|
-
if (relatedDefaultValues[parentId][fieldId] === undefined) {
|
|
1242
|
-
relatedDefaultValues[parentId][fieldId] = null
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
function getFieldWithDefaults(field: any) {
|
|
1247
|
-
let fieldType = field.$el || 'text'
|
|
1248
|
-
|
|
1249
|
-
if (fieldDataTypes[field.id]) {
|
|
1250
|
-
switch (fieldDataTypes[field.id]) {
|
|
1251
|
-
case DATA_TYPES.NUMBER:
|
|
1252
|
-
fieldType = 'number'
|
|
1253
|
-
break
|
|
1254
|
-
case DATA_TYPES.DATE:
|
|
1255
|
-
fieldType = 'date'
|
|
1256
|
-
break
|
|
1257
|
-
case DATA_TYPES.BOOLEAN:
|
|
1258
|
-
fieldType = 'toggle'
|
|
1259
|
-
break
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
if (field.id) {
|
|
1264
|
-
formData.value[field.id] = defaultValues[field.id]
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
return {
|
|
1268
|
-
...field,
|
|
1269
|
-
$el: fieldType,
|
|
1270
|
-
placeholder: 'Set default...'
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
function getRelatedFieldWithDefaults(parentId: string, field: any) {
|
|
1275
|
-
let fieldType = field.$el || 'text'
|
|
1276
|
-
const fullFieldId = `${parentId}.${field.id}`
|
|
1277
|
-
if (relatedFieldDataTypes[fullFieldId]) {
|
|
1278
|
-
switch (relatedFieldDataTypes[fullFieldId]) {
|
|
1279
|
-
case DATA_TYPES.NUMBER:
|
|
1280
|
-
fieldType = 'number'
|
|
1281
|
-
break
|
|
1282
|
-
case DATA_TYPES.DATE:
|
|
1283
|
-
fieldType = 'date'
|
|
1284
|
-
break
|
|
1285
|
-
case DATA_TYPES.BOOLEAN:
|
|
1286
|
-
fieldType = 'toggle'
|
|
1287
|
-
break
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
// Create a modified id to avoid collision with main fields
|
|
1292
|
-
const modifiedField = {
|
|
1293
|
-
...field,
|
|
1294
|
-
id: fullFieldId,
|
|
1295
|
-
$el: fieldType,
|
|
1296
|
-
placeholder: 'Set default...'
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
// Update formData with current value
|
|
1300
|
-
formData.value[fullFieldId] = relatedDefaultValues[parentId][field.id]
|
|
1301
|
-
|
|
1302
|
-
return modifiedField
|
|
1303
|
-
}
|
|
1304
|
-
</script>
|
|
1305
|
-
|
|
1306
|
-
<template>
|
|
1307
|
-
<Card class="upload-data-container h-100p grid overflow-hidden list-wrap ">
|
|
1308
|
-
<h2 class="line-height-1 m-0 pb-2 txt-center" v-text="props.title || 'Upload and Map Data'" />
|
|
1309
|
-
<div v-if="!file" class="h-100p flex column justify-content-center">
|
|
1310
|
-
<DragOver accept=".csv,.xls,.xlsx" class="max-h300px w-500px" browse @addFiles="handleFilesUploaded">
|
|
1311
|
-
<Card
|
|
1312
|
-
class="flex flex-column items-center justify-center outline-dashed outline-3 bg-input hover h-100p justify-content-center txt-center"
|
|
1313
|
-
>
|
|
1314
|
-
<Icon name="upload" size="5" />
|
|
1315
|
-
<p>Drag and drop an Excel or CSV file here</p>
|
|
1316
|
-
<u>or click to select a file</u>
|
|
1317
|
-
<p class="txt-12 color-gray">
|
|
1318
|
-
Accepts .xlsx, .xls, and .csv files
|
|
1319
|
-
</p>
|
|
1320
|
-
</Card>
|
|
1321
|
-
</DragOver>
|
|
1322
|
-
</div>
|
|
1323
|
-
<div v-if="isLoading" class="loading-container">
|
|
1324
|
-
<div class="spinner" />
|
|
1325
|
-
<p>Processing your file...</p>
|
|
1326
|
-
</div>
|
|
1327
|
-
|
|
1328
|
-
<div class="overflow h-100p">
|
|
1329
|
-
<div v-if="file && !isLoading && sheetNames.length > 0" class="config-section flex gap-05 pb-2 m_flex-wrap">
|
|
1330
|
-
<Btn v-tooltip="'Change File'" class="px-1" color="gray" @click="file = null">
|
|
1331
|
-
<Icon icon="draft" size="1.5" weight="300" />
|
|
1332
|
-
<p v-text="file.name" />
|
|
1333
|
-
</Btn>
|
|
1334
|
-
<SelectInput
|
|
1335
|
-
v-if="sheetNames.length > 1" v-model="selectedSheet" :options="sheetNames"
|
|
1336
|
-
:label="$t('importData.selectSheet')"
|
|
1337
|
-
/>
|
|
1338
|
-
<CheckInput
|
|
1339
|
-
v-model="hasHeaders" :label="$t('importData.hasHeaders')" class="m-0"
|
|
1340
|
-
style="--bgl-accent-color: var(--bgl-black); --bgl-primary: var(--bgl-black);"
|
|
1341
|
-
/>
|
|
1342
|
-
</div>
|
|
1343
|
-
<div v-if="file && !isLoading && fileHeaders.length > 0">
|
|
1344
|
-
<p class="label pb-1 border-bottom mb-1">
|
|
1345
|
-
Match each required field to a column from your file, set default values, or configure
|
|
1346
|
-
transformations
|
|
1347
|
-
</p>
|
|
1348
|
-
|
|
1349
|
-
<div class="mapping-table">
|
|
1350
|
-
<div class="grid grid-wrap-5 gap-1 bold pb-1 m_none">
|
|
1351
|
-
<p>Schema Field</p>
|
|
1352
|
-
<p>Column from File</p>
|
|
1353
|
-
<p>Default Value</p>
|
|
1354
|
-
<p>Data Type</p>
|
|
1355
|
-
<p>Actions</p>
|
|
1356
|
-
</div>
|
|
1357
|
-
|
|
1358
|
-
<div
|
|
1359
|
-
v-for="field in schemaFields" :key="field.id"
|
|
1360
|
-
class="grid grid-wrap-5 gap-1 m_gap-025 m_pb-1-5"
|
|
1361
|
-
:class="{ 'array-field-row': field.isArrayField || field.$el === 'array' }"
|
|
1362
|
-
>
|
|
1363
|
-
<div>
|
|
1364
|
-
<div class="field-label">
|
|
1365
|
-
<p class="grid-span-2 input-size line-height-1 inline-block">
|
|
1366
|
-
{{ field.label }}
|
|
1367
|
-
</p>
|
|
1368
|
-
<span v-if="field.isArrayField">↳</span>
|
|
1369
|
-
<Pill v-if="field.$el === 'array'" class="txt10 ms-05" round thin value="Array" />
|
|
1370
|
-
<!-- <span v-if="field.$el === 'array'" class="array-parent-indicator">[Array]</span> -->
|
|
1371
|
-
<span v-if="isFieldRequired(field)">*</span>
|
|
1372
|
-
<span v-if="getFieldDescription(field).isConditional">†</span>
|
|
1373
|
-
</div>
|
|
1374
|
-
<div v-if="field.disabled" class="field-disabled-reason">
|
|
1375
|
-
{{ field.disabledReason }}
|
|
1376
|
-
</div>
|
|
1377
|
-
<div v-if="getFieldDescription(field).isConditional">
|
|
1378
|
-
{{ getFieldDescription(field).description }}
|
|
1379
|
-
</div>
|
|
1380
|
-
</div>
|
|
1381
|
-
<div class="fileColSelect">
|
|
1382
|
-
<SelectInput
|
|
1383
|
-
v-model="fieldMapping[field.id]" icon="table_chart" :options="fileHeaders"
|
|
1384
|
-
searchable :required="isFieldRequired(field)" :disabled="field.disabled"
|
|
1385
|
-
@change="handleSelectChange($event, field.id)"
|
|
1386
|
-
/>
|
|
1387
|
-
</div>
|
|
1388
|
-
<div>
|
|
1389
|
-
<!-- Default Value Input -->
|
|
1390
|
-
<div class="default-value-container hideLabel">
|
|
1391
|
-
{{ initDefaultValue(field.id) }}
|
|
1392
|
-
<component :is="renderField(getFieldWithDefaults(field))" />
|
|
1393
|
-
</div>
|
|
1394
|
-
</div>
|
|
1395
|
-
<div>
|
|
1396
|
-
<SelectInput
|
|
1397
|
-
v-model="fieldDataTypes[field.id]" :options="dataTypeOptions"
|
|
1398
|
-
:disabled="!fieldMapping[field.id] && !defaultValues[field.id]"
|
|
1399
|
-
/>
|
|
1400
|
-
</div>
|
|
1401
|
-
<div>
|
|
1402
|
-
<div class="flex gap-05 my-05">
|
|
1403
|
-
<Btn
|
|
1404
|
-
v-tooltip="'Transform'" thin :disabled="field.disabled" icon="transform"
|
|
1405
|
-
@click="openTransformDialog(field)"
|
|
1406
|
-
/>
|
|
1407
|
-
<Btn
|
|
1408
|
-
v-if="field.$el === 'array'" v-tooltip="'Related File'" thin icon="attach_file"
|
|
1409
|
-
:disabled="field.disabled" @click="openRelatedDialog(field)"
|
|
1410
|
-
/>
|
|
1411
|
-
</div>
|
|
1412
|
-
</div>
|
|
1413
|
-
</div>
|
|
1414
|
-
</div>
|
|
1415
|
-
|
|
1416
|
-
<div v-if="mappingComplete" class="action-buttons">
|
|
1417
|
-
<Btn @click="showPreview">
|
|
1418
|
-
Preview Data
|
|
1419
|
-
</Btn>
|
|
1420
|
-
</div>
|
|
1421
|
-
<div v-else class="action-buttons">
|
|
1422
|
-
<div class="mapping-incomplete-message">
|
|
1423
|
-
Please map the required fields to continue
|
|
1424
|
-
</div>
|
|
1425
|
-
</div>
|
|
1426
|
-
</div>
|
|
1427
|
-
</div>
|
|
1428
|
-
<Dialog :open="showTransformDialog" title="Configure Transformations" width="l" @update:open="showTransformDialog = $event">
|
|
1429
|
-
<div v-if="selectedTransformField">
|
|
1430
|
-
<div class="flex space-between gap-1 mb-1 border-bottom pb-05 m_flex-wrap">
|
|
1431
|
-
<p>Create transformations for <strong>{{ selectedTransformField.label }}</strong></p>
|
|
1432
|
-
<Btn
|
|
1433
|
-
icon="auto_awesome" thin value="Autodetect"
|
|
1434
|
-
@click="autoPopulateTransformations(selectedTransformField.id)"
|
|
1435
|
-
/>
|
|
1436
|
-
</div>
|
|
1437
|
-
|
|
1438
|
-
<div>
|
|
1439
|
-
<div class="grid grid-wrap-7 gap-1 bold pb-05 m_none">
|
|
1440
|
-
<p class="grid-span-2">
|
|
1441
|
-
Source Value
|
|
1442
|
-
</p>
|
|
1443
|
-
<p class="grid-span-4">
|
|
1444
|
-
Target Value
|
|
1445
|
-
</p>
|
|
1446
|
-
<p>Action</p>
|
|
1447
|
-
</div>
|
|
1448
|
-
<div>
|
|
1449
|
-
<div
|
|
1450
|
-
v-for="(transform, index) in transformations[selectedTransformField.id] || []" :key="index"
|
|
1451
|
-
class="grid grid-wrap-7 gap-1 align-items-center m_gap-025 m_pb-1-5"
|
|
1452
|
-
>
|
|
1453
|
-
<p class="grid-span-2 input-size line-height-1">
|
|
1454
|
-
{{ transform.sourceValue }}
|
|
1455
|
-
</p>
|
|
1456
|
-
<p class="grid-span-4 input-size line-height-1 ellipsis-1">
|
|
1457
|
-
{{ transform.targetValue }}
|
|
1458
|
-
</p>
|
|
1459
|
-
<Btn
|
|
1460
|
-
v-tooltip="'Remove'" class="mb-05" thin icon="delete" color="red"
|
|
1461
|
-
@click="removeTransformation(selectedTransformField.id, index)"
|
|
1462
|
-
/>
|
|
1463
|
-
</div>
|
|
1464
|
-
<div class="grid grid-wrap-7 gap-1 align-items-center m_gap-025 m_pb-1-5">
|
|
1465
|
-
<div class="grid-span-2">
|
|
1466
|
-
<SelectInput
|
|
1467
|
-
v-if="fieldMapping[selectedTransformField.id]"
|
|
1468
|
-
v-model="selectedSourceValue" searchable :options="sourceValueOptions"
|
|
1469
|
-
:placeholder="$t('importData.selectSourceValue')"
|
|
1470
|
-
/>
|
|
1471
|
-
<TextInput
|
|
1472
|
-
v-else v-model="selectedSourceValue" type="text"
|
|
1473
|
-
:placeholder="$t('importData.sourceValue')"
|
|
1474
|
-
/>
|
|
1475
|
-
</div>
|
|
1476
|
-
<div class="grid-span-4">
|
|
1477
|
-
<SelectInput
|
|
1478
|
-
v-if="selectedTransformField.options && selectedTransformField.options.length > 0"
|
|
1479
|
-
v-model="selectedTargetValue" searchable :options="selectedTransformField.options"
|
|
1480
|
-
:placeholder="$t('importData.selectTargetValue')"
|
|
1481
|
-
/>
|
|
1482
|
-
<TextInput
|
|
1483
|
-
v-else v-model="selectedTargetValue" type="text"
|
|
1484
|
-
:placeholder="$t('importData.targetValue')"
|
|
1485
|
-
/>
|
|
1486
|
-
</div>
|
|
1487
|
-
<Btn
|
|
1488
|
-
v-tooltip="'Add'" class="mb-05" thin icon="add" color="primary"
|
|
1489
|
-
@click="addTransformation(selectedTransformField.id)"
|
|
1490
|
-
/>
|
|
1491
|
-
</div>
|
|
1492
|
-
</div>
|
|
1493
|
-
</div>
|
|
1494
|
-
|
|
1495
|
-
<div class="flex pt-05">
|
|
1496
|
-
<Btn class="ms-auto" value="$t:importData.close" @click="showTransformDialog = false" />
|
|
1497
|
-
</div>
|
|
1498
|
-
</div>
|
|
1499
|
-
</Dialog>
|
|
1500
|
-
<Dialog :open="showRelatedDialog" title="Configure Related Data" width="xl" @update:open="showRelatedDialog = $event">
|
|
1501
|
-
<div v-if="selectedRelationField">
|
|
1502
|
-
<p class="pb-05">
|
|
1503
|
-
Upload a file with related data for {{ selectedRelationField.label }}
|
|
1504
|
-
</p>
|
|
1505
|
-
|
|
1506
|
-
<div v-if="!relatedFiles[selectedRelationField.id]" class="mb-05">
|
|
1507
|
-
<DragOver
|
|
1508
|
-
accept=".csv,.xls,.xlsx" browse
|
|
1509
|
-
@addFiles="(files) => processRelatedFile(selectedRelationField!.id, files)"
|
|
1510
|
-
>
|
|
1511
|
-
<Card
|
|
1512
|
-
class="flex flex-column items-center justify-center outline-dashed outline-3 hover bg-input"
|
|
1513
|
-
>
|
|
1514
|
-
<Icon name="upload" size="5" />
|
|
1515
|
-
<p>Drag and drop an Excel or CSV file here</p>
|
|
1516
|
-
<p>or click to select a file</p>
|
|
1517
|
-
<p class="txt-12 color-gray">
|
|
1518
|
-
Accepts .xlsx, .xls, and .csv files
|
|
1519
|
-
</p>
|
|
1520
|
-
</Card>
|
|
1521
|
-
</DragOver>
|
|
1522
|
-
</div>
|
|
1523
|
-
|
|
1524
|
-
<div v-else>
|
|
1525
|
-
<div class="mb-1">
|
|
1526
|
-
<Pill>
|
|
1527
|
-
{{ relatedFiles[selectedRelationField.id]!.name }}
|
|
1528
|
-
</Pill>
|
|
1529
|
-
<Btn
|
|
1530
|
-
thin round value="$t:importData.changeFile"
|
|
1531
|
-
@click="relatedFiles[selectedRelationField.id] = null"
|
|
1532
|
-
/>
|
|
1533
|
-
</div>
|
|
1534
|
-
|
|
1535
|
-
<div v-if="relatedFileData[selectedRelationField.id]">
|
|
1536
|
-
<h4>Configure Relationship</h4>
|
|
1537
|
-
|
|
1538
|
-
<div class="flex gap-1">
|
|
1539
|
-
<SelectInput
|
|
1540
|
-
v-model="parentKeyField[selectedRelationField.id]" :options="fileHeaders"
|
|
1541
|
-
:label="$t('importData.sourceKeyField')"
|
|
1542
|
-
/>
|
|
1543
|
-
<SelectInput
|
|
1544
|
-
v-model="relatedKeyField[selectedRelationField.id]"
|
|
1545
|
-
:options="Object.keys(relatedFileData[selectedRelationField.id][0] || {})"
|
|
1546
|
-
:label="$t('importData.relatedKeyField')"
|
|
1547
|
-
/>
|
|
1548
|
-
</div>
|
|
1549
|
-
|
|
1550
|
-
<h4>Map Related Fields</h4>
|
|
1551
|
-
|
|
1552
|
-
<table>
|
|
1553
|
-
<thead>
|
|
1554
|
-
<tr>
|
|
1555
|
-
<th>Child Field</th>
|
|
1556
|
-
<th>Related File Column</th>
|
|
1557
|
-
<th>Default Value</th>
|
|
1558
|
-
<th>Data Type</th>
|
|
1559
|
-
<th>Actions</th>
|
|
1560
|
-
</tr>
|
|
1561
|
-
</thead>
|
|
1562
|
-
<tbody>
|
|
1563
|
-
<tr
|
|
1564
|
-
v-for="schemaItem in (selectedRelationField.attrs?.schema || [])"
|
|
1565
|
-
:key="schemaItem.id"
|
|
1566
|
-
>
|
|
1567
|
-
<td>{{ schemaItem.label }}</td>
|
|
1568
|
-
<td>
|
|
1569
|
-
<SelectInput
|
|
1570
|
-
v-model="relatedFileMappings[selectedRelationField.id][schemaItem.id]"
|
|
1571
|
-
:options="Object.keys(relatedFileData[selectedRelationField.id][0] || {})"
|
|
1572
|
-
:placeholder="$t('importData.selectColumn')"
|
|
1573
|
-
/>
|
|
1574
|
-
</td>
|
|
1575
|
-
<td>
|
|
1576
|
-
<!-- Default Value Input for Related Fields -->
|
|
1577
|
-
<div class="default-value-container">
|
|
1578
|
-
{{ initRelatedDefaultValue(selectedRelationField.id, schemaItem.id) }}
|
|
1579
|
-
<component
|
|
1580
|
-
:is="renderField(getRelatedFieldWithDefaults(selectedRelationField.id, schemaItem))"
|
|
1581
|
-
/>
|
|
1582
|
-
</div>
|
|
1583
|
-
</td>
|
|
1584
|
-
<td>
|
|
1585
|
-
<SelectInput
|
|
1586
|
-
v-model="relatedFieldDataTypes[`${selectedRelationField.id}.${schemaItem.id}`]"
|
|
1587
|
-
:options="dataTypeOptions"
|
|
1588
|
-
:disabled="!relatedFileMappings[selectedRelationField.id]?.[schemaItem.id] && !relatedDefaultValues[selectedRelationField.id]?.[schemaItem.id]"
|
|
1589
|
-
/>
|
|
1590
|
-
</td>
|
|
1591
|
-
<td>
|
|
1592
|
-
<div class="action-buttons-cell">
|
|
1593
|
-
<Btn
|
|
1594
|
-
thin icon="transform"
|
|
1595
|
-
@click="openRelatedTransformDialog(selectedRelationField.id, schemaItem)"
|
|
1596
|
-
>
|
|
1597
|
-
Transform
|
|
1598
|
-
</Btn>
|
|
1599
|
-
</div>
|
|
1600
|
-
</td>
|
|
1601
|
-
</tr>
|
|
1602
|
-
</tbody>
|
|
1603
|
-
</table>
|
|
1604
|
-
</div>
|
|
1605
|
-
</div>
|
|
1606
|
-
<div class="flex pt-05">
|
|
1607
|
-
<Btn class="ms-auto" value="$t:importData.close" @click="showRelatedDialog = false" />
|
|
1608
|
-
</div>
|
|
1609
|
-
</div>
|
|
1610
|
-
</Dialog>
|
|
1611
|
-
|
|
1612
|
-
<Dialog :open="showPreviewModal" title="Data Preview & Edit" width="full" @update:open="showPreviewModal = $event">
|
|
1613
|
-
<div>
|
|
1614
|
-
<Spreadsheet
|
|
1615
|
-
v-model="previewData" class="popupPreviewSpreadsheet" :column-config="spreadsheetColumns"
|
|
1616
|
-
allow-add-row
|
|
1617
|
-
/>
|
|
1618
|
-
</div>
|
|
1619
|
-
<div>
|
|
1620
|
-
<p class="mt-1">
|
|
1621
|
-
Showing all {{ previewData.length }} records. You can edit values directly.
|
|
1622
|
-
</p>
|
|
1623
|
-
<div class="flex gap-1 mt-1 space-between">
|
|
1624
|
-
<Btn flat thin value="$t:importData.cancel" @click="showPreviewModal = false" />
|
|
1625
|
-
<Btn value="$t:importData.importData" @click="processData()" />
|
|
1626
|
-
</div>
|
|
1627
|
-
</div>
|
|
1628
|
-
</Dialog>
|
|
1629
|
-
|
|
1630
|
-
<Dialog :open="showRelatedTransformDialog" title="Configure Related Transformations" width="l" @update:open="showRelatedTransformDialog = $event">
|
|
1631
|
-
<div v-if="selectedRelatedTransformField">
|
|
1632
|
-
<p>
|
|
1633
|
-
Create transformations for <strong>{{ selectedRelatedTransformField.field.label }}</strong> in {{
|
|
1634
|
-
selectedRelationField?.label }}
|
|
1635
|
-
</p>
|
|
1636
|
-
|
|
1637
|
-
<div>
|
|
1638
|
-
<div>
|
|
1639
|
-
<Btn
|
|
1640
|
-
thin icon="auto_awesome" value="Autolink" color="primary" @click="autoPopulateRelatedTransformations(
|
|
1641
|
-
selectedRelatedTransformField.parentId,
|
|
1642
|
-
selectedRelatedTransformField.field.id,
|
|
1643
|
-
)"
|
|
1644
|
-
/>
|
|
1645
|
-
</div>
|
|
1646
|
-
</div>
|
|
1647
|
-
|
|
1648
|
-
<div>
|
|
1649
|
-
<table>
|
|
1650
|
-
<thead>
|
|
1651
|
-
<tr>
|
|
1652
|
-
<th>Source Value</th>
|
|
1653
|
-
<th>Target Value</th>
|
|
1654
|
-
<th>Action</th>
|
|
1655
|
-
</tr>
|
|
1656
|
-
</thead>
|
|
1657
|
-
<tbody>
|
|
1658
|
-
<tr
|
|
1659
|
-
v-for="(transform, index) in relatedTransformations[selectedRelatedTransformField.parentId]?.[selectedRelatedTransformField.field.id] || []"
|
|
1660
|
-
:key="index"
|
|
1661
|
-
>
|
|
1662
|
-
<td>{{ transform.sourceValue }}</td>
|
|
1663
|
-
<td>{{ transform.targetValue }}</td>
|
|
1664
|
-
<td>
|
|
1665
|
-
<Btn
|
|
1666
|
-
v-tooltip="'Remove'" thin icon="delete" color="red" @click="removeRelatedTransformation(
|
|
1667
|
-
selectedRelatedTransformField.parentId,
|
|
1668
|
-
selectedRelatedTransformField.field.id,
|
|
1669
|
-
index,
|
|
1670
|
-
)"
|
|
1671
|
-
/>
|
|
1672
|
-
</td>
|
|
1673
|
-
</tr>
|
|
1674
|
-
<tr>
|
|
1675
|
-
<td>
|
|
1676
|
-
<SelectInput
|
|
1677
|
-
v-if="relatedFileMappings[selectedRelatedTransformField.parentId]?.[selectedRelatedTransformField.field.id]"
|
|
1678
|
-
v-model="selectedRelatedSourceValue" searchable
|
|
1679
|
-
:options="relatedSourceValueOptions"
|
|
1680
|
-
:placeholder="$t('importData.selectSourceValue')"
|
|
1681
|
-
/>
|
|
1682
|
-
<input
|
|
1683
|
-
v-else v-model="selectedRelatedSourceValue" type="text"
|
|
1684
|
-
:placeholder="$t('importData.sourceValue')"
|
|
1685
|
-
>
|
|
1686
|
-
</td>
|
|
1687
|
-
<td>
|
|
1688
|
-
<SelectInput
|
|
1689
|
-
v-if="selectedRelatedTransformField.field.options && selectedRelatedTransformField.field.options.length > 0"
|
|
1690
|
-
v-model="selectedRelatedTargetValue" searchable
|
|
1691
|
-
:options="selectedRelatedTransformField.field.options"
|
|
1692
|
-
:placeholder="$t('importData.selectTargetValue')"
|
|
1693
|
-
/>
|
|
1694
|
-
<input
|
|
1695
|
-
v-else v-model="selectedRelatedTargetValue" type="text"
|
|
1696
|
-
:placeholder="$t('importData.targetValue')"
|
|
1697
|
-
>
|
|
1698
|
-
</td>
|
|
1699
|
-
<td>
|
|
1700
|
-
<Btn
|
|
1701
|
-
v-tooltip="'Add'" thin icon="add" color="primary" @click="addRelatedTransformation(
|
|
1702
|
-
selectedRelatedTransformField.parentId,
|
|
1703
|
-
selectedRelatedTransformField.field.id,
|
|
1704
|
-
)"
|
|
1705
|
-
/>
|
|
1706
|
-
</td>
|
|
1707
|
-
</tr>
|
|
1708
|
-
</tbody>
|
|
1709
|
-
</table>
|
|
1710
|
-
</div>
|
|
1711
|
-
|
|
1712
|
-
<div>
|
|
1713
|
-
<Btn value="$t:importData.close" @click="showRelatedTransformDialog = false" />
|
|
1714
|
-
</div>
|
|
1715
|
-
</div>
|
|
1716
|
-
</Dialog>
|
|
1717
|
-
</Card>
|
|
1718
|
-
</template>
|
|
1719
|
-
|
|
1720
|
-
<style>
|
|
1721
|
-
.fileColSelect {
|
|
1722
|
-
--bgl-input-bg: var(--bgl-green-light);
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
.fileColSelect .bgl_icon-font {
|
|
1726
|
-
color: var(--bgl-green);
|
|
1727
|
-
line-height: 0;
|
|
1728
|
-
}
|
|
1729
|
-
|
|
1730
|
-
.hideLabel label,
|
|
1731
|
-
.hideLabel .label {
|
|
1732
|
-
font-size: 0 !important;
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
|
-
.mapping-table .selectinput-btn:disabled {
|
|
1736
|
-
background: var(--bgl-input-bg) !important;
|
|
1737
|
-
cursor: not-allowed !important;
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
.field-label {
|
|
1741
|
-
--bgl-pill-height: 20px;
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
.popupPreviewSpreadsheet .spreadsheet {
|
|
1745
|
-
width: 100%;
|
|
1746
|
-
overflow: auto;
|
|
1747
|
-
height: calc(100vh - 300px);
|
|
1748
|
-
}
|
|
1749
|
-
</style>
|