@oronts/vendure-data-hub-plugin 0.1.1 → 0.1.2

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 (235) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/dashboard/components/common/ConnectionConfigEditor.tsx +589 -0
  3. package/dist/dashboard/components/common/HeadersEditor.tsx +90 -0
  4. package/dist/dashboard/components/common/ValidationFeedback.tsx +17 -0
  5. package/dist/dashboard/components/common/index.ts +10 -0
  6. package/dist/dashboard/components/pipelines/PipelineEditor.tsx +504 -0
  7. package/dist/dashboard/components/pipelines/PipelineExport.tsx +63 -0
  8. package/dist/dashboard/components/pipelines/PipelineImport.tsx +87 -0
  9. package/dist/dashboard/components/pipelines/ReactFlowPipelineEditor.tsx +539 -0
  10. package/dist/dashboard/components/pipelines/shared/NodePropertiesPanel.tsx +146 -0
  11. package/dist/dashboard/components/pipelines/shared/PipelineNode.tsx +155 -0
  12. package/dist/dashboard/components/pipelines/shared/PipelineSettingsPanel.tsx +392 -0
  13. package/dist/dashboard/components/pipelines/shared/StepListItem.tsx +144 -0
  14. package/dist/dashboard/components/pipelines/shared/index.ts +33 -0
  15. package/dist/dashboard/components/pipelines/shared/visual-node-config.ts +169 -0
  16. package/dist/dashboard/components/shared/LoadMoreButton.tsx +18 -0
  17. package/dist/dashboard/components/shared/entity-selector/EntitySelector.tsx +59 -0
  18. package/dist/dashboard/components/shared/entity-selector/index.ts +1 -0
  19. package/dist/dashboard/components/shared/error-boundary/ErrorBoundary.tsx +90 -0
  20. package/dist/dashboard/components/shared/error-boundary/index.ts +1 -0
  21. package/dist/dashboard/components/shared/feedback/EmptyState.tsx +36 -0
  22. package/dist/dashboard/components/shared/feedback/ErrorState.tsx +69 -0
  23. package/dist/dashboard/components/shared/feedback/LoadingState.tsx +104 -0
  24. package/dist/dashboard/components/shared/feedback/ValidationErrorDisplay.tsx +29 -0
  25. package/dist/dashboard/components/shared/feedback/index.ts +4 -0
  26. package/dist/dashboard/components/shared/file-dropzone/FileDropzone.tsx +167 -0
  27. package/dist/dashboard/components/shared/file-dropzone/index.ts +1 -0
  28. package/dist/dashboard/components/shared/filter-conditions-editor/FilterConditionsEditor.tsx +226 -0
  29. package/dist/dashboard/components/shared/filter-conditions-editor/index.ts +1 -0
  30. package/dist/dashboard/components/shared/index.ts +45 -0
  31. package/dist/dashboard/components/shared/schema-form/SchemaFormRenderer.tsx +248 -0
  32. package/dist/dashboard/components/shared/schema-form/fields/BooleanField.tsx +26 -0
  33. package/dist/dashboard/components/shared/schema-form/fields/FieldWrapper.tsx +28 -0
  34. package/dist/dashboard/components/shared/schema-form/fields/FileUploadField.tsx +171 -0
  35. package/dist/dashboard/components/shared/schema-form/fields/JsonField.tsx +132 -0
  36. package/dist/dashboard/components/shared/schema-form/fields/NumberField.tsx +33 -0
  37. package/dist/dashboard/components/shared/schema-form/fields/SelectField.tsx +70 -0
  38. package/dist/dashboard/components/shared/schema-form/fields/StringField.tsx +36 -0
  39. package/dist/dashboard/components/shared/schema-form/fields/TextareaField.tsx +31 -0
  40. package/dist/dashboard/components/shared/schema-form/fields/index.ts +23 -0
  41. package/dist/dashboard/components/shared/schema-form/index.ts +2 -0
  42. package/dist/dashboard/components/shared/schema-form/utils.ts +3 -0
  43. package/dist/dashboard/components/shared/selectable-card/SelectableCard.tsx +65 -0
  44. package/dist/dashboard/components/shared/selectable-card/index.ts +1 -0
  45. package/dist/dashboard/components/shared/stat-card/StatCard.tsx +121 -0
  46. package/dist/dashboard/components/shared/stat-card/index.ts +1 -0
  47. package/dist/dashboard/components/shared/step-config/AdapterRequiredWarning.tsx +25 -0
  48. package/dist/dashboard/components/shared/step-config/AdapterSelector.tsx +109 -0
  49. package/dist/dashboard/components/shared/step-config/AdvancedEditors.tsx +634 -0
  50. package/dist/dashboard/components/shared/step-config/EnrichConfigComponent.tsx +295 -0
  51. package/dist/dashboard/components/shared/step-config/ExtractTestResults.tsx +143 -0
  52. package/dist/dashboard/components/shared/step-config/GateConfigComponent.tsx +127 -0
  53. package/dist/dashboard/components/shared/step-config/LoadTestResults.tsx +104 -0
  54. package/dist/dashboard/components/shared/step-config/OperatorCard.tsx +266 -0
  55. package/dist/dashboard/components/shared/step-config/OperatorCheatSheetButton.tsx +54 -0
  56. package/dist/dashboard/components/shared/step-config/OperatorFieldInput.tsx +209 -0
  57. package/dist/dashboard/components/shared/step-config/RetrySettingsComponent.tsx +111 -0
  58. package/dist/dashboard/components/shared/step-config/RouteConfigComponent.tsx +125 -0
  59. package/dist/dashboard/components/shared/step-config/StepConfigPanel.tsx +564 -0
  60. package/dist/dashboard/components/shared/step-config/StepTester.tsx +165 -0
  61. package/dist/dashboard/components/shared/step-config/TestResultContainer.tsx +57 -0
  62. package/dist/dashboard/components/shared/step-config/TransformTestResults.tsx +130 -0
  63. package/dist/dashboard/components/shared/step-config/ValidateConfigComponent.tsx +334 -0
  64. package/dist/dashboard/components/shared/step-config/index.ts +29 -0
  65. package/dist/dashboard/components/shared/step-config/step-test-handlers.ts +297 -0
  66. package/dist/dashboard/components/shared/trigger-config/TriggerForm.tsx +478 -0
  67. package/dist/dashboard/components/shared/trigger-config/index.ts +1 -0
  68. package/dist/dashboard/components/shared/triggers-panel/TriggersPanel.tsx +281 -0
  69. package/dist/dashboard/components/shared/triggers-panel/index.ts +1 -0
  70. package/dist/dashboard/components/shared/wizard/ConfigurationNameCard.tsx +59 -0
  71. package/dist/dashboard/components/shared/wizard/SummaryCard.tsx +53 -0
  72. package/dist/dashboard/components/shared/wizard/WizardFooter.tsx +47 -0
  73. package/dist/dashboard/components/shared/wizard/WizardProgressBar.tsx +78 -0
  74. package/dist/dashboard/components/shared/wizard/index.ts +4 -0
  75. package/dist/dashboard/components/shared/wizard-trigger/TriggerSchemaFields.tsx +128 -0
  76. package/dist/dashboard/components/shared/wizard-trigger/TriggerSelector.tsx +33 -0
  77. package/dist/dashboard/components/shared/wizard-trigger/index.ts +2 -0
  78. package/dist/dashboard/components/templates/TemplateGallery.tsx +210 -0
  79. package/dist/dashboard/components/templates/TemplatePreview.tsx +214 -0
  80. package/dist/dashboard/components/templates/index.ts +4 -0
  81. package/dist/dashboard/components/wizards/export-wizard/DestinationStep.tsx +207 -0
  82. package/dist/dashboard/components/wizards/export-wizard/ExportWizard.tsx +221 -0
  83. package/dist/dashboard/components/wizards/export-wizard/FieldsStep.tsx +159 -0
  84. package/dist/dashboard/components/wizards/export-wizard/FormatStep.tsx +246 -0
  85. package/dist/dashboard/components/wizards/export-wizard/ReviewStep.tsx +231 -0
  86. package/dist/dashboard/components/wizards/export-wizard/SourceStep.tsx +154 -0
  87. package/dist/dashboard/components/wizards/export-wizard/TriggerStep.tsx +234 -0
  88. package/dist/dashboard/components/wizards/export-wizard/constants.ts +73 -0
  89. package/dist/dashboard/components/wizards/export-wizard/index.ts +2 -0
  90. package/dist/dashboard/components/wizards/export-wizard/types.ts +39 -0
  91. package/dist/dashboard/components/wizards/import-wizard/ImportWizard.tsx +350 -0
  92. package/dist/dashboard/components/wizards/import-wizard/MappingStep.tsx +286 -0
  93. package/dist/dashboard/components/wizards/import-wizard/PreviewStep.tsx +79 -0
  94. package/dist/dashboard/components/wizards/import-wizard/ReviewStep.tsx +266 -0
  95. package/dist/dashboard/components/wizards/import-wizard/SourceStep.tsx +537 -0
  96. package/dist/dashboard/components/wizards/import-wizard/StrategyStep.tsx +328 -0
  97. package/dist/dashboard/components/wizards/import-wizard/TargetStep.tsx +76 -0
  98. package/dist/dashboard/components/wizards/import-wizard/TemplateStep.tsx +116 -0
  99. package/dist/dashboard/components/wizards/import-wizard/TransformStep.tsx +666 -0
  100. package/dist/dashboard/components/wizards/import-wizard/TriggerStep.tsx +51 -0
  101. package/dist/dashboard/components/wizards/import-wizard/constants.ts +104 -0
  102. package/dist/dashboard/components/wizards/import-wizard/index.ts +3 -0
  103. package/dist/dashboard/components/wizards/import-wizard/types.ts +35 -0
  104. package/dist/dashboard/components/wizards/index.ts +7 -0
  105. package/dist/dashboard/components/wizards/shared/WizardStepContainer.tsx +27 -0
  106. package/dist/dashboard/components/wizards/shared/constants.ts +16 -0
  107. package/dist/dashboard/components/wizards/shared/index.ts +10 -0
  108. package/dist/dashboard/constants/colors.ts +25 -0
  109. package/dist/dashboard/constants/connection-defaults.ts +7 -0
  110. package/dist/dashboard/constants/connection-types.ts +1 -0
  111. package/dist/dashboard/constants/defaults.ts +18 -0
  112. package/dist/dashboard/constants/editor.ts +69 -0
  113. package/dist/dashboard/constants/enum-maps.ts +18 -0
  114. package/dist/dashboard/constants/fallbacks.ts +44 -0
  115. package/dist/dashboard/constants/file-format-registry.ts +206 -0
  116. package/dist/dashboard/constants/index.ts +24 -0
  117. package/dist/dashboard/constants/navigation.ts +29 -0
  118. package/dist/dashboard/constants/permissions.ts +41 -0
  119. package/dist/dashboard/constants/placeholders.ts +77 -0
  120. package/dist/dashboard/constants/routes.ts +12 -0
  121. package/dist/dashboard/constants/run-status.ts +1 -0
  122. package/dist/dashboard/constants/sentinel-values.ts +12 -0
  123. package/dist/dashboard/constants/step-configs.ts +9 -0
  124. package/dist/dashboard/constants/step-mappings.ts +170 -0
  125. package/dist/dashboard/constants/steps.ts +37 -0
  126. package/dist/dashboard/constants/toast-messages.ts +149 -0
  127. package/dist/dashboard/constants/triggers.ts +5 -0
  128. package/dist/dashboard/constants/ui-config.ts +139 -0
  129. package/dist/dashboard/constants/ui-dimensions.ts +145 -0
  130. package/dist/dashboard/constants/ui-states.ts +28 -0
  131. package/dist/dashboard/constants/ui-types.ts +85 -0
  132. package/dist/dashboard/constants/validation-patterns.ts +26 -0
  133. package/dist/dashboard/gql/gql.ts +370 -0
  134. package/dist/dashboard/gql/graphql.ts +10378 -0
  135. package/dist/dashboard/gql/index.ts +1 -0
  136. package/dist/dashboard/hooks/api/index.ts +115 -0
  137. package/dist/dashboard/hooks/api/mutation-helpers.ts +34 -0
  138. package/dist/dashboard/hooks/api/use-adapters.ts +92 -0
  139. package/dist/dashboard/hooks/api/use-config-options.ts +513 -0
  140. package/dist/dashboard/hooks/api/use-connections.ts +84 -0
  141. package/dist/dashboard/hooks/api/use-entity-field-schemas.ts +99 -0
  142. package/dist/dashboard/hooks/api/use-entity-loaders.ts +45 -0
  143. package/dist/dashboard/hooks/api/use-hooks.ts +68 -0
  144. package/dist/dashboard/hooks/api/use-logs.ts +102 -0
  145. package/dist/dashboard/hooks/api/use-pipeline-runs.ts +221 -0
  146. package/dist/dashboard/hooks/api/use-pipelines.ts +279 -0
  147. package/dist/dashboard/hooks/api/use-queues.ts +141 -0
  148. package/dist/dashboard/hooks/api/use-secrets.ts +75 -0
  149. package/dist/dashboard/hooks/api/use-settings.ts +55 -0
  150. package/dist/dashboard/hooks/api/use-step-tester.ts +79 -0
  151. package/dist/dashboard/hooks/index.ts +13 -0
  152. package/dist/dashboard/hooks/use-adapter-catalog.ts +253 -0
  153. package/dist/dashboard/hooks/use-export-templates.ts +80 -0
  154. package/dist/dashboard/hooks/use-import-templates.ts +139 -0
  155. package/dist/dashboard/hooks/use-load-more.ts +29 -0
  156. package/dist/dashboard/hooks/use-stable-keys.ts +54 -0
  157. package/dist/dashboard/hooks/use-trigger-types.ts +100 -0
  158. package/dist/dashboard/hooks/use-wizard-navigation.ts +128 -0
  159. package/dist/dashboard/index.tsx +55 -0
  160. package/dist/dashboard/routes/adapters/AdapterCard.tsx +102 -0
  161. package/dist/dashboard/routes/adapters/AdapterConstants.tsx +20 -0
  162. package/dist/dashboard/routes/adapters/AdapterDetail.tsx +208 -0
  163. package/dist/dashboard/routes/adapters/AdapterTypeSection.tsx +105 -0
  164. package/dist/dashboard/routes/adapters/AdaptersPage.tsx +276 -0
  165. package/dist/dashboard/routes/adapters/AdaptersTable.tsx +107 -0
  166. package/dist/dashboard/routes/adapters/index.ts +1 -0
  167. package/dist/dashboard/routes/connections/ConnectionDetail.tsx +218 -0
  168. package/dist/dashboard/routes/connections/ConnectionsList.tsx +34 -0
  169. package/dist/dashboard/routes/connections/index.ts +2 -0
  170. package/dist/dashboard/routes/hooks/Hooks.tsx +425 -0
  171. package/dist/dashboard/routes/hooks/hook-stages.ts +52 -0
  172. package/dist/dashboard/routes/hooks/index.ts +1 -0
  173. package/dist/dashboard/routes/index.ts +8 -0
  174. package/dist/dashboard/routes/logs/Logs.tsx +93 -0
  175. package/dist/dashboard/routes/logs/components/LogDetailDrawer.tsx +118 -0
  176. package/dist/dashboard/routes/logs/components/LogExplorerTab.tsx +367 -0
  177. package/dist/dashboard/routes/logs/components/LogLevelBadge.tsx +34 -0
  178. package/dist/dashboard/routes/logs/components/LogTableRow.tsx +70 -0
  179. package/dist/dashboard/routes/logs/components/LogsOverviewTab.tsx +178 -0
  180. package/dist/dashboard/routes/logs/components/RealtimeLogTab.tsx +122 -0
  181. package/dist/dashboard/routes/logs/index.ts +1 -0
  182. package/dist/dashboard/routes/pipelines/ErrorAuditList.tsx +39 -0
  183. package/dist/dashboard/routes/pipelines/ExportWizardPage.tsx +96 -0
  184. package/dist/dashboard/routes/pipelines/ImportWizardPage.tsx +104 -0
  185. package/dist/dashboard/routes/pipelines/PipelineDetail.tsx +211 -0
  186. package/dist/dashboard/routes/pipelines/PipelineRunsBlock.tsx +377 -0
  187. package/dist/dashboard/routes/pipelines/PipelinesList.tsx +87 -0
  188. package/dist/dashboard/routes/pipelines/RetryPatchHelper.tsx +51 -0
  189. package/dist/dashboard/routes/pipelines/RunDetailsPanel.tsx +238 -0
  190. package/dist/dashboard/routes/pipelines/RunErrorsList.tsx +116 -0
  191. package/dist/dashboard/routes/pipelines/StepCounters.tsx +24 -0
  192. package/dist/dashboard/routes/pipelines/StepSummaryTable.tsx +36 -0
  193. package/dist/dashboard/routes/pipelines/components/DryRunDialog.tsx +341 -0
  194. package/dist/dashboard/routes/pipelines/components/PipelineActionButtons.tsx +201 -0
  195. package/dist/dashboard/routes/pipelines/components/PipelineEditorToggle.tsx +116 -0
  196. package/dist/dashboard/routes/pipelines/components/PipelineFormFields.tsx +156 -0
  197. package/dist/dashboard/routes/pipelines/components/PipelineWebhookInfo.tsx +111 -0
  198. package/dist/dashboard/routes/pipelines/components/ReviewActionsPanel.tsx +342 -0
  199. package/dist/dashboard/routes/pipelines/components/ValidationPanel.tsx +121 -0
  200. package/dist/dashboard/routes/pipelines/components/VersionHistoryDialog.tsx +131 -0
  201. package/dist/dashboard/routes/pipelines/components/index.ts +25 -0
  202. package/dist/dashboard/routes/pipelines/hooks/index.ts +1 -0
  203. package/dist/dashboard/routes/pipelines/hooks/use-pipeline-validation.ts +114 -0
  204. package/dist/dashboard/routes/pipelines/index.ts +4 -0
  205. package/dist/dashboard/routes/pipelines/utils/index.ts +1 -0
  206. package/dist/dashboard/routes/pipelines/utils/pipeline-conversion.ts +261 -0
  207. package/dist/dashboard/routes/queues/ConsumersTable.tsx +134 -0
  208. package/dist/dashboard/routes/queues/DeadLettersTable.tsx +118 -0
  209. package/dist/dashboard/routes/queues/FailedRunsTable.tsx +74 -0
  210. package/dist/dashboard/routes/queues/QueuesPage.tsx +290 -0
  211. package/dist/dashboard/routes/queues/index.ts +1 -0
  212. package/dist/dashboard/routes/queues/types.ts +22 -0
  213. package/dist/dashboard/routes/secrets/SecretDetail.tsx +278 -0
  214. package/dist/dashboard/routes/secrets/SecretsList.tsx +34 -0
  215. package/dist/dashboard/routes/secrets/index.ts +2 -0
  216. package/dist/dashboard/routes/settings/Settings.tsx +343 -0
  217. package/dist/dashboard/routes/settings/index.ts +1 -0
  218. package/dist/dashboard/types/index.ts +89 -0
  219. package/dist/dashboard/types/pipeline.ts +51 -0
  220. package/dist/dashboard/types/ui-types.ts +400 -0
  221. package/dist/dashboard/types/wizard.ts +235 -0
  222. package/dist/dashboard/utils/adapter-grouping.ts +43 -0
  223. package/dist/dashboard/utils/column-analysis.ts +11 -0
  224. package/dist/dashboard/utils/field-preparation.ts +31 -0
  225. package/dist/dashboard/utils/form-validation.ts +373 -0
  226. package/dist/dashboard/utils/formatters.ts +92 -0
  227. package/dist/dashboard/utils/icon-resolver.ts +35 -0
  228. package/dist/dashboard/utils/index.ts +60 -0
  229. package/dist/dashboard/utils/query-key-factory.ts +54 -0
  230. package/dist/dashboard/utils/step-helpers.ts +32 -0
  231. package/dist/dashboard/utils/string-helpers.ts +4 -0
  232. package/dist/dashboard/utils/template-helpers.ts +26 -0
  233. package/dist/dashboard/utils/trigger-sync.ts +138 -0
  234. package/dist/dashboard/utils/wizard-to-pipeline.ts +569 -0
  235. package/package.json +2 -2
