Skip to content

Commit 6e47349

Browse files
committed
Load live preview column headers for resolver and processing steps
The resolver and processing steps' Data Source Index selects read columns from configData.columnHeaders, which reflects only the persisted config — running a preview does not invalidate that query, so the selects stayed empty until a save/reload. Add a shared useColumnHeaderOptions hook that loads the columns live from the current loader/interpreter config (the endpoint the mapping step uses). data-setup-tab owns a previewVersion counter that the preview step bumps on copy-from-source and upload; the hook refetches when it changes, so columns stay in sync with the preview data without refetching on every visit. Config changes refetch on their own via the query key.
1 parent 52a5b57 commit 6e47349

4 files changed

Lines changed: 103 additions & 26 deletions

File tree

assets/studio/js/src/modules/data-importer/components/tabs/data-setup-tab.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { ResolverStep } from './steps/resolver-step'
1818
import { MappingStep } from './steps/mapping-step'
1919
import { ProcessingSettingsStep } from './steps/processing-settings-step'
2020
import { useStyles } from './data-setup-tab.styles'
21-
import { useBundleDataImporterConfigGetQuery } from '../../data-importer-api-slice-enhanced'
21+
import { useColumnHeaderOptions } from '../../hooks/use-column-header-options'
2222
import { Box } from '@pimcore/studio-ui-bundle/components'
2323

