@oronts/vendure-data-hub-plugin 0.1.0 → 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 +10 -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 +4 -4
@@ -0,0 +1,125 @@
1
+ import React, { useCallback } from 'react';
2
+ import { Plus, Trash2, AlertTriangle } from 'lucide-react';
3
+ import { Button, Input, Label } from '@vendure/dashboard';
4
+ import { ROUTE_BRANCH_DEFAULTS, ERROR_MESSAGES } from '../../../constants';
5
+ import { useStableKeys } from '../../../hooks';
6
+
7
+ export interface RouteConfigComponentProps {
8
+ readonly config: Record<string, unknown>;
9
+ readonly onChange: (config: Record<string, unknown>) => void;
10
+ readonly showDuplicateWarning?: boolean;
11
+ }
12
+
13
+ interface Branch {
14
+ name: string;
15
+ conditions?: unknown[];
16
+ }
17
+
18
+ export function RouteConfigComponent({
19
+ config,
20
+ onChange,
21
+ showDuplicateWarning = true,
22
+ }: RouteConfigComponentProps) {
23
+ const branches = (config.branches as Branch[]) ?? [];
24
+ const branchKeys = useStableKeys(branches, 'branch');
25
+
26
+ const getDuplicateBranches = React.useCallback((branchList: Branch[]) => {
27
+ const names = branchList.map((b) => b.name.trim().toLowerCase());
28
+ const duplicates = new Set<string>();
29
+ const seen = new Set<string>();
30
+ for (const name of names) {
31
+ if (name && seen.has(name)) {
32
+ duplicates.add(name);
33
+ }
34
+ seen.add(name);
35
+ }
36
+ return duplicates;
37
+ }, []);
38
+
39
+ const duplicates = getDuplicateBranches(branches);
40
+ const hasDuplicates = duplicates.size > 0;
41
+
42
+ const addBranch = useCallback(() => {
43
+ let branchNum = branches.length + 1;
44
+ let newName = `${ROUTE_BRANCH_DEFAULTS.namePrefix}${branchNum}`;
45
+ const existingNames = new Set(branches.map((b) => b.name.toLowerCase()));
46
+ while (existingNames.has(newName.toLowerCase())) {
47
+ branchNum++;
48
+ newName = `${ROUTE_BRANCH_DEFAULTS.namePrefix}${branchNum}`;
49
+ }
50
+
51
+ onChange({
52
+ ...config,
53
+ branches: [...branches, { name: newName, conditions: [] }],
54
+ });
55
+ }, [branches, config, onChange]);
56
+
57
+ const updateBranch = useCallback((index: number, patch: Record<string, unknown>) => {
58
+ const newBranches = [...branches];
59
+ newBranches[index] = { ...newBranches[index], ...patch };
60
+ onChange({ ...config, branches: newBranches });
61
+ }, [branches, config, onChange]);
62
+
63
+ const removeBranch = useCallback((index: number) => {
64
+ onChange({ ...config, branches: branches.filter((_, i) => i !== index) });
65
+ }, [branches, config, onChange]);
66
+
67
+ const isBranchDuplicate = useCallback((branchName: string) => {
68
+ return duplicates.has(branchName.trim().toLowerCase());
69
+ }, [duplicates]);
70
+
71
+ return (
72
+ <div className="space-y-3">
73
+ <div className="flex items-center justify-between">
74
+ <Label className="text-sm font-medium">Routing Branches</Label>
75
+ <Button variant="outline" size="sm" onClick={addBranch}>
76
+ <Plus className="h-3 w-3 mr-1" />
77
+ Add Branch
78
+ </Button>
79
+ </div>
80
+
81
+ {showDuplicateWarning && hasDuplicates && (
82
+ <div className="p-3 bg-amber-50 dark:bg-amber-950/30 border border-amber-200 dark:border-amber-800 rounded-md">
83
+ <div className="flex items-center gap-2 text-amber-800 dark:text-amber-400">
84
+ <AlertTriangle className="h-4 w-4" />
85
+ <span className="text-sm font-medium">Duplicate branch names detected</span>
86
+ </div>
87
+ <p className="text-xs text-amber-700 mt-1">
88
+ {ERROR_MESSAGES.DUPLICATE_BRANCH_NAMES}. Duplicate: {Array.from(duplicates).join(', ')}
89
+ </p>
90
+ </div>
91
+ )}
92
+
93
+ {branches.map((branch, i) => (
94
+ <div key={branchKeys[i]} className="flex items-center gap-2 p-2 border rounded">
95
+ <div className="flex-1">
96
+ <Input
97
+ value={branch.name}
98
+ onChange={(e) => updateBranch(i, { name: e.target.value })}
99
+ placeholder="Branch name"
100
+ className={isBranchDuplicate(branch.name) ? 'border-amber-300 focus:border-amber-500' : ''}
101
+ />
102
+ {!branch.name.trim() && (
103
+ <p className="text-xs text-destructive mt-1">{ERROR_MESSAGES.BRANCH_NAME_EMPTY}</p>
104
+ )}
105
+ </div>
106
+ <Button
107
+ variant="ghost"
108
+ size="sm"
109
+ onClick={() => removeBranch(i)}
110
+ className="text-destructive"
111
+ aria-label={`Remove branch ${branch.name || i + 1}`}
112
+ >
113
+ <Trash2 className="h-4 w-4" />
114
+ </Button>
115
+ </div>
116
+ ))}
117
+
118
+ {branches.length === 0 && (
119
+ <p className="text-sm text-muted-foreground">
120
+ Add branches to route records based on conditions.
121
+ </p>
122
+ )}
123
+ </div>
124
+ );
125
+ }
@@ -0,0 +1,564 @@
1
+ import * as React from 'react';
2
+ import { useMemo, useCallback } from 'react';
3
+ import {
4
+ Button,
5
+ Input,
6
+ Card,
7
+ CardContent,
8
+ CardHeader,
9
+ CardTitle,
10
+ Label,
11
+ Separator,
12
+ } from '@vendure/dashboard';
13
+ import { Trash2, Settings2 } from 'lucide-react';
14
+
15
+ import { SchemaFormRenderer } from '../schema-form';
16
+ import {
17
+ AdapterSelector,
18
+ AdapterRequiredWarning,
19
+ ValidateConfigComponent,
20
+ RouteConfigComponent,
21
+ EnrichConfigComponent,
22
+ GateConfigComponent,
23
+ } from './index';
24
+ import { TriggerForm } from '../trigger-config';
25
+ import { OperatorCheatSheetButton } from './OperatorCheatSheetButton';
26
+ import {
27
+ AdvancedMapEditor,
28
+ AdvancedTemplateEditor,
29
+ AdvancedWhenEditor,
30
+ MultiOperatorEditor,
31
+ } from './AdvancedEditors';
32
+ import { StepTester } from './StepTester';
33
+ import { RetrySettingsComponent } from './RetrySettingsComponent';
34
+ import type { RetrySettings } from './RetrySettingsComponent';
35
+ import { useAdapterCatalog, useStepConfigs, AdapterMetadata } from '../../../hooks';
36
+ import { getAdapterTypeLabel, prepareDynamicFields, normalizeStepType, getAdapterTypeForStep } from '../../../utils';
37
+ import {
38
+ STEP_TYPE,
39
+ ADAPTER_TYPES,
40
+ NODE_CATEGORIES,
41
+ PANEL_VARIANT,
42
+ FALLBACK_COLORS,
43
+ } from '../../../constants';
44
+ import type {
45
+ StepType,
46
+ PipelineStepDefinition,
47
+ PipelineTrigger,
48
+ TriggerType,
49
+ AdapterSchemaField,
50
+ } from '../../../types';
51
+
52
+ /** Common props shared by all special config editors (Route, Validate, Enrich, Gate). */
53
+ interface SpecialConfigProps {
54
+ readonly config: Record<string, unknown>;
55
+ readonly onChange: (config: Record<string, unknown>) => void;
56
+ readonly showErrorHandling?: boolean;
57
+ readonly showValidationMode?: boolean;
58
+ readonly showRulesEditor?: boolean;
59
+ readonly showDuplicateWarning?: boolean;
60
+ }
61
+
62
+ /**
63
+ * Registry mapping step types to their dedicated config editor component.
64
+ * Used both as a guard (`stepType in SPECIAL_CONFIG_EDITORS`) and for
65
+ * table-driven dispatch in `renderSpecialConfigs`.
66
+ */
67
+ const SPECIAL_CONFIG_EDITORS: Record<string, React.ComponentType<SpecialConfigProps>> = {
68
+ [STEP_TYPE.ROUTE]: RouteConfigComponent as React.ComponentType<SpecialConfigProps>,
69
+ [STEP_TYPE.VALIDATE]: ValidateConfigComponent as React.ComponentType<SpecialConfigProps>,
70
+ [STEP_TYPE.ENRICH]: EnrichConfigComponent as React.ComponentType<SpecialConfigProps>,
71
+ [STEP_TYPE.GATE]: GateConfigComponent as React.ComponentType<SpecialConfigProps>,
72
+ };
73
+
74
+ /** Props shared by all advanced operator editors (map, template, filter). */
75
+ interface AdvancedEditorProps {
76
+ readonly config: Record<string, unknown>;
77
+ readonly onChange: (values: Record<string, unknown>) => void;
78
+ }
79
+
80
+ /**
81
+ * Registry mapping operator editor types to their advanced editor component.
82
+ * Keyed by the canonical lowercase adapter code.
83
+ */
84
+ const ADVANCED_EDITORS: Record<string, React.ComponentType<AdvancedEditorProps>> = {
85
+ map: AdvancedMapEditor,
86
+ template: AdvancedTemplateEditor,
87
+ filter: AdvancedWhenEditor,
88
+ };
89
+
90
+ export interface StepConfigData {
91
+ key: string;
92
+ type: StepType | string;
93
+ config: Record<string, unknown>;
94
+ adapterCode?: string;
95
+ }
96
+
97
+ export interface StepConfigPanelProps {
98
+ data: StepConfigData;
99
+ onChange: (data: StepConfigData) => void;
100
+ onDelete?: () => void;
101
+ catalog?: AdapterMetadata[];
102
+ connectionCodes?: string[];
103
+ secretOptions?: Array<{ code: string; provider?: string }>;
104
+ variant?: 'panel' | 'inline';
105
+ showKeyInput?: boolean;
106
+ showHeader?: boolean;
107
+ showDeleteButton?: boolean;
108
+ showCheatSheet?: boolean;
109
+ showStepTester?: boolean;
110
+ showAdvancedEditors?: boolean;
111
+ compact?: boolean;
112
+ }
113
+
114
+ export function StepConfigPanel({
115
+ data,
116
+ onChange,
117
+ onDelete,
118
+ catalog: externalCatalog,
119
+ connectionCodes: externalConnectionCodes,
120
+ secretOptions: externalSecretOptions,
121
+ variant = 'inline',
122
+ showKeyInput = true,
123
+ showHeader = true,
124
+ showDeleteButton = true,
125
+ showCheatSheet = true,
126
+ showStepTester = true,
127
+ showAdvancedEditors = true,
128
+ compact = false,
129
+ }: StepConfigPanelProps) {
130
+ const hookResult = useAdapterCatalog();
131
+ const { getStepConfig } = useStepConfigs();
132
+ const catalog = externalCatalog ?? hookResult.adapters;
133
+ const connectionCodes = externalConnectionCodes ?? hookResult.connectionCodes;
134
+ const secretOptions = externalSecretOptions ?? hookResult.secretOptions;
135
+ const isLoadingCatalog = !externalCatalog && hookResult.isLoading;
136
+
137
+ const stepType = normalizeStepType(data.type);
138
+ const config = getStepConfig(stepType);
139
+ const adapterType = getAdapterTypeForStep(data.type);
140
+
141
+ const availableAdapters = useMemo(
142
+ () => (adapterType ? catalog.filter((a) => a.type === adapterType) : []),
143
+ [catalog, adapterType]
144
+ );
145
+
146
+ const adapterCode = data.adapterCode ?? (data.config?.adapterCode as string);
147
+
148
+ const selectedAdapter = useMemo(
149
+ () => availableAdapters.find((a) => a.code === adapterCode),
150
+ [availableAdapters, adapterCode]
151
+ );
152
+
153
+ const hasMultiOperatorConfig = useMemo(
154
+ () =>
155
+ stepType === STEP_TYPE.TRANSFORM &&
156
+ Array.isArray(data.config?.operators) &&
157
+ data.config.operators.length > 0,
158
+ [stepType, data.config?.operators]
159
+ );
160
+
161
+ const needsAdapterSelection = useMemo(
162
+ () => !adapterCode && !hasMultiOperatorConfig && stepType !== STEP_TYPE.TRIGGER,
163
+ [adapterCode, hasMultiOperatorConfig, stepType]
164
+ );
165
+
166
+ const dynamicFields = React.useMemo<AdapterSchemaField[]>(
167
+ () =>
168
+ prepareDynamicFields({
169
+ baseFields: selectedAdapter?.schema?.fields ?? [],
170
+ connectionCodes,
171
+ }),
172
+ [selectedAdapter, connectionCodes]
173
+ );
174
+
175
+ const updateKey = useCallback((key: string) => {
176
+ onChange({ ...data, key });
177
+ }, [onChange, data]);
178
+
179
+ const updateConfig = useCallback((key: string, value: unknown) => {
180
+ onChange({
181
+ ...data,
182
+ config: { ...data.config, [key]: value },
183
+ });
184
+ }, [onChange, data]);
185
+
186
+ const updateConfigBatch = useCallback((values: Record<string, unknown>) => {
187
+ onChange({
188
+ ...data,
189
+ config: { ...data.config, ...values },
190
+ });
191
+ }, [onChange, data]);
192
+
193
+ const updateAdapterCode = useCallback((code: string) => {
194
+ onChange({
195
+ ...data,
196
+ adapterCode: code,
197
+ config: { ...data.config, adapterCode: code },
198
+ });
199
+ }, [onChange, data]);
200
+
201
+ const updateOperators = useCallback((operators: Array<{ op: string; args?: Record<string, unknown> }>) => {
202
+ onChange({
203
+ ...data,
204
+ config: { ...data.config, operators },
205
+ });
206
+ }, [onChange, data]);
207
+
208
+ const handleKeyChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
209
+ updateKey(e.target.value);
210
+ }, [updateKey]);
211
+
212
+ const handleAdapterCodeChange = useCallback((code: string) => {
213
+ if (code !== adapterCode) {
214
+ updateAdapterCode(code);
215
+ }
216
+ }, [adapterCode, updateAdapterCode]);
217
+
218
+ const handleResetDefaults = useCallback(() => {
219
+ const defaults: Record<string, unknown> = {};
220
+ for (const f of dynamicFields) {
221
+ if (f.defaultValue !== undefined) {
222
+ defaults[f.key] = f.defaultValue;
223
+ }
224
+ }
225
+ updateConfigBatch(defaults);
226
+ }, [dynamicFields, updateConfigBatch]);
227
+
228
+ const handleTriggerChange = useCallback((trigger: PipelineTrigger) => {
229
+ updateConfigBatch({
230
+ type: trigger.type,
231
+ enabled: trigger.enabled,
232
+ cron: trigger.cron,
233
+ timezone: trigger.timezone,
234
+ webhookCode: trigger.webhookCode,
235
+ authentication: trigger.authentication,
236
+ secretCode: trigger.secretCode,
237
+ event: trigger.eventType,
238
+ eventType: trigger.eventType,
239
+ });
240
+ }, [updateConfigBatch]);
241
+
242
+ const triggerValue = useMemo(() => ({
243
+ type: (data.config?.type as TriggerType) || 'MANUAL',
244
+ enabled: data.config?.enabled !== false,
245
+ cron: data.config?.cron as string,
246
+ timezone: data.config?.timezone as string,
247
+ webhookCode: data.config?.webhookCode as string,
248
+ authentication: data.config?.authentication as PipelineTrigger['authentication'],
249
+ secretCode: data.config?.secretCode as string,
250
+ eventType: (data.config?.event as string) || (data.config?.eventType as string),
251
+ }), [data.config?.type, data.config?.enabled, data.config?.cron, data.config?.timezone, data.config?.webhookCode, data.config?.authentication, data.config?.secretCode, data.config?.event, data.config?.eventType]);
252
+
253
+ const renderHeader = () => {
254
+ if (!showHeader) return null;
255
+
256
+ return (
257
+ <div
258
+ className={compact ? 'p-3 rounded-lg mb-3' : 'p-4 rounded-lg mb-4'}
259
+ style={{ backgroundColor: config?.bgColor ?? FALLBACK_COLORS.UNKNOWN_STEP_BG }}
260
+ >
261
+ <div className="flex items-center justify-between">
262
+ <div className="flex items-center gap-3">
263
+ {selectedAdapter ? (
264
+ <div
265
+ className="w-8 h-8 rounded-full flex items-center justify-center text-white"
266
+ style={{ backgroundColor: selectedAdapter.color }}
267
+ >
268
+ <selectedAdapter.icon className="h-4 w-4" />
269
+ </div>
270
+ ) : (
271
+ <div
272
+ className="w-8 h-8 rounded-full flex items-center justify-center text-white"
273
+ style={{ backgroundColor: config?.color ?? FALLBACK_COLORS.UNKNOWN_STEP_COLOR }}
274
+ >
275
+ {stepType.charAt(0)}
276
+ </div>
277
+ )}
278
+ <div>
279
+ <h3
280
+ className={compact ? 'font-medium text-sm' : 'font-semibold'}
281
+ style={{ color: config?.color }}
282
+ >
283
+ {config?.label ?? stepType} Step
284
+ </h3>
285
+ {!compact && (
286
+ <p className="text-sm text-muted-foreground">{config?.description}</p>
287
+ )}
288
+ </div>
289
+ </div>
290
+ <div className="flex items-center gap-1">
291
+ {showCheatSheet && adapterType === ADAPTER_TYPES.OPERATOR && (
292
+ <OperatorCheatSheetButton label={compact ? undefined : 'Help'} />
293
+ )}
294
+ {showDeleteButton && onDelete && (
295
+ <Button
296
+ variant="ghost"
297
+ size="sm"
298
+ className="text-destructive hover:text-destructive"
299
+ onClick={onDelete}
300
+ aria-label="Remove step"
301
+ >
302
+ <Trash2 className="h-4 w-4" />
303
+ </Button>
304
+ )}
305
+ </div>
306
+ </div>
307
+ </div>
308
+ );
309
+ };
310
+
311
+ const renderKeyInput = () => {
312
+ if (!showKeyInput) return null;
313
+
314
+ return (
315
+ <div className={compact ? 'space-y-1' : 'space-y-2'}>
316
+ <Label className={compact ? 'text-xs' : 'text-sm'}>
317
+ {variant === PANEL_VARIANT.PANEL ? 'Node Label' : 'Step Key'}
318
+ </Label>
319
+ <Input
320
+ value={data.key}
321
+ onChange={handleKeyChange}
322
+ placeholder="unique-step-key"
323
+ className={compact ? 'h-8 font-mono' : 'font-mono'}
324
+ />
325
+ {!compact && (
326
+ <p className="text-xs text-muted-foreground">
327
+ Unique identifier for this step in the pipeline
328
+ </p>
329
+ )}
330
+ </div>
331
+ );
332
+ };
333
+
334
+ const renderTriggerConfig = () => {
335
+ if (stepType !== STEP_TYPE.TRIGGER) return null;
336
+
337
+ return (
338
+ <TriggerForm
339
+ trigger={triggerValue}
340
+ onChange={handleTriggerChange}
341
+ compact={compact}
342
+ />
343
+ );
344
+ };
345
+
346
+ const renderAdapterSelection = () => {
347
+ if (!adapterType || stepType === STEP_TYPE.TRIGGER) return null;
348
+
349
+ if (stepType === STEP_TYPE.TRANSFORM) {
350
+ return (
351
+ <MultiOperatorEditor
352
+ operators={
353
+ Array.isArray(data.config?.operators) ? data.config.operators : []
354
+ }
355
+ availableOperators={availableAdapters.map((a) => ({
356
+ code: a.code,
357
+ name: a.name,
358
+ description: a.description,
359
+ schema: a.schema,
360
+ }))}
361
+ onChange={updateOperators}
362
+ />
363
+ );
364
+ }
365
+
366
+ // Steps with built-in config editors don't need the adapter selector empty state
367
+ const hasBuiltInConfig = stepType in SPECIAL_CONFIG_EDITORS;
368
+ if (hasBuiltInConfig && availableAdapters.length === 0) return null;
369
+
370
+ return (
371
+ <div className={compact ? 'space-y-2' : 'space-y-3'}>
372
+ <div className="flex items-center justify-between">
373
+ <Label className={compact ? 'text-xs' : 'text-sm'}>
374
+ {getAdapterTypeLabel(adapterType)}
375
+ </Label>
376
+ {showCheatSheet && adapterType === ADAPTER_TYPES.OPERATOR && !showHeader && (
377
+ <OperatorCheatSheetButton />
378
+ )}
379
+ </div>
380
+
381
+ {availableAdapters.length === 0 && isLoadingCatalog && (
382
+ <div className="p-3 bg-muted rounded-md">
383
+ <p className="text-sm text-muted-foreground">
384
+ Loading {getAdapterTypeLabel(adapterType).toLowerCase()}s...
385
+ </p>
386
+ </div>
387
+ )}
388
+
389
+ {availableAdapters.length > 0 && needsAdapterSelection && (
390
+ <AdapterRequiredWarning
391
+ adapterTypeLabel={getAdapterTypeLabel(adapterType).toLowerCase()}
392
+ compact={compact}
393
+ />
394
+ )}
395
+
396
+ {availableAdapters.length > 0 && (
397
+ <AdapterSelector
398
+ stepType={stepType}
399
+ value={adapterCode}
400
+ onChange={handleAdapterCodeChange}
401
+ placeholder={`Select ${getAdapterTypeLabel(adapterType).toLowerCase()}...`}
402
+ adapters={availableAdapters}
403
+ />
404
+ )}
405
+
406
+ {selectedAdapter && (
407
+ <div className="flex items-center gap-2 p-2 bg-muted/50 rounded border">
408
+ <div
409
+ className="w-7 h-7 rounded flex items-center justify-center text-white shrink-0"
410
+ style={{ backgroundColor: selectedAdapter.color }}
411
+ >
412
+ <selectedAdapter.icon className="w-3.5 h-3.5" />
413
+ </div>
414
+ <div className="min-w-0">
415
+ <div className="font-medium text-sm truncate">{selectedAdapter.name}</div>
416
+ <div className="text-xs text-muted-foreground truncate">
417
+ {selectedAdapter.description}
418
+ </div>
419
+ </div>
420
+ </div>
421
+ )}
422
+ </div>
423
+ );
424
+ };
425
+
426
+ const renderSchemaForm = () => {
427
+ if (!selectedAdapter?.schema?.fields?.length) return null;
428
+
429
+ const content = (
430
+ <SchemaFormRenderer
431
+ schema={{ fields: dynamicFields }}
432
+ values={data.config}
433
+ onChange={updateConfigBatch}
434
+ secretCodes={secretOptions.map((s) => s.code)}
435
+ compact={compact}
436
+ />
437
+ );
438
+
439
+ if (variant === PANEL_VARIANT.PANEL) {
440
+ return (
441
+ <>
442
+ <Separator />
443
+ <div className={compact ? 'space-y-2' : 'space-y-3'}>
444
+ <div className="flex items-center justify-between">
445
+ <h4 className="font-medium text-sm text-muted-foreground uppercase tracking-wide">
446
+ Configuration
447
+ </h4>
448
+ <Button
449
+ variant="ghost"
450
+ size="sm"
451
+ className="h-6 text-xs"
452
+ onClick={handleResetDefaults}
453
+ >
454
+ Reset defaults
455
+ </Button>
456
+ </div>
457
+ {content}
458
+ </div>
459
+ </>
460
+ );
461
+ }
462
+
463
+ return (
464
+ <Card>
465
+ <CardHeader className="py-3">
466
+ <CardTitle className="text-sm">Configuration</CardTitle>
467
+ </CardHeader>
468
+ <CardContent>{content}</CardContent>
469
+ </Card>
470
+ );
471
+ };
472
+
473
+ const renderAdvancedEditors = () => {
474
+ if (!showAdvancedEditors || adapterType !== ADAPTER_TYPES.OPERATOR) return null;
475
+
476
+ const editorType = selectedAdapter?.editorType ?? selectedAdapter?.code;
477
+ if (!editorType) return null;
478
+
479
+ const AdvancedEditor = ADVANCED_EDITORS[editorType];
480
+ return AdvancedEditor ? <AdvancedEditor config={data.config} onChange={updateConfigBatch} /> : null;
481
+ };
482
+
483
+ const renderSpecialConfigs = () => {
484
+ const SpecialEditor = SPECIAL_CONFIG_EDITORS[stepType];
485
+ if (!SpecialEditor) return null;
486
+
487
+ return (
488
+ <SpecialEditor
489
+ config={data.config}
490
+ onChange={updateConfigBatch}
491
+ showErrorHandling={variant === PANEL_VARIANT.PANEL}
492
+ showValidationMode={true}
493
+ showRulesEditor={true}
494
+ />
495
+ );
496
+ };
497
+
498
+ const handleRetryChange = useCallback((retrySettings: RetrySettings | undefined) => {
499
+ onChange({
500
+ ...data,
501
+ config: {
502
+ ...data.config,
503
+ retryMaxRetries: retrySettings?.maxRetries,
504
+ retryDelayMs: retrySettings?.retryDelayMs,
505
+ retryBackoff: retrySettings?.backoff,
506
+ },
507
+ });
508
+ }, [onChange, data]);
509
+
510
+ const renderRetrySettings = () => {
511
+ if (stepType !== STEP_TYPE.TRANSFORM) return null;
512
+
513
+ const retrySettings: RetrySettings = {
514
+ maxRetries: data.config?.retryMaxRetries as number | undefined,
515
+ retryDelayMs: data.config?.retryDelayMs as number | undefined,
516
+ backoff: data.config?.retryBackoff as RetrySettings['backoff'],
517
+ };
518
+
519
+ return (
520
+ <RetrySettingsComponent
521
+ retrySettings={retrySettings}
522
+ onChange={handleRetryChange}
523
+ />
524
+ );
525
+ };
526
+
527
+ const renderStepTester = () => {
528
+ if (
529
+ !showStepTester ||
530
+ stepType === STEP_TYPE.TRIGGER ||
531
+ !adapterType ||
532
+ !selectedAdapter
533
+ ) {
534
+ return null;
535
+ }
536
+
537
+ return (
538
+ <>
539
+ {variant === PANEL_VARIANT.PANEL && <Separator />}
540
+ <StepTester
541
+ stepType={stepType}
542
+ adapterType={adapterType}
543
+ config={{ adapterCode: data.adapterCode, ...(data.config || {}) }}
544
+ />
545
+ </>
546
+ );
547
+ };
548
+
549
+ const spacing = compact ? 'space-y-3' : 'space-y-4';
550
+
551
+ return (
552
+ <div className={spacing}>
553
+ {renderHeader()}
554
+ {renderKeyInput()}
555
+ {renderTriggerConfig()}
556
+ {renderAdapterSelection()}
557
+ {renderSchemaForm()}
558
+ {renderSpecialConfigs()}
559
+ {renderAdvancedEditors()}
560
+ {renderRetrySettings()}
561
+ {renderStepTester()}
562
+ </div>
563
+ );
564
+ }