@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,128 @@
1
+ import * as React from 'react';
2
+ import { toast } from 'sonner';
3
+ import type { WizardStep } from '../types/wizard';
4
+ import type { FormValidationResult } from '../utils/form-validation';
5
+
6
+ interface UseWizardNavigationOptions<TConfig> {
7
+ steps: WizardStep[];
8
+ initialConfig: TConfig;
9
+ validateStep: (stepId: string, config: TConfig) => FormValidationResult;
10
+ onComplete: (config: TConfig) => void;
11
+ nameRequiredMessage: string;
12
+ isSubmitting?: boolean;
13
+ }
14
+
15
+ export function useWizardNavigation<TConfig extends { name?: string }>({
16
+ steps,
17
+ initialConfig,
18
+ validateStep,
19
+ onComplete,
20
+ nameRequiredMessage,
21
+ isSubmitting,
22
+ }: UseWizardNavigationOptions<TConfig>) {
23
+ const [config, setConfig] = React.useState<TConfig>(initialConfig);
24
+ const [currentStep, setCurrentStep] = React.useState(0);
25
+ const [stepErrors, setStepErrors] = React.useState<Record<string, string>>({});
26
+ const [attemptedNext, setAttemptedNext] = React.useState(false);
27
+
28
+ const updateConfig = React.useCallback((updates: Partial<TConfig>) => {
29
+ setConfig(prev => ({ ...prev, ...updates }));
30
+ setStepErrors({});
31
+ setAttemptedNext(false);
32
+ }, []);
33
+
34
+ const validateCurrentStep = React.useCallback(() => {
35
+ const stepId = steps[currentStep].id;
36
+ return validateStep(stepId, config);
37
+ }, [steps, currentStep, config, validateStep]);
38
+
39
+ const canProceed = React.useMemo(() => {
40
+ const validation = validateCurrentStep();
41
+ return validation.isValid;
42
+ }, [validateCurrentStep]);
43
+
44
+ const handleNext = React.useCallback(() => {
45
+ setAttemptedNext(true);
46
+ const validation = validateCurrentStep();
47
+
48
+ if (!validation.isValid) {
49
+ setStepErrors(validation.errorsByField);
50
+ const firstError = validation.errors[0];
51
+ if (firstError) {
52
+ toast.error(firstError.message);
53
+ }
54
+ return;
55
+ }
56
+
57
+ if (currentStep < steps.length - 1) {
58
+ setAttemptedNext(false);
59
+ setStepErrors({});
60
+ setCurrentStep(prev => prev + 1);
61
+ }
62
+ }, [validateCurrentStep, currentStep, steps]);
63
+
64
+ const handleBack = React.useCallback(() => {
65
+ if (currentStep > 0) {
66
+ setAttemptedNext(false);
67
+ setStepErrors({});
68
+ setCurrentStep(prev => prev - 1);
69
+ }
70
+ }, [currentStep]);
71
+
72
+ const handleStepClick = React.useCallback((index: number) => {
73
+ // Going backward is always allowed; going forward requires current step validation
74
+ if (index > currentStep) {
75
+ const validation = validateCurrentStep();
76
+ if (!validation.isValid) {
77
+ setAttemptedNext(true);
78
+ setStepErrors(validation.errorsByField);
79
+ const firstError = validation.errors[0];
80
+ if (firstError) {
81
+ toast.error(firstError.message);
82
+ }
83
+ return;
84
+ }
85
+ }
86
+ setAttemptedNext(false);
87
+ setStepErrors({});
88
+ setCurrentStep(index);
89
+ }, [currentStep, validateCurrentStep]);
90
+
91
+ const handleComplete = React.useCallback(() => {
92
+ if (isSubmitting) return;
93
+
94
+ const validation = validateCurrentStep();
95
+ if (!validation.isValid) {
96
+ setAttemptedNext(true);
97
+ setStepErrors(validation.errorsByField);
98
+ const firstError = validation.errors[0];
99
+ if (firstError) {
100
+ toast.error(firstError.message);
101
+ }
102
+ return;
103
+ }
104
+
105
+ if (!config.name) {
106
+ toast.error(nameRequiredMessage);
107
+ return;
108
+ }
109
+ onComplete(config);
110
+ }, [config, onComplete, nameRequiredMessage, isSubmitting, validateCurrentStep]);
111
+
112
+ return {
113
+ config,
114
+ setConfig,
115
+ currentStep,
116
+ setCurrentStep,
117
+ stepErrors,
118
+ setStepErrors,
119
+ attemptedNext,
120
+ setAttemptedNext,
121
+ updateConfig,
122
+ handleNext,
123
+ handleBack,
124
+ handleStepClick,
125
+ handleComplete,
126
+ canProceed,
127
+ };
128
+ }
@@ -0,0 +1,55 @@
1
+ import { defineDashboardExtension, DashboardRouteDefinition } from '@vendure/dashboard';
2
+ import { Boxes } from 'lucide-react';
3
+ import { DATAHUB_NAV_SECTION } from './constants';
4
+ import {
5
+ pipelinesList,
6
+ pipelineDetail,
7
+ importWizardPage,
8
+ exportWizardPage,
9
+ adaptersList,
10
+ secretsList,
11
+ secretDetail,
12
+ connectionsList,
13
+ connectionDetail,
14
+ hooksPage,
15
+ queuesPage,
16
+ settingsPage,
17
+ logsPage,
18
+ } from './routes';
19
+ import { ErrorBoundary } from './components/shared';
20
+
21
+ function wrapWithErrorBoundary(route: DashboardRouteDefinition): DashboardRouteDefinition {
22
+ const originalComponent = route.component;
23
+ if (!originalComponent) return route;
24
+ return {
25
+ ...route,
26
+ component: (routeArg) => (
27
+ <ErrorBoundary>
28
+ {originalComponent(routeArg)}
29
+ </ErrorBoundary>
30
+ ),
31
+ };
32
+ }
33
+
34
+ const routes: DashboardRouteDefinition[] = [
35
+ pipelinesList,
36
+ importWizardPage,
37
+ exportWizardPage,
38
+ pipelineDetail,
39
+ adaptersList,
40
+ secretsList,
41
+ secretDetail,
42
+ connectionsList,
43
+ connectionDetail,
44
+ hooksPage,
45
+ queuesPage,
46
+ settingsPage,
47
+ logsPage,
48
+ ].map(wrapWithErrorBoundary);
49
+
50
+ export default defineDashboardExtension({
51
+ navSections: [
52
+ { id: DATAHUB_NAV_SECTION, title: 'Data Hub', icon: Boxes, placement: 'bottom', order: 999 },
53
+ ],
54
+ routes,
55
+ });
@@ -0,0 +1,102 @@
1
+ import * as React from 'react';
2
+ import { Badge } from '@vendure/dashboard';
3
+ import { Puzzle, CheckCircle2, Sparkles } from 'lucide-react';
4
+ import { cn } from '../../utils';
5
+ import { resolveIconName } from '../../utils/icon-resolver';
6
+ import type { DataHubAdapter } from '../../types';
7
+
8
+ export const AdapterCard = React.memo(function AdapterCard({
9
+ adapter,
10
+ onSelect,
11
+ isBuiltIn = false,
12
+ }: Readonly<{
13
+ adapter: DataHubAdapter;
14
+ onSelect: (adapter: DataHubAdapter) => void;
15
+ isBuiltIn?: boolean;
16
+ }>) {
17
+ const handleClick = React.useCallback(() => {
18
+ onSelect(adapter);
19
+ }, [onSelect, adapter]);
20
+
21
+ const handleKeyDown = React.useCallback((e: React.KeyboardEvent) => {
22
+ if (e.key === 'Enter' || e.key === ' ') {
23
+ e.preventDefault();
24
+ onSelect(adapter);
25
+ }
26
+ }, [onSelect, adapter]);
27
+
28
+ const Icon = resolveIconName(adapter.icon);
29
+ const fieldCount = adapter.schema.fields.length;
30
+ const requiredCount = adapter.schema.fields.filter(f => f.required).length;
31
+
32
+ return (
33
+ <div
34
+ className={cn(
35
+ 'group relative rounded-xl border bg-card p-4 cursor-pointer transition-all duration-200',
36
+ 'hover:shadow-lg hover:border-primary/40 hover:-translate-y-0.5',
37
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
38
+ )}
39
+ onClick={handleClick}
40
+ onKeyDown={handleKeyDown}
41
+ role="button"
42
+ tabIndex={0}
43
+ aria-label={`View ${adapter.name ?? adapter.code} adapter details`}
44
+ data-testid={`datahub-adapter-card-${adapter.code}`}
45
+ >
46
+ {/* Header: icon + code + badges */}
47
+ <div className="flex items-start gap-3 mb-3">
48
+ <div className={cn(
49
+ 'flex-shrink-0 p-2 rounded-lg transition-colors',
50
+ isBuiltIn
51
+ ? 'bg-primary/10 text-primary group-hover:bg-primary/15'
52
+ : 'bg-violet-500/10 text-violet-600 dark:text-violet-400 group-hover:bg-violet-500/15',
53
+ )}>
54
+ {Icon ? <Icon className="w-4 h-4" /> : <Puzzle className="w-4 h-4" />}
55
+ </div>
56
+ <div className="min-w-0 flex-1">
57
+ <div className="flex items-center gap-2">
58
+ <code className="text-sm font-semibold truncate">{adapter.code}</code>
59
+ </div>
60
+ {adapter.name && adapter.name !== adapter.code && (
61
+ <p className="text-xs text-muted-foreground truncate">{adapter.name}</p>
62
+ )}
63
+ </div>
64
+ <div className="flex-shrink-0 flex items-center gap-1">
65
+ {isBuiltIn ? (
66
+ <Badge variant="outline" className="text-[10px] px-1.5 py-0 gap-1">
67
+ <CheckCircle2 className="w-3 h-3" />
68
+ Built-in
69
+ </Badge>
70
+ ) : (
71
+ <Badge variant="secondary" className="text-[10px] px-1.5 py-0 gap-1">
72
+ <Sparkles className="w-3 h-3" />
73
+ Custom
74
+ </Badge>
75
+ )}
76
+ </div>
77
+ </div>
78
+
79
+ {/* Description */}
80
+ <p className="text-xs text-muted-foreground line-clamp-2 mb-3 min-h-[2rem]">
81
+ {adapter.description || 'No description available'}
82
+ </p>
83
+
84
+ {/* Footer: metadata chips */}
85
+ <div className="flex items-center gap-2 text-[11px] text-muted-foreground">
86
+ <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-muted/60">
87
+ {fieldCount} field{fieldCount !== 1 ? 's' : ''}
88
+ </span>
89
+ {requiredCount > 0 && (
90
+ <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-muted/60">
91
+ {requiredCount} required
92
+ </span>
93
+ )}
94
+ {adapter.pure && (
95
+ <span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full bg-emerald-500/10 text-emerald-600 dark:text-emerald-400">
96
+ Pure
97
+ </span>
98
+ )}
99
+ </div>
100
+ </div>
101
+ );
102
+ });
@@ -0,0 +1,20 @@
1
+ export const ADAPTERS_TABLE_PAGE_SIZE = 25;
2
+
3
+ /** UI helper: default example values per schema field type for adapter detail display */
4
+ const EXAMPLE_VALUES: Record<string, unknown> = {
5
+ number: 1000,
6
+ boolean: true,
7
+ select: 'value',
8
+ json: {},
9
+ array: [],
10
+ };
11
+
12
+ export function guessExampleValue(
13
+ type: string,
14
+ options?: Array<{ value: string; label: string }> | null,
15
+ ): unknown {
16
+ if (options && options.length > 0) {
17
+ return options[0].value;
18
+ }
19
+ return EXAMPLE_VALUES[type] ?? 'value';
20
+ }
@@ -0,0 +1,208 @@
1
+ import * as React from 'react';
2
+ import {
3
+ Button,
4
+ Badge,
5
+ Textarea,
6
+ } from '@vendure/dashboard';
7
+ import {
8
+ Copy,
9
+ CheckCircle2,
10
+ Code2,
11
+ Settings2,
12
+ Puzzle,
13
+ Zap,
14
+ Link2,
15
+ } from 'lucide-react';
16
+ import { toast } from 'sonner';
17
+ import { cn } from '../../utils';
18
+ import { UI_DEFAULTS, TEXTAREA_HEIGHTS, TOAST_ADAPTER } from '../../constants';
19
+ import { resolveIconName } from '../../utils/icon-resolver';
20
+ import { guessExampleValue } from './AdapterConstants';
21
+ import type { DataHubAdapter } from '../../types';
22
+
23
+ export function AdapterDetail({ adapter }: Readonly<{ adapter: DataHubAdapter }>) {
24
+ const [copied, setCopied] = React.useState(false);
25
+ const copyTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
26
+ const Icon = resolveIconName(adapter.icon);
27
+
28
+ // Cleanup timeout on unmount
29
+ React.useEffect(() => {
30
+ return () => {
31
+ if (copyTimeoutRef.current) {
32
+ clearTimeout(copyTimeoutRef.current);
33
+ }
34
+ };
35
+ }, []);
36
+
37
+ const exampleConfig = React.useMemo(() => {
38
+ const config: Record<string, unknown> = { adapterCode: adapter.code };
39
+ for (const field of adapter.schema.fields) {
40
+ if (field.required) {
41
+ config[field.key] = guessExampleValue(field.type, field.options);
42
+ }
43
+ }
44
+ return JSON.stringify(config, null, 2);
45
+ }, [adapter]);
46
+
47
+ const copyConfig = async () => {
48
+ try {
49
+ await navigator.clipboard.writeText(exampleConfig);
50
+ setCopied(true);
51
+ toast.success(TOAST_ADAPTER.CONFIG_COPIED);
52
+ if (copyTimeoutRef.current) {
53
+ clearTimeout(copyTimeoutRef.current);
54
+ }
55
+ copyTimeoutRef.current = setTimeout(() => setCopied(false), UI_DEFAULTS.COPY_FEEDBACK_TIMEOUT_MS);
56
+ } catch {
57
+ toast.error(TOAST_ADAPTER.COPY_ERROR);
58
+ }
59
+ };
60
+
61
+ const requiredFields = adapter.schema.fields.filter(f => f.required);
62
+ const optionalFields = adapter.schema.fields.filter(f => !f.required);
63
+
64
+ return (
65
+ <div className="p-5 space-y-6">
66
+ {/* Quick info cards */}
67
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-3">
68
+ <div className="flex items-center gap-3 p-3 rounded-xl bg-muted/30 border">
69
+ <div className="p-2 rounded-lg bg-primary/10 text-primary">
70
+ {Icon ? <Icon className="w-4 h-4" /> : <Puzzle className="w-4 h-4" />}
71
+ </div>
72
+ <div>
73
+ <div className="text-[11px] text-muted-foreground uppercase tracking-wider">Type</div>
74
+ <div className="font-medium text-sm">{adapter.type}</div>
75
+ </div>
76
+ </div>
77
+ <div className="flex items-center gap-3 p-3 rounded-xl bg-muted/30 border">
78
+ <div className={cn(
79
+ 'p-2 rounded-lg',
80
+ adapter.pure ? 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400' : 'bg-muted text-muted-foreground',
81
+ )}>
82
+ <Zap className="w-4 h-4" />
83
+ </div>
84
+ <div>
85
+ <div className="text-[11px] text-muted-foreground uppercase tracking-wider">Pure</div>
86
+ <div className="font-medium text-sm">{adapter.pure ? 'Yes' : 'No'}</div>
87
+ </div>
88
+ </div>
89
+ <div className="flex items-center gap-3 p-3 rounded-xl bg-muted/30 border">
90
+ <div className="p-2 rounded-lg bg-muted text-muted-foreground">
91
+ <Link2 className="w-4 h-4" />
92
+ </div>
93
+ <div>
94
+ <div className="text-[11px] text-muted-foreground uppercase tracking-wider">Deps</div>
95
+ <div className="font-medium text-sm">
96
+ {adapter.requires?.length ? adapter.requires.join(', ') : 'None'}
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
101
+
102
+ {/* Configuration Fields */}
103
+ <div>
104
+ <h4 className="text-sm font-medium mb-3 flex items-center gap-2">
105
+ <Settings2 className="w-4 h-4 text-muted-foreground" />
106
+ Configuration Fields
107
+ <Badge variant="secondary" className="text-xs ml-auto">
108
+ {adapter.schema.fields.length} total
109
+ </Badge>
110
+ </h4>
111
+ <div className="border rounded-xl overflow-hidden">
112
+ <table className="w-full text-sm">
113
+ <thead>
114
+ <tr className="bg-muted/50">
115
+ <th className="text-left px-3 py-2.5 text-xs font-medium text-muted-foreground uppercase tracking-wider">Field</th>
116
+ <th className="text-left px-3 py-2.5 text-xs font-medium text-muted-foreground uppercase tracking-wider">Type</th>
117
+ <th className="text-left px-3 py-2.5 text-xs font-medium text-muted-foreground uppercase tracking-wider">Status</th>
118
+ <th className="text-left px-3 py-2.5 text-xs font-medium text-muted-foreground uppercase tracking-wider">Description</th>
119
+ </tr>
120
+ </thead>
121
+ <tbody>
122
+ {requiredFields.map(field => (
123
+ <tr key={field.key} className="border-t hover:bg-muted/20 transition-colors">
124
+ <td className="px-3 py-2.5">
125
+ <code className="text-xs bg-muted px-1.5 py-0.5 rounded font-medium">
126
+ {field.key}
127
+ </code>
128
+ </td>
129
+ <td className="px-3 py-2.5 text-muted-foreground">
130
+ <span className="text-xs">{field.type}</span>
131
+ {field.options && field.options.length > 0 && (
132
+ <span className="ml-1 text-[11px] text-muted-foreground/60">
133
+ ({field.options.map(o => o.value).join(' | ')})
134
+ </span>
135
+ )}
136
+ </td>
137
+ <td className="px-3 py-2.5">
138
+ <Badge variant="destructive" className="text-[10px] px-1.5 py-0">
139
+ Required
140
+ </Badge>
141
+ </td>
142
+ <td className="px-3 py-2.5 text-muted-foreground text-xs">
143
+ {field.description || field.label || '\u2014'}
144
+ </td>
145
+ </tr>
146
+ ))}
147
+ {optionalFields.map(field => (
148
+ <tr key={field.key} className="border-t hover:bg-muted/20 transition-colors">
149
+ <td className="px-3 py-2.5">
150
+ <code className="text-xs bg-muted px-1.5 py-0.5 rounded">
151
+ {field.key}
152
+ </code>
153
+ </td>
154
+ <td className="px-3 py-2.5 text-muted-foreground">
155
+ <span className="text-xs">{field.type}</span>
156
+ {field.options && field.options.length > 0 && (
157
+ <span className="ml-1 text-[11px] text-muted-foreground/60">
158
+ ({field.options.map(o => o.value).join(' | ')})
159
+ </span>
160
+ )}
161
+ </td>
162
+ <td className="px-3 py-2.5">
163
+ <Badge variant="outline" className="text-[10px] px-1.5 py-0">
164
+ Optional
165
+ </Badge>
166
+ </td>
167
+ <td className="px-3 py-2.5 text-muted-foreground text-xs">
168
+ {field.description || field.label || '\u2014'}
169
+ </td>
170
+ </tr>
171
+ ))}
172
+ {adapter.schema.fields.length === 0 && (
173
+ <tr>
174
+ <td colSpan={4} className="px-3 py-6 text-center text-muted-foreground text-sm">
175
+ No configuration fields
176
+ </td>
177
+ </tr>
178
+ )}
179
+ </tbody>
180
+ </table>
181
+ </div>
182
+ </div>
183
+
184
+ {/* Example Configuration */}
185
+ <div>
186
+ <div className="flex items-center justify-between mb-3">
187
+ <h4 className="text-sm font-medium flex items-center gap-2">
188
+ <Code2 className="w-4 h-4 text-muted-foreground" />
189
+ Example Configuration
190
+ </h4>
191
+ <Button variant="outline" size="sm" onClick={copyConfig} data-testid="datahub-adapter-config-copy-button">
192
+ {copied ? (
193
+ <CheckCircle2 className="w-4 h-4 mr-1 text-emerald-600" />
194
+ ) : (
195
+ <Copy className="w-4 h-4 mr-1" />
196
+ )}
197
+ {copied ? 'Copied!' : 'Copy'}
198
+ </Button>
199
+ </div>
200
+ <Textarea
201
+ value={exampleConfig}
202
+ readOnly
203
+ className={`font-mono text-sm ${TEXTAREA_HEIGHTS.ADAPTER_SCHEMA} rounded-xl`}
204
+ />
205
+ </div>
206
+ </div>
207
+ );
208
+ }
@@ -0,0 +1,105 @@
1
+ import * as React from 'react';
2
+ import { CheckCircle2, Info, Puzzle, Sparkles } from 'lucide-react';
3
+ import { Badge } from '@vendure/dashboard';
4
+ import { AdapterCard } from './AdapterCard';
5
+ import { ITEMS_PER_PAGE } from '../../constants';
6
+ import { useLoadMore } from '../../hooks';
7
+ import { LoadMoreButton } from '../../components/shared';
8
+ import { resolveIconName } from '../../utils/icon-resolver';
9
+ import type { DataHubAdapter } from '../../types';
10
+
11
+ export function AdapterTypeSection({
12
+ type,
13
+ label,
14
+ description,
15
+ icon,
16
+ adapters,
17
+ onSelect,
18
+ isBuiltIn,
19
+ }: Readonly<{
20
+ type: string;
21
+ label: string;
22
+ description?: string;
23
+ icon?: string;
24
+ adapters: DataHubAdapter[];
25
+ onSelect: (adapter: DataHubAdapter) => void;
26
+ isBuiltIn: (code: string) => boolean;
27
+ }>) {
28
+ const Icon = resolveIconName(icon);
29
+ const builtIn = React.useMemo(() => adapters.filter(a => isBuiltIn(a.code)), [adapters, isBuiltIn]);
30
+ const custom = React.useMemo(() => adapters.filter(a => !isBuiltIn(a.code)), [adapters, isBuiltIn]);
31
+
32
+ const { displayed: displayedBuiltIn, hasMore: hasMoreBuiltIn, remaining: remainingBuiltIn, loadMore: loadMoreBuiltIn } = useLoadMore(builtIn, { pageSize: ITEMS_PER_PAGE });
33
+ const { displayed: displayedCustom, hasMore: hasMoreCustom, remaining: remainingCustom, loadMore: loadMoreCustom } = useLoadMore(custom, { pageSize: ITEMS_PER_PAGE });
34
+
35
+ return (
36
+ <div className="space-y-6">
37
+ {/* Section header */}
38
+ <div className="flex items-center gap-3 pb-4 border-b">
39
+ <div className="p-2.5 rounded-xl bg-primary/10 text-primary">
40
+ {Icon ? <Icon className="w-5 h-5" /> : <Puzzle className="w-5 h-5" />}
41
+ </div>
42
+ <div className="flex-1 min-w-0">
43
+ <div className="flex items-center gap-2">
44
+ <h3 className="font-semibold">{label}</h3>
45
+ <Badge variant="secondary" className="text-xs">
46
+ {adapters.length}
47
+ </Badge>
48
+ </div>
49
+ {description && <p className="text-sm text-muted-foreground mt-0.5">{description}</p>}
50
+ </div>
51
+ </div>
52
+
53
+ {builtIn.length > 0 && (
54
+ <div>
55
+ <h4 className="text-sm font-medium mb-3 flex items-center gap-2 text-muted-foreground">
56
+ <CheckCircle2 className="w-4 h-4 text-emerald-600 dark:text-emerald-400" />
57
+ Built-in
58
+ <span className="text-xs font-normal">({builtIn.length})</span>
59
+ </h4>
60
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
61
+ {displayedBuiltIn.map(adapter => (
62
+ <AdapterCard
63
+ key={adapter.code}
64
+ adapter={adapter}
65
+ onSelect={onSelect}
66
+ isBuiltIn
67
+ />
68
+ ))}
69
+ </div>
70
+ {hasMoreBuiltIn && <LoadMoreButton remaining={remainingBuiltIn} onClick={loadMoreBuiltIn} data-testid={`datahub-adapters-load-more-builtin-${type}`} />}
71
+ </div>
72
+ )}
73
+
74
+ {custom.length > 0 && (
75
+ <div>
76
+ <h4 className="text-sm font-medium mb-3 flex items-center gap-2 text-muted-foreground">
77
+ <Sparkles className="w-4 h-4 text-violet-600 dark:text-violet-400" />
78
+ Custom
79
+ <span className="text-xs font-normal">({custom.length})</span>
80
+ </h4>
81
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
82
+ {displayedCustom.map(adapter => (
83
+ <AdapterCard
84
+ key={adapter.code}
85
+ adapter={adapter}
86
+ onSelect={onSelect}
87
+ />
88
+ ))}
89
+ </div>
90
+ {hasMoreCustom && <LoadMoreButton remaining={remainingCustom} onClick={loadMoreCustom} data-testid={`datahub-adapters-load-more-custom-${type}`} />}
91
+ </div>
92
+ )}
93
+
94
+ {adapters.length === 0 && (
95
+ <div className="text-center py-12 text-muted-foreground">
96
+ <div className="mx-auto w-12 h-12 rounded-full bg-muted/50 flex items-center justify-center mb-3">
97
+ <Info className="w-6 h-6 opacity-50" />
98
+ </div>
99
+ <p className="font-medium">No {label.toLowerCase()} registered</p>
100
+ <p className="text-sm mt-1">Register adapters in your plugin configuration.</p>
101
+ </div>
102
+ )}
103
+ </div>
104
+ );
105
+ }