2424
export interface DataSetupTabProps {
@@ -29,17 +29,17 @@ export const DataSetupTab = ({ configName }: DataSetupTabProps): React.JSX.Eleme
2929
const { t } = useTranslation()
3030
const { styles } = useStyles()
3131
const [currentStep, setCurrentStep] = useState(0)
32+
// Bumped on preview-data change so dependent steps refresh their column lists.
33+
const [previewVersion, setPreviewVersion] = useState(0)
3234

33-
const { data: configData } = useBundleDataImporterConfigGetQuery({ name: configName })
34-
const columnHeaderOptions = useMemo(
35-
() => (configData?.columnHeaders ?? []).map((header) => {
36-
// API returns objects {id, dataIndex, label}; type says string[] — handle both
37-
const h = header as unknown as { dataIndex?: string, label?: string } | string
38-
const value = typeof h === 'string' ? h : (h.dataIndex ?? '')
39-
const label = typeof h === 'string' ? h : (h.label ?? h.dataIndex ?? '')
40-
return { value, label }
41-
}),
42-
[configData?.columnHeaders]
35+
const RESOLVER_STEP_INDEX = 2
36+
const PROCESSING_STEP_INDEX = 4
37+
38+
// Shared column options for the resolver and processing steps; loaded while either is active.
39+
const columnHeaderOptions = useColumnHeaderOptions(
40+
configName,
41+
currentStep === RESOLVER_STEP_INDEX || currentStep === PROCESSING_STEP_INDEX,
42+
previewVersion
4343
)
4444

4545
const steps: StepItem[] = useMemo(() => [
@@ -83,6 +83,7 @@ export const DataSetupTab = ({ configName }: DataSetupTabProps): React.JSX.Eleme
8383
<PreviewImportStep
8484
configName={ configName }
8585
isActive={ currentStep === 1 }
86+
onPreviewDataChange={ () => { setPreviewVersion((v) => v + 1) } }
8687
/>
8788
</div>
8889

@@ -95,7 +96,7 @@ export const DataSetupTab = ({ configName }: DataSetupTabProps): React.JSX.Eleme
9596
</div>
9697

9798
<div className={ cn(styles.stepContent, currentStep !== 4 && styles.stepContentHidden) }>
98-
<ProcessingSettingsStep configName={ configName } />
99+
<ProcessingSettingsStep columnHeaderOptions={ columnHeaderOptions } />
99100
</div>
100101
</Flex>
101102
)

assets/studio/js/src/modules/data-importer/components/tabs/steps/preview-import-step/preview-import-step.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import { usePreviewRecordQuery } from '../shared/use-preview-record-query'
2626
export interface PreviewImportStepProps {
2727
configName: string
2828
isActive: boolean
29+
/** Called when the preview data changes (copy from source / upload). */
30+
onPreviewDataChange?: () => void
2931
}
3032

3133
interface PreviewRow {
@@ -42,7 +44,7 @@ const isNotFoundError = (error: unknown): boolean => {
4244

4345
const columnHelper = createColumnHelper<PreviewRow>()
4446

45-
export const PreviewImportStep = ({ configName, isActive }: PreviewImportStepProps): React.JSX.Element => {
47+
export const PreviewImportStep = ({ configName, isActive, onPreviewDataChange }: PreviewImportStepProps): React.JSX.Element => {
4648
const { t } = useTranslation()
4749
const form = Form.useFormInstance()
4850

@@ -111,6 +113,7 @@ export const PreviewImportStep = ({ configName, isActive }: PreviewImportStepPro
111113
}
112114

113115
fetchPreview(0, { forceRefetch: true })
116+
onPreviewDataChange?.()
114117
}
115118

116119
const dropdownItems = [
@@ -230,6 +233,7 @@ export const PreviewImportStep = ({ configName, isActive }: PreviewImportStepPro
230233
onUploadSuccess={ () => {
231234
setUploadModalOpen(false)
232235
fetchPreview(0, { forceRefetch: true })
236+
onPreviewDataChange?.()
233237
} }
234238
open={ uploadModalOpen }
235239
title={ t('data-importer.preview-import.upload-file') }

assets/studio/js/src/modules/data-importer/components/tabs/steps/processing-settings-step/processing-settings-step.tsx

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,22 @@
1111
import React, { useEffect } from 'react'
1212
import { Flex, Select, Form, Switch } from '@pimcore/studio-ui-bundle/components'
1313
import { useTranslation } from '@pimcore/studio-ui-bundle/app'
14-
import { useBundleDataImporterConfigGetQuery } from '../../../../data-importer-api-slice-enhanced'
1514
import { DataImporterPanel } from '../data-importer-panel/data-importer-panel'
1615
import { StepHeading } from '../step-heading/step-heading'
1716
import { filterByLabel } from '../../../../utils/select-utils'
1817
import type { DataImporterFormValues } from '../../../../types'
18+
import { type ColumnHeaderOption } from '../../../../hooks/use-column-header-options'
1919
import { useStyles } from './processing-settings-step.styles'
2020

2121
export interface ProcessingSettingsStepProps {
22-
configName: string
22+
columnHeaderOptions: ColumnHeaderOption[]
2323
}
2424

25-
export const ProcessingSettingsStep = ({ configName }: ProcessingSettingsStepProps): React.JSX.Element => {
25+
export const ProcessingSettingsStep = ({ columnHeaderOptions }: ProcessingSettingsStepProps): React.JSX.Element => {
2626
const { t } = useTranslation()
2727
const { styles, cx } = useStyles()
2828
const form = Form.useFormInstance()
2929

30-
const { data: configData } = useBundleDataImporterConfigGetQuery({ name: configName })
31-
32-
const columnHeaderOptions = (configData?.columnHeaders ?? []).map((header) => {
33-
// API returns objects {id, dataIndex, label}; type says string[] — handle both
34-
const h = header as unknown as { dataIndex?: string, label?: string } | string
35-
const value = typeof h === 'string' ? h : (h.dataIndex ?? '')
36-
const label = typeof h === 'string' ? h : (h.label ?? h.dataIndex ?? '')
37-
return { value, label }
38-
})
39-
4030
const executionTypeOptions = [
4131
{ value: 'sequential', label: t('data-importer.processing.execution-type.sequential') },
4232
{ value: 'parallel', label: t('data-importer.processing.execution-type.parallel') }
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* This source file is available under the terms of the
3+
* Pimcore Open Core License (POCL)
4+
* Full copyright and license information is available in
5+
* LICENSE.md which is distributed with this source code.
6+
*
7+
* @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com)
8+
* @license Pimcore Open Core License (POCL)
9+
*/
10+
11+
import { useEffect, useMemo, useRef } from 'react'
12+
import { Form } from '@pimcore/studio-ui-bundle/components'
13+
import { useBundleDataImporterConfigGetQuery } from '../data-importer-api-slice-enhanced'
14+
import { useBundleDataImporterConfigLoadColumnHeadersQuery } from '../data-importer-api-slice.gen'
15+
import { transformFormToBackend, type BackendConfiguration } from '../utils/transformers'
16+
import { type DataImporterFormValues } from '../types'
17+
18+
export interface ColumnHeaderOption {
19+
value: string
20+
label: string
21+
}
22+
23+
/**
24+
* Returns the preview "Data Source Index" column options, loaded live from the current
25+
* loader/interpreter config (persisted `configData.columnHeaders` can be stale).
26+
*
27+
* Bump `previewVersion` when the preview data changes to trigger a refetch.
28+
* Must be called from within the detail-view Form context.
29+
*/
30+
export const useColumnHeaderOptions = (
31+
configName: string,
32+
active: boolean,
33+
previewVersion: number = 0
34+
): ColumnHeaderOption[] => {
35+
const form = Form.useFormInstance()
36+
const { data: configData } = useBundleDataImporterConfigGetQuery({ name: configName })
37+
38+
const loaderConfigType = Form.useWatch(['loaderConfig', 'type']) as string | undefined
39+
const interpreterConfigType = Form.useWatch(['interpreterConfig', 'type']) as string | undefined
40+
41+
const headersRequest = useMemo(() => {
42+
if (configData === undefined || loaderConfigType === undefined) return undefined
43+
const formValues = form.getFieldsValue(true) as DataImporterFormValues
44+
const existingConfig = (configData.configuration ?? {}) as BackendConfiguration
45+
const backendConfig = transformFormToBackend(formValues, existingConfig)
46+
return {
47+
name: configName,
48+
bundleDataImporterCopyPreviewParameters: {
49+
currentConfig: {
50+
loaderConfig: backendConfig.loaderConfig,
51+
interpreterConfig: backendConfig.interpreterConfig
52+
}
53+
}
54+
}
55+
}, [configName, configData, form, loaderConfigType, interpreterConfigType])
56+
57+
const { data: liveHeaders, refetch } = useBundleDataImporterConfigLoadColumnHeadersQuery(
58+
headersRequest!,
59+
{ skip: headersRequest === undefined || !active }
60+
)
61+
62+
// Refetch on preview-data change (the query key doesn't change after a copy/upload).
63+
const lastFetchedVersionRef = useRef(previewVersion)
64+
useEffect(() => {
65+
if (!active || headersRequest === undefined) return
66+
if (previewVersion !== lastFetchedVersionRef.current) {
67+
lastFetchedVersionRef.current = previewVersion
68+
void refetch().catch(() => undefined)
69+
}
70+
}, [active, previewVersion, headersRequest, refetch])
71+
72+
return useMemo(
73+
() => (liveHeaders?.columnHeaders ?? configData?.columnHeaders ?? []).map((header) => {
74+
// API returns {dataIndex, label} objects; the persisted type says string[] — handle both
75+
const h = header as unknown as { dataIndex?: string, label?: string } | string
76+
const value = typeof h === 'string' ? h : (h.dataIndex ?? '')
77+
const label = typeof h === 'string' ? h : (h.label ?? h.dataIndex ?? '')
78+
return { value, label }
79+
}),
80+
[liveHeaders?.columnHeaders, configData?.columnHeaders]
81+
)
82+
}

0 commit comments

Comments
 (0)