@@ -0,0 +1,231 @@
1
+ import * as React from 'react';
2
+ import {
3
+ Badge,
4
+ Accordion,
5
+ AccordionContent,
6
+ AccordionItem,
7
+ AccordionTrigger,
8
+ } from '@vendure/dashboard';
9
+ import {
10
+ Database,
11
+ Columns,
12
+ FileSpreadsheet,
13
+ Send,
14
+ } from 'lucide-react';
15
+ import { VENDURE_ENTITY_LIST } from '../../../../shared';
16
+ import { useDestinationSchemas } from '../../../hooks/api/use-config-options';
17
+ import { formatFieldLabel } from '../../../utils/formatters';
18
+ import { WizardStepContainer } from '../shared';
19
+ import { ConfigurationNameCard, SummaryCard, SummaryCardGrid, SummaryField } from '../../shared/wizard';
20
+ import { STEP_CONTENT, EXPORT_PLACEHOLDERS } from './constants';
21
+ import type { ExportConfiguration } from './types';
22
+ import type { QueryConfig, DestinationConfig } from '../../../types/wizard';
23
+
24
+ interface ReviewStepProps {
25
+ config: Partial<ExportConfiguration>;
26
+ updateConfig: (updates: Partial<ExportConfiguration>) => void;
27
+ errors?: Record<string, string>;
28
+ }
29
+
30
+ export function ReviewStep({ config, updateConfig, errors = {} }: ReviewStepProps) {
31
+ const selectedFieldsCount = config.fields?.filter(f => f.include).length ?? 0;
32
+
33
+ return (
34
+ <WizardStepContainer
35
+ title={STEP_CONTENT.review.title}
36
+ description={STEP_CONTENT.review.description}
37
+ >
38
+ <ConfigurationNameCard
39
+ title={STEP_CONTENT.review.cardTitle}
40
+ name={config.name ?? ''}
41
+ description={config.description ?? ''}
42
+ onNameChange={name => updateConfig({ name })}
43
+ onDescriptionChange={description => updateConfig({ description })}
44
+ namePlaceholder={EXPORT_PLACEHOLDERS.configName}
45
+ nameError={errors.name}
46
+ nameHelperText="A descriptive name to identify this export configuration"
47
+ />
48
+ <SummaryCards config={config} selectedFieldsCount={selectedFieldsCount} />
49
+ <DetailedConfigAccordion config={config} selectedFieldsCount={selectedFieldsCount} />
50
+ </WizardStepContainer>
51
+ );
52
+ }
53
+
54
+ interface SummaryCardsProps {
55
+ config: Partial<ExportConfiguration>;
56
+ selectedFieldsCount: number;
57
+ }
58
+
59
+ function SummaryCards({ config, selectedFieldsCount }: SummaryCardsProps) {
60
+ return (
61
+ <SummaryCardGrid columns={4}>
62
+ <SummaryCard
63
+ icon={Database}
64
+ label="Source"
65
+ value={VENDURE_ENTITY_LIST.find(e => e.code === config.sourceEntity)?.name}
66
+ />
67
+ <SummaryCard
68
+ icon={Columns}
69
+ label="Fields"
70
+ value={`${selectedFieldsCount} selected`}
71
+ />
72
+ <SummaryCard
73
+ icon={FileSpreadsheet}
74
+ label="Format"
75
+ value={<span className="uppercase">{config.format?.type}</span>}
76
+ />
77
+ <SummaryCard
78
+ icon={Send}
79
+ label="Destination"
80
+ value={<span className="capitalize">{config.destination?.type}</span>}
81
+ />
82
+ </SummaryCardGrid>
83
+ );
84
+ }
85
+
86
+ interface DetailedConfigAccordionProps {
87
+ config: Partial<ExportConfiguration>;
88
+ selectedFieldsCount: number;
89
+ }
90
+
91
+ function DetailedConfigAccordion({ config, selectedFieldsCount }: DetailedConfigAccordionProps) {
92
+ return (
93
+ <Accordion type="multiple" defaultValue={['source', 'fields']}>
94
+ <AccordionItem value="source">
95
+ <AccordionTrigger>Source Configuration</AccordionTrigger>
96
+ <AccordionContent>
97
+ <SourceQuerySummary sourceQuery={config.sourceQuery} />
98
+ </AccordionContent>
99
+ </AccordionItem>
100
+
101
+ <AccordionItem value="fields">
102
+ <AccordionTrigger>Selected Fields ({selectedFieldsCount})</AccordionTrigger>
103
+ <AccordionContent>
104
+ <div className="flex flex-wrap gap-2">
105
+ {config.fields?.filter(f => f.include).map(f => (
106
+ <Badge key={f.sourceField} variant="secondary">
107
+ {f.sourceField}
108
+ {f.outputName !== f.sourceField && ` -> ${f.outputName}`}
109
+ </Badge>
110
+ ))}
111
+ </div>
112
+ </AccordionContent>
113
+ </AccordionItem>
114
+
115
+ <AccordionItem value="destination">
116
+ <AccordionTrigger>Destination</AccordionTrigger>
117
+ <AccordionContent>
118
+ <DestinationSummary destination={config.destination} />
119
+ </AccordionContent>
120
+ </AccordionItem>
121
+ </Accordion>
122
+ );
123
+ }
124
+
125
+ function SourceQuerySummary({ sourceQuery }: { sourceQuery?: QueryConfig }) {
126
+ if (!sourceQuery) return <p className="text-sm text-muted-foreground">All records (default)</p>;
127
+
128
+ return (
129
+ <div className="grid grid-cols-2 gap-4 text-sm">
130
+ <SummaryField label="Query type" className="capitalize">{sourceQuery.type}</SummaryField>
131
+ {sourceQuery.limit != null && (
132
+ <SummaryField label="Limit">{sourceQuery.limit.toLocaleString()}</SummaryField>
133
+ )}
134
+ {sourceQuery.orderBy && (
135
+ <SummaryField label="Order by">{sourceQuery.orderBy} {sourceQuery.orderDirection ?? 'ASC'}</SummaryField>
136
+ )}
137
+ {sourceQuery.type === 'graphql' && sourceQuery.customQuery && (
138
+ <div className="col-span-2">
139
+ <span className="text-muted-foreground">Custom query:</span>
140
+ <pre className="mt-1 p-2 bg-muted rounded text-xs overflow-auto">{sourceQuery.customQuery}</pre>
141
+ </div>
142
+ )}
143
+ </div>
144
+ );
145
+ }
146
+
147
+ /**
148
+ * Formats a field value for display in the summary, based on its schema field type.
149
+ * Returns null to skip rendering the field.
150
+ */
151
+ function formatFieldValue(value: unknown, fieldType: string): React.ReactNode | null {
152
+ if (value == null || value === '') return null;
153
+
154
+ switch (fieldType) {
155
+ case 'boolean':
156
+ return value ? 'Yes' : 'No';
157
+ case 'secret':
158
+ return typeof value === 'string' && value.length > 0 ? 'Configured' : null;
159
+ case 'headers':
160
+ if (value && typeof value === 'object') {
161
+ const count = Object.keys(value).length;
162
+ return count > 0 ? `${count} custom` : null;
163
+ }
164
+ return null;
165
+ default:
166
+ return typeof value === 'object' ? JSON.stringify(value) : String(value);
167
+ }
168
+ }
169
+
170
+ function DestinationSummary({ destination }: { destination?: DestinationConfig }) {
171
+ const { schemas } = useDestinationSchemas();
172
+
173
+ if (!destination) return <p className="text-sm text-muted-foreground">No destination configured</p>;
174
+
175
+ const schema = schemas.find(s => s.type === destination.type);
176
+
177
+ // Static message (e.g. DOWNLOAD)
178
+ if (schema?.message) {
179
+ return (
180
+ <div className="grid grid-cols-2 gap-4 text-sm">
181
+ <SummaryField label="Type">{destination.type}</SummaryField>
182
+ <div className="col-span-2">
183
+ <span className="text-muted-foreground">{schema.message}</span>
184
+ </div>
185
+ </div>
186
+ );
187
+ }
188
+
189
+ // Schema-driven summary: read config sub-object and display schema fields
190
+ const configObj = schema?.configKey
191
+ ? (destination as Record<string, unknown>)[schema.configKey] as Record<string, unknown> | undefined
192
+ : undefined;
193
+
194
+ return (
195
+ <div className="grid grid-cols-2 gap-4 text-sm">
196
+ <SummaryField label="Type">{destination.type}</SummaryField>
197
+
198
+ {schema && configObj ? (
199
+ schema.fields.map(field => {
200
+ const displayValue = formatFieldValue(configObj[field.key], field.type);
201
+ if (displayValue == null) return null;
202
+ return (
203
+ <SummaryField key={field.key} label={field.label}>
204
+ {displayValue}
205
+ </SummaryField>
206
+ );
207
+ })
208
+ ) : (
209
+ renderGenericConfigFields(destination)
210
+ )}
211
+ </div>
212
+ );
213
+ }
214
+
215
+ /**
216
+ * Fallback renderer for unknown destination types. Finds the first config sub-object
217
+ * matching the `${type.toLowerCase()}Config` convention and displays its fields.
218
+ */
219
+ function renderGenericConfigFields(destination: DestinationConfig): React.ReactNode {
220
+ const configKey = `${destination.type.toLowerCase()}Config`;
221
+ const config = (destination as Record<string, unknown>)[configKey] as Record<string, unknown> | undefined;
222
+ if (!config) return null;
223
+
224
+ return Object.entries(config)
225
+ .filter(([, v]) => v != null && v !== '' && v !== false)
226
+ .map(([key, value]) => (
227
+ <SummaryField key={key} label={formatFieldLabel(key)}>
228
+ {typeof value === 'object' ? JSON.stringify(value) : String(value)}
229
+ </SummaryField>
230
+ ));
231
+ }
@@ -0,0 +1,154 @@
1
+ import { useMemo } from 'react';
2
+ import {
3
+ Card,
4
+ CardContent,
5
+ CardHeader,
6
+ CardTitle,
7
+ CardDescription,
8
+ Input,
9
+ Label,
10
+ Select,
11
+ SelectContent,
12
+ SelectItem,
13
+ SelectTrigger,
14
+ SelectValue,
15
+ } from '@vendure/dashboard';
16
+ import { VENDURE_ENTITY_SCHEMAS } from '../../../../shared';
17
+ import { QUERY_LIMITS } from '../../../constants';
18
+ import { useEntityFieldSchemas, useQueryTypeOptions } from '../../../hooks/api';
19
+ import { WizardStepContainer } from '../shared';
20
+ import { EntitySelector } from '../../shared/entity-selector';
21
+ import { FilterConditionsEditor } from '../../shared/filter-conditions-editor';
22
+ import { STEP_CONTENT } from './constants';
23
+ import type { ExportConfiguration, QueryType } from './types';
24
+
25
+ interface SourceStepProps {
26
+ config: Partial<ExportConfiguration>;
27
+ updateConfig: (updates: Partial<ExportConfiguration>) => void;
28
+ errors?: Record<string, string>;
29
+ }
30
+
31
+ export function SourceStep({ config, updateConfig, errors = {} }: SourceStepProps) {
32
+ const query = config.sourceQuery ?? { type: 'all' };
33
+
34
+ return (
35
+ <WizardStepContainer
36
+ title={STEP_CONTENT.source.title}
37
+ description={STEP_CONTENT.source.description}
38
+ >
39
+ <EntitySelector
40
+ value={config.sourceEntity}
41
+ onChange={(entityCode) => updateConfig({ sourceEntity: entityCode })}
42
+ />
43
+
44
+ {config.sourceEntity && (
45
+ <QueryConfiguration config={config} updateConfig={updateConfig} query={query} />
46
+ )}
47
+ </WizardStepContainer>
48
+ );
49
+ }
50
+
51
+ /**
52
+ * Resolve entity field names dynamically from the backend, falling back
53
+ * to the static VENDURE_ENTITY_SCHEMAS while the query is loading or
54
+ * if the entity is not known to the backend.
55
+ */
56
+ function useEntityFields(entityCode: string | undefined): string[] {
57
+ const { getFieldNames, isLoading } = useEntityFieldSchemas();
58
+
59
+ return useMemo(() => {
60
+ if (!entityCode) return [];
61
+ // Try dynamic fields from backend first
62
+ const dynamicFields = getFieldNames(entityCode);
63
+ if (dynamicFields.length > 0) return dynamicFields;
64
+ // Fall back to static schemas while loading or for unknown entities
65
+ const staticSchema = VENDURE_ENTITY_SCHEMAS[entityCode];
66
+ if (staticSchema) return Object.keys(staticSchema.fields);
67
+ return [];
68
+ }, [entityCode, getFieldNames]);
69
+ }
70
+
71
+ interface QueryConfigurationProps {
72
+ config: Partial<ExportConfiguration>;
73
+ updateConfig: (updates: Partial<ExportConfiguration>) => void;
74
+ query: NonNullable<ExportConfiguration['sourceQuery']>;
75
+ }
76
+
77
+ function QueryConfiguration({ config, updateConfig, query }: QueryConfigurationProps) {
78
+ const entityFields = useEntityFields(config.sourceEntity);
79
+ const { options: queryTypeOptions } = useQueryTypeOptions();
80
+
81
+ return (
82
+ <Card>
83
+ <CardHeader>
84
+ <CardTitle>Query Options</CardTitle>
85
+ <CardDescription>Configure how to fetch data</CardDescription>
86
+ </CardHeader>
87
+ <CardContent className="space-y-4">
88
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
89
+ <div>
90
+ <Label>Query Type</Label>
91
+ <Select
92
+ value={query.type}
93
+ onValueChange={type => updateConfig({
94
+ sourceQuery: { ...query, type: type as QueryType },
95
+ })}
96
+ >
97
+ <SelectTrigger>
98
+ <SelectValue />
99
+ </SelectTrigger>
100
+ <SelectContent>
101
+ {queryTypeOptions.map(option => (
102
+ <SelectItem key={option.value} value={option.value}>{option.label}</SelectItem>
103
+ ))}
104
+ </SelectContent>
105
+ </Select>
106
+ </div>
107
+
108
+ <div>
109
+ <Label>Limit</Label>
110
+ <Input
111
+ type="number"
112
+ value={query.limit ?? QUERY_LIMITS.EXPORT_DEFAULT}
113
+ onChange={e => updateConfig({
114
+ sourceQuery: { ...query, limit: parseInt(e.target.value) || QUERY_LIMITS.EXPORT_DEFAULT },
115
+ })}
116
+ />
117
+ </div>
118
+
119
+ <div>
120
+ <Label>Order By</Label>
121
+ <Select
122
+ value={query.orderBy ?? 'id'}
123
+ onValueChange={orderBy => updateConfig({
124
+ sourceQuery: { ...query, orderBy },
125
+ })}
126
+ >
127
+ <SelectTrigger>
128
+ <SelectValue />
129
+ </SelectTrigger>
130
+ <SelectContent>
131
+ {entityFields.map(field => (
132
+ <SelectItem key={field} value={field}>{field}</SelectItem>
133
+ ))}
134
+ </SelectContent>
135
+ </Select>
136
+ </div>
137
+ </div>
138
+
139
+ {query.type === 'query' && config.sourceEntity && (
140
+ <FilterConditionsEditor
141
+ conditions={config.filters ?? []}
142
+ onChange={(filters) => updateConfig({ filters })}
143
+ fields={entityFields}
144
+ showLogicSelector={false}
145
+ fieldPlaceholder="Select field..."
146
+ valuePlaceholder="Value"
147
+ emptyMessage="No filters - all records will be exported"
148
+ addLabel="Add Filter"
149
+ />
150
+ )}
151
+ </CardContent>
152
+ </Card>
153
+ );
154
+ }
@@ -0,0 +1,234 @@
1
+ import {
2
+ Card,
3
+ CardContent,
4
+ CardHeader,
5
+ CardTitle,
6
+ CardDescription,
7
+ Input,
8
+ Label,
9
+ Select,
10
+ SelectContent,
11
+ SelectItem,
12
+ SelectTrigger,
13
+ SelectValue,
14
+ Switch,
15
+ } from '@vendure/dashboard';
16
+ import { TRIGGER_TYPE, UI_DEFAULTS, COMPRESSION_TYPE, LOADING_STATE_TYPE } from '../../../constants';
17
+ import { useOptionValues } from '../../../hooks/api/use-config-options';
18
+ import { useTriggerTypes } from '../../../hooks';
19
+ import { WizardStepContainer } from '../shared';
20
+ import { TriggerSelector, TriggerSchemaFields } from '../../shared/wizard-trigger';
21
+ import { LoadingState } from '../../shared/feedback';
22
+ import { STEP_CONTENT, DEFAULT_EXPORT_OPTIONS } from './constants';
23
+ import type { ExportConfiguration, ExportTriggerType, CompressionType } from './types';
24
+
25
+ interface TriggerStepProps {
26
+ config: Partial<ExportConfiguration>;
27
+ updateConfig: (updates: Partial<ExportConfiguration>) => void;
28
+ errors?: Record<string, string>;
29
+ }
30
+
31
+ export function TriggerStep({ config, updateConfig, errors = {} }: TriggerStepProps) {
32
+ const trigger = config.trigger ?? { type: TRIGGER_TYPE.MANUAL };
33
+ const options = config.options ?? { ...DEFAULT_EXPORT_OPTIONS };
34
+
35
+ const handleTriggerTypeChange = (type: string) => {
36
+ updateConfig({ trigger: { ...trigger, type: type as ExportTriggerType } });
37
+ };
38
+
39
+ return (
40
+ <WizardStepContainer
41
+ title={STEP_CONTENT.trigger.title}
42
+ description={STEP_CONTENT.trigger.description}
43
+ >
44
+ <TriggerCard
45
+ trigger={trigger}
46
+ updateConfig={updateConfig}
47
+ onTriggerTypeChange={handleTriggerTypeChange}
48
+ />
49
+ <ExportOptionsCard options={options} updateConfig={updateConfig} />
50
+ <CachingCard config={config} updateConfig={updateConfig} />
51
+ </WizardStepContainer>
52
+ );
53
+ }
54
+
55
+ interface TriggerCardProps {
56
+ trigger: ExportConfiguration['trigger'];
57
+ updateConfig: (updates: Partial<ExportConfiguration>) => void;
58
+ onTriggerTypeChange: (type: string) => void;
59
+ }
60
+
61
+ function TriggerCard({ trigger, updateConfig, onTriggerTypeChange }: TriggerCardProps) {
62
+ const { exportWizardTriggers, triggerSchemas, isLoading } = useTriggerTypes();
63
+ const currentSchema = triggerSchemas.find(s => s.value === trigger.type);
64
+
65
+ const handleFieldChange = (key: string, value: unknown) => {
66
+ updateConfig({ trigger: { ...trigger, [key]: value } });
67
+ };
68
+
69
+ return (
70
+ <Card>
71
+ <CardHeader>
72
+ <CardTitle>Trigger</CardTitle>
73
+ </CardHeader>
74
+ <CardContent className="space-y-4">
75
+ <TriggerSelector
76
+ options={exportWizardTriggers}
77
+ value={trigger.type}
78
+ onChange={onTriggerTypeChange}
79
+ />
80
+
81
+ {currentSchema && currentSchema.fields.length > 0 ? (
82
+ <div className="pt-4 border-t">
83
+ <TriggerSchemaFields
84
+ fields={currentSchema.fields}
85
+ values={trigger as Record<string, unknown>}
86
+ onChange={handleFieldChange}
87
+ />
88
+ </div>
89
+ ) : isLoading && trigger.type !== TRIGGER_TYPE.MANUAL ? (
90
+ <div className="pt-4 border-t">
91
+ <LoadingState type={LOADING_STATE_TYPE.FORM} rows={2} message="" />
92
+ </div>
93
+ ) : null}
94
+ </CardContent>
95
+ </Card>
96
+ );
97
+ }
98
+
99
+ interface ExportOptionsCardProps {
100
+ options: ExportConfiguration['options'];
101
+ updateConfig: (updates: Partial<ExportConfiguration>) => void;
102
+ }
103
+
104
+ function ExportOptionsCard({ options, updateConfig }: ExportOptionsCardProps) {
105
+ const { options: compressionOptions } = useOptionValues('compressionTypes');
106
+ return (
107
+ <Card>
108
+ <CardHeader>
109
+ <CardTitle>Export Options</CardTitle>
110
+ </CardHeader>
111
+ <CardContent className="space-y-4">
112
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
113
+ <div>
114
+ <Label>Batch Size</Label>
115
+ <Input
116
+ type="number"
117
+ value={options.batchSize}
118
+ onChange={e => updateConfig({
119
+ options: { ...options, batchSize: parseInt(e.target.value) || UI_DEFAULTS.EXPORT_BATCH_SIZE },
120
+ })}
121
+ />
122
+ </div>
123
+
124
+ <div>
125
+ <Label>Compression</Label>
126
+ <Select
127
+ value={options.compression ?? COMPRESSION_TYPE.NONE}
128
+ onValueChange={compression => updateConfig({
129
+ options: { ...options, compression: compression as CompressionType },
130
+ })}
131
+ >
132
+ <SelectTrigger>
133
+ <SelectValue />
134
+ </SelectTrigger>
135
+ <SelectContent>
136
+ {compressionOptions.map(option => (
137
+ <SelectItem key={option.value} value={option.value}>
138
+ {option.label}
139
+ </SelectItem>
140
+ ))}
141
+ </SelectContent>
142
+ </Select>
143
+ </div>
144
+
145
+ <div>
146
+ <Label>Max Retries</Label>
147
+ <Input
148
+ type="number"
149
+ value={options.maxRetries}
150
+ onChange={e => updateConfig({
151
+ options: { ...options, maxRetries: parseInt(e.target.value) || UI_DEFAULTS.DEFAULT_MAX_RETRIES },
152
+ })}
153
+ />
154
+ </div>
155
+ </div>
156
+
157
+ <div className="flex flex-wrap gap-6">
158
+ <div className="flex items-center gap-2">
159
+ <Switch
160
+ id="include-metadata"
161
+ checked={options.includeMetadata}
162
+ onCheckedChange={includeMetadata => updateConfig({
163
+ options: { ...options, includeMetadata },
164
+ })}
165
+ />
166
+ <Label htmlFor="include-metadata">Include metadata</Label>
167
+ </div>
168
+
169
+ <div className="flex items-center gap-2">
170
+ <Switch
171
+ id="notify-on-complete"
172
+ checked={options.notifyOnComplete}
173
+ onCheckedChange={notifyOnComplete => updateConfig({
174
+ options: { ...options, notifyOnComplete },
175
+ })}
176
+ />
177
+ <Label htmlFor="notify-on-complete">Notify on complete</Label>
178
+ </div>
179
+
180
+ <div className="flex items-center gap-2">
181
+ <Switch
182
+ id="retry-on-failure"
183
+ checked={options.retryOnFailure}
184
+ onCheckedChange={retryOnFailure => updateConfig({
185
+ options: { ...options, retryOnFailure },
186
+ })}
187
+ />
188
+ <Label htmlFor="retry-on-failure">Retry on failure</Label>
189
+ </div>
190
+ </div>
191
+ </CardContent>
192
+ </Card>
193
+ );
194
+ }
195
+
196
+ interface CachingCardProps {
197
+ config: Partial<ExportConfiguration>;
198
+ updateConfig: (updates: Partial<ExportConfiguration>) => void;
199
+ }
200
+
201
+ function CachingCard({ config, updateConfig }: CachingCardProps) {
202
+ return (
203
+ <Card>
204
+ <CardHeader>
205
+ <CardTitle>Caching</CardTitle>
206
+ <CardDescription>Cache export results for faster delivery</CardDescription>
207
+ </CardHeader>
208
+ <CardContent className="space-y-4">
209
+ <div className="flex items-center gap-3">
210
+ <Switch
211
+ checked={config.caching?.enabled ?? false}
212
+ onCheckedChange={enabled => updateConfig({
213
+ caching: { ...config.caching, enabled, ttl: config.caching?.ttl ?? UI_DEFAULTS.DEFAULT_CACHE_TTL_SECONDS },
214
+ })}
215
+ />
216
+ <Label>Enable caching</Label>
217
+ </div>
218
+
219
+ {config.caching?.enabled && (
220
+ <div>
221
+ <Label>Cache TTL (seconds)</Label>
222
+ <Input
223
+ type="number"
224
+ value={config.caching.ttl}
225
+ onChange={e => updateConfig({
226
+ caching: { ...config.caching!, ttl: parseInt(e.target.value) || UI_DEFAULTS.DEFAULT_CACHE_TTL_SECONDS },
227
+ })}
228
+ />
229
+ </div>
230
+ )}
231
+ </CardContent>
232
+ </Card>
233
+ );
234
+ }
@@ -0,0 +1,73 @@
1
+ import {
2
+ Database,
3
+ Columns,
4
+ FileSpreadsheet,
5
+ Send,
6
+ Clock,
7
+ Check,
8
+ } from 'lucide-react';
9
+ import type { WizardStep, ExportOptions } from '../../../types/wizard';
10
+ import { BATCH_SIZES, UI_DEFAULTS, COMPRESSION_TYPE } from '../../../constants';
11
+
12
+ export const EXPORT_STEP_ID = {
13
+ SOURCE: 'source',
14
+ FIELDS: 'fields',
15
+ FORMAT: 'format',
16
+ DESTINATION: 'destination',
17
+ TRIGGER: 'trigger',
18
+ REVIEW: 'review',
19
+ } as const;
20
+
21
+ export const WIZARD_STEPS: WizardStep[] = [
22
+ { id: 'source', label: 'Data Source', icon: Database },
23
+ { id: 'fields', label: 'Select Fields', icon: Columns },
24
+ { id: 'format', label: 'Output Format', icon: FileSpreadsheet },
25
+ { id: 'destination', label: 'Destination', icon: Send },
26
+ { id: 'trigger', label: 'Schedule', icon: Clock },
27
+ { id: 'review', label: 'Review', icon: Check },
28
+ ];
29
+
30
+ export const STEP_CONTENT = {
31
+ source: {
32
+ title: 'Select Data Source',
33
+ description: 'Choose which Vendure entity to export',
34
+ },
35
+ fields: {
36
+ title: 'Select Fields',
37
+ description: 'Choose which fields to include in the export',
38
+ },
39
+ format: {
40
+ title: 'Output Format',
41
+ description: 'Choose the output format and configure options',
42
+ },
43
+ destination: {
44
+ title: 'Destination',
45
+ description: 'Choose where to deliver the exported data',
46
+ },
47
+ trigger: {
48
+ title: 'Schedule & Options',
49
+ description: 'Configure when to run the export and additional options',
50
+ },
51
+ review: {
52
+ title: 'Review & Create',
53
+ description: 'Review your export configuration before creating',
54
+ cardTitle: 'Export Configuration',
55
+ },
56
+ } as const;
57
+
58
+ export const EXPORT_PLACEHOLDERS = {
59
+ configName: 'My Product Export',
60
+ filename: 'export-{date}.csv',
61
+ sftpHost: 'sftp.example.com',
62
+ remotePath: '/uploads/feeds',
63
+ httpUrl: 'https://api.example.com/import',
64
+ } as const;
65
+
66
+ export const DEFAULT_EXPORT_OPTIONS: ExportOptions = {
67
+ batchSize: BATCH_SIZES.EXPORT_DEFAULT,
68
+ includeMetadata: false,
69
+ compression: COMPRESSION_TYPE.NONE,
70
+ notifyOnComplete: true,
71
+ retryOnFailure: true,
72
+ maxRetries: UI_DEFAULTS.DEFAULT_MAX_RETRIES,
73
+ };