@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,156 @@
1
+ import * as React from 'react';
2
+ import {
3
+ Badge,
4
+ DetailFormGrid,
5
+ FormFieldWrapper,
6
+ Input,
7
+ Switch,
8
+ } from '@vendure/dashboard';
9
+ import type { UseFormReturn, FieldValues } from 'react-hook-form';
10
+ import { FieldError } from '../../../components/common';
11
+ import { PIPELINE_STATUS, getStatusBadgeVariant, ERROR_MESSAGES, PLACEHOLDERS } from '../../../constants';
12
+ import { CODE_PATTERN, formatDateTime } from '../../../utils';
13
+ import type { PipelineEntity, ValidationState } from '../../../types';
14
+ import { ValidationStatusBadge } from './ValidationPanel';
15
+
16
+ export interface PipelineFormFieldsProps {
17
+ /** The form control instance from react-hook-form */
18
+ form: UseFormReturn<FieldValues>;
19
+ /** Whether the pipeline is being created (new) */
20
+ creating: boolean;
21
+ /** The pipeline entity (for existing pipelines) */
22
+ entity?: PipelineEntity;
23
+ /** Current validation state */
24
+ validation: ValidationState;
25
+ /** Whether validation is in progress */
26
+ validationPending: boolean;
27
+ /** Callback to show validation issues panel */
28
+ onShowIssues: () => void;
29
+ }
30
+
31
+ /**
32
+ * Form fields for the pipeline detail page.
33
+ * Includes name, code, enabled toggle, and status display for existing pipelines.
34
+ */
35
+ export function PipelineFormFields({
36
+ form,
37
+ creating,
38
+ entity,
39
+ validation,
40
+ validationPending,
41
+ onShowIssues,
42
+ }: Readonly<PipelineFormFieldsProps>) {
43
+ return (
44
+ <DetailFormGrid>
45
+ <FormFieldWrapper
46
+ name="name"
47
+ label="Name"
48
+ control={form.control}
49
+ rules={{
50
+ required: ERROR_MESSAGES.NAME_REQUIRED,
51
+ minLength: {
52
+ value: 2,
53
+ message: ERROR_MESSAGES.MIN_LENGTH_2,
54
+ },
55
+ }}
56
+ render={({ field, fieldState }) => (
57
+ <div>
58
+ <Input
59
+ {...field}
60
+ placeholder={PLACEHOLDERS.PIPELINE_NAME}
61
+ className={
62
+ fieldState.error
63
+ ? 'border-destructive focus-visible:ring-destructive'
64
+ : ''
65
+ }
66
+ data-testid="pipeline-name-input"
67
+ />
68
+ <FieldError
69
+ error={fieldState.error?.message}
70
+ touched={fieldState.isTouched}
71
+ />
72
+ </div>
73
+ )}
74
+ />
75
+ <FormFieldWrapper
76
+ name="code"
77
+ label="Code"
78
+ control={form.control}
79
+ rules={{
80
+ required: ERROR_MESSAGES.CODE_REQUIRED,
81
+ pattern: {
82
+ value: CODE_PATTERN,
83
+ message: ERROR_MESSAGES.CODE_PATTERN,
84
+ },
85
+ }}
86
+ render={({ field, fieldState }) => (
87
+ <div>
88
+ <Input
89
+ {...field}
90
+ placeholder={PLACEHOLDERS.PIPELINE_CODE}
91
+ className={
92
+ fieldState.error
93
+ ? 'border-destructive focus-visible:ring-destructive'
94
+ : ''
95
+ }
96
+ data-testid="pipeline-code-input"
97
+ />
98
+ <FieldError
99
+ error={fieldState.error?.message}
100
+ touched={fieldState.isTouched}
101
+ />
102
+ {!fieldState.error && (
103
+ <p className="mt-1 text-xs text-muted-foreground">
104
+ Letters, numbers, hyphens, and underscores only
105
+ </p>
106
+ )}
107
+ </div>
108
+ )}
109
+ />
110
+ <FormFieldWrapper
111
+ name="enabled"
112
+ label="Enabled"
113
+ control={form.control}
114
+ render={({ field }) => (
115
+ <div className="flex items-center h-10">
116
+ <Switch
117
+ checked={Boolean(field.value)}
118
+ onCheckedChange={field.onChange}
119
+ data-testid="pipeline-enabled-toggle"
120
+ />
121
+ </div>
122
+ )}
123
+ />
124
+ {!creating && entity && (
125
+ <>
126
+ <div className="col-span-2 text-sm flex items-center gap-2">
127
+ Status:{' '}
128
+ <Badge
129
+ variant={getStatusBadgeVariant(
130
+ entity.status ?? PIPELINE_STATUS.DRAFT
131
+ )}
132
+ >
133
+ {entity.status ?? PIPELINE_STATUS.DRAFT}
134
+ </Badge>
135
+ <span className="text-muted-foreground">
136
+ v{entity.version ?? 0}
137
+ </span>
138
+ </div>
139
+ <div className="col-span-2 text-sm flex items-center gap-3">
140
+ <span>
141
+ Published:{' '}
142
+ {entity.publishedAt
143
+ ? formatDateTime(entity.publishedAt)
144
+ : '-'}
145
+ </span>
146
+ <ValidationStatusBadge
147
+ validation={validation}
148
+ isLoading={validationPending}
149
+ onShowIssues={onShowIssues}
150
+ />
151
+ </div>
152
+ </>
153
+ )}
154
+ </DetailFormGrid>
155
+ );
156
+ }
@@ -0,0 +1,111 @@
1
+ import * as React from 'react';
2
+ import { STEP_TYPE, DATAHUB_API_WEBHOOK } from '../../../constants';
3
+ import type { PipelineDefinition, PipelineStep } from '../../../types';
4
+
5
+ export interface PipelineWebhookInfoProps {
6
+ /** Function that returns the current pipeline definition */
7
+ definition: () => PipelineDefinition | undefined;
8
+ }
9
+
10
+ interface WebhookTriggerInfo {
11
+ key: string;
12
+ requiresIdk: boolean;
13
+ sig: boolean;
14
+ headerName: string;
15
+ authType: string;
16
+ }
17
+
18
+ /**
19
+ * Displays webhook trigger information including URL and example cURL command.
20
+ * Shows info for ALL webhook triggers configured on the pipeline.
21
+ */
22
+ export function PipelineWebhookInfo({
23
+ definition,
24
+ }: Readonly<PipelineWebhookInfoProps>) {
25
+ const def = definition() ?? {};
26
+ const steps = def.steps ?? [];
27
+
28
+ // Find ALL webhook triggers
29
+ const webhookTriggers: WebhookTriggerInfo[] = steps
30
+ .filter((step): step is PipelineStep =>
31
+ step.type === STEP_TYPE.TRIGGER &&
32
+ (step.config as Record<string, unknown>)?.type === 'WEBHOOK'
33
+ )
34
+ .map(trigger => {
35
+ const cfg = trigger.config as Record<string, unknown> ?? {};
36
+ return {
37
+ key: trigger.key,
38
+ requiresIdk: Boolean(cfg.requireIdempotencyKey),
39
+ sig: cfg.signature === 'hmac-sha256' || cfg.authentication === 'hmac',
40
+ headerName: String(cfg.hmacHeaderName ?? cfg.headerName ?? 'x-datahub-signature'),
41
+ authType: String(cfg.authentication ?? 'NONE'),
42
+ };
43
+ });
44
+
45
+ if (webhookTriggers.length === 0) {
46
+ return null;
47
+ }
48
+
49
+ const pipelineCode = def.code ?? 'PIPELINE_CODE';
50
+ const url = `${window.location.origin}${DATAHUB_API_WEBHOOK(pipelineCode)}`;
51
+
52
+ // Generate cURL for first webhook (as example)
53
+ const firstWebhook = webhookTriggers[0];
54
+ const curlParts = [
55
+ `curl -X POST '${url}' \\`,
56
+ ` -H 'Content-Type: application/json'`,
57
+ ];
58
+
59
+ if (firstWebhook.requiresIdk) {
60
+ curlParts.push(` -H 'X-Idempotency-Key: <unique-id>' \\`);
61
+ }
62
+
63
+ if (firstWebhook.sig) {
64
+ curlParts.push(` -H '${firstWebhook.headerName}: <hmac-of-body>' \\`);
65
+ }
66
+
67
+ curlParts.push(` -d '{"records":[{"id":"123","name":"Example"}]}'`);
68
+
69
+ const curl = curlParts.join(' \\\n');
70
+
71
+ return (
72
+ <div className="border rounded-md p-3 space-y-2">
73
+ <div className="text-sm font-medium">
74
+ Webhook Trigger{webhookTriggers.length > 1 ? 's' : ''} ({webhookTriggers.length})
75
+ </div>
76
+ <div className="text-sm">
77
+ POST{' '}
78
+ <code className="font-mono">
79
+ {DATAHUB_API_WEBHOOK(pipelineCode)}
80
+ </code>
81
+ </div>
82
+
83
+ {webhookTriggers.length > 1 && (
84
+ <div className="text-xs text-muted-foreground">
85
+ Multiple webhook triggers configured - request will authenticate against any matching trigger
86
+ </div>
87
+ )}
88
+
89
+ {webhookTriggers.map((webhook, index) => (
90
+ <div key={webhook.key} className="text-sm border-l-2 border-muted pl-2 py-1">
91
+ <div className="font-medium text-xs text-muted-foreground">
92
+ {webhookTriggers.length > 1 ? `Webhook ${index + 1}: ` : ''}
93
+ {webhook.key}
94
+ </div>
95
+ <div className="text-xs">
96
+ Auth: <code className="font-mono">{webhook.authType}</code>
97
+ {webhook.requiresIdk && ' • Requires Idempotency Key'}
98
+ {webhook.sig && ` • HMAC header: ${webhook.headerName}`}
99
+ </div>
100
+ </div>
101
+ ))}
102
+
103
+ <div>
104
+ <div className="text-sm font-medium mb-1">Example cURL</div>
105
+ <pre className="bg-muted p-2 rounded text-xs overflow-auto">
106
+ {curl}
107
+ </pre>
108
+ </div>
109
+ </div>
110
+ );
111
+ }
@@ -0,0 +1,342 @@
1
+ import * as React from 'react';
2
+ import {
3
+ Button,
4
+ Dialog,
5
+ DialogContent,
6
+ DialogHeader,
7
+ DialogTitle,
8
+ DialogDescription,
9
+ DialogFooter,
10
+ } from '@vendure/dashboard';
11
+ import { toast } from 'sonner';
12
+ import {
13
+ CheckCircle2,
14
+ XCircle,
15
+ Send,
16
+ Clock,
17
+ FileCheck,
18
+ AlertCircle,
19
+ Archive,
20
+ Play,
21
+ } from 'lucide-react';
22
+ import { getErrorMessage } from '../../../../shared';
23
+ import {
24
+ PIPELINE_STATUS,
25
+ TOAST_PIPELINE,
26
+ } from '../../../constants';
27
+ import type { PipelineStatus } from '../../../constants';
28
+ import {
29
+ useSubmitPipelineForReview,
30
+ useApprovePipeline,
31
+ useRejectPipeline,
32
+ useArchivePipeline,
33
+ } from '../../../hooks';
34
+
35
+ export interface ReviewActionsPanelProps {
36
+ entityId?: string;
37
+ status?: PipelineStatus;
38
+ onStatusChange?: () => void;
39
+ }
40
+
41
+ const STATUS_CONFIG: Record<string, {
42
+ label: string;
43
+ description: string;
44
+ icon: React.ElementType;
45
+ dotColor: string;
46
+ }> = {
47
+ DRAFT: {
48
+ label: 'Draft',
49
+ description: 'Not yet submitted for review',
50
+ icon: Clock,
51
+ dotColor: 'bg-slate-400',
52
+ },
53
+ REVIEW: {
54
+ label: 'In Review',
55
+ description: 'Awaiting approval',
56
+ icon: FileCheck,
57
+ dotColor: 'bg-amber-500',
58
+ },
59
+ PUBLISHED: {
60
+ label: 'Published',
61
+ description: 'Active and ready to run',
62
+ icon: CheckCircle2,
63
+ dotColor: 'bg-emerald-500',
64
+ },
65
+ ARCHIVED: {
66
+ label: 'Archived',
67
+ description: 'Disabled and inactive',
68
+ icon: AlertCircle,
69
+ dotColor: 'bg-slate-400',
70
+ },
71
+ };
72
+
73
+ export function ReviewActionsPanel({
74
+ entityId,
75
+ status,
76
+ onStatusChange,
77
+ }: Readonly<ReviewActionsPanelProps>) {
78
+ const [rejectDialogOpen, setRejectDialogOpen] = React.useState(false);
79
+ const [approveDialogOpen, setApproveDialogOpen] = React.useState(false);
80
+ const [submitDialogOpen, setSubmitDialogOpen] = React.useState(false);
81
+ const [archiveDialogOpen, setArchiveDialogOpen] = React.useState(false);
82
+
83
+ const submitForReview = useSubmitPipelineForReview();
84
+ const approve = useApprovePipeline();
85
+ const reject = useRejectPipeline();
86
+ const archive = useArchivePipeline();
87
+
88
+ const isSubmitting = submitForReview.isPending || approve.isPending || reject.isPending || archive.isPending;
89
+
90
+ const statusConfig = STATUS_CONFIG[status ?? 'DRAFT'] ?? STATUS_CONFIG.DRAFT;
91
+
92
+ const createMutationHandler = React.useCallback(
93
+ (
94
+ mutateFn: (id: string, options: { onSuccess: () => void; onError: (err: unknown) => void }) => void,
95
+ successMessage: string,
96
+ errorMessage: string,
97
+ closeDialog: React.Dispatch<React.SetStateAction<boolean>>,
98
+ onAfterSuccess?: () => void,
99
+ ) => () => {
100
+ if (!entityId) return;
101
+ mutateFn(entityId, {
102
+ onSuccess: () => {
103
+ toast.success(successMessage);
104
+ closeDialog(false);
105
+ onAfterSuccess?.();
106
+ onStatusChange?.();
107
+ },
108
+ onError: (err) => {
109
+ toast.error(errorMessage, {
110
+ description: getErrorMessage(err),
111
+ });
112
+ },
113
+ });
114
+ },
115
+ [entityId, onStatusChange],
116
+ );
117
+
118
+ const handleSubmitForReview = React.useMemo(
119
+ () => createMutationHandler(submitForReview.mutate, TOAST_PIPELINE.SUBMITTED_FOR_REVIEW, TOAST_PIPELINE.SUBMIT_ERROR, setSubmitDialogOpen),
120
+ [createMutationHandler, submitForReview.mutate],
121
+ );
122
+
123
+ const handleApprove = React.useMemo(
124
+ () => createMutationHandler(approve.mutate, TOAST_PIPELINE.APPROVED, TOAST_PIPELINE.APPROVE_ERROR, setApproveDialogOpen),
125
+ [createMutationHandler, approve.mutate],
126
+ );
127
+
128
+ const handleReject = React.useMemo(
129
+ () => createMutationHandler(reject.mutate, TOAST_PIPELINE.REJECTED, TOAST_PIPELINE.REJECT_ERROR, setRejectDialogOpen),
130
+ [createMutationHandler, reject.mutate],
131
+ );
132
+
133
+ const handleArchive = React.useMemo(
134
+ () => createMutationHandler(archive.mutate, TOAST_PIPELINE.ARCHIVED, TOAST_PIPELINE.ARCHIVE_ERROR, setArchiveDialogOpen),
135
+ [createMutationHandler, archive.mutate],
136
+ );
137
+
138
+ if (!entityId) return null;
139
+
140
+ return (
141
+ <>
142
+ <div className="flex items-center justify-between gap-4 rounded-lg border bg-card p-3">
143
+ <div className="flex items-center gap-3">
144
+ <div className={`h-2.5 w-2.5 rounded-full ${statusConfig.dotColor}`} />
145
+ <div className="flex items-center gap-2">
146
+ <span className="font-medium text-sm">{statusConfig.label}</span>
147
+ <span className="text-muted-foreground text-sm">·</span>
148
+ <span className="text-muted-foreground text-sm">{statusConfig.description}</span>
149
+ </div>
150
+ </div>
151
+
152
+ <div className="flex items-center gap-2">
153
+ {status === PIPELINE_STATUS.DRAFT && (
154
+ <Button
155
+ variant="default"
156
+ size="sm"
157
+ onClick={() => setSubmitDialogOpen(true)}
158
+ aria-label="Submit pipeline for review"
159
+ data-testid="datahub-review-submit-btn"
160
+ >
161
+ <Send className="mr-1.5 h-3.5 w-3.5" />
162
+ Submit for Review
163
+ </Button>
164
+ )}
165
+
166
+ {status === PIPELINE_STATUS.REVIEW && (
167
+ <>
168
+ <Button
169
+ variant="outline"
170
+ size="sm"
171
+ onClick={() => setRejectDialogOpen(true)}
172
+ aria-label="Reject pipeline and return to draft"
173
+ data-testid="datahub-review-reject-btn"
174
+ >
175
+ <XCircle className="mr-1.5 h-3.5 w-3.5" />
176
+ Reject
177
+ </Button>
178
+ <Button
179
+ variant="default"
180
+ size="sm"
181
+ onClick={() => setApproveDialogOpen(true)}
182
+ aria-label="Approve and publish pipeline"
183
+ data-testid="datahub-review-approve-btn"
184
+ >
185
+ <CheckCircle2 className="mr-1.5 h-3.5 w-3.5" />
186
+ Approve
187
+ </Button>
188
+ </>
189
+ )}
190
+
191
+ {status === PIPELINE_STATUS.PUBLISHED && (
192
+ <Button
193
+ variant="outline"
194
+ size="sm"
195
+ onClick={() => setArchiveDialogOpen(true)}
196
+ aria-label="Archive pipeline"
197
+ data-testid="datahub-review-archive-btn"
198
+ >
199
+ <Archive className="mr-1.5 h-3.5 w-3.5" />
200
+ Archive
201
+ </Button>
202
+ )}
203
+
204
+ {status === PIPELINE_STATUS.ARCHIVED && (
205
+ <Button
206
+ variant="outline"
207
+ size="sm"
208
+ onClick={() => setSubmitDialogOpen(true)}
209
+ aria-label="Reactivate archived pipeline"
210
+ data-testid="datahub-review-reactivate-btn"
211
+ >
212
+ <Play className="mr-1.5 h-3.5 w-3.5" />
213
+ Reactivate
214
+ </Button>
215
+ )}
216
+ </div>
217
+ </div>
218
+
219
+ {/* Submit for Review Dialog */}
220
+ <Dialog open={submitDialogOpen} onOpenChange={setSubmitDialogOpen}>
221
+ <DialogContent className="max-w-md">
222
+ <DialogHeader>
223
+ <DialogTitle>Submit for Review</DialogTitle>
224
+ <DialogDescription>
225
+ A reviewer will check the configuration before publishing.
226
+ </DialogDescription>
227
+ </DialogHeader>
228
+ <div className="py-2">
229
+ <p className="text-sm text-muted-foreground">
230
+ Make sure you've saved all changes before submitting.
231
+ </p>
232
+ </div>
233
+ <DialogFooter>
234
+ <Button
235
+ variant="outline"
236
+ onClick={() => setSubmitDialogOpen(false)}
237
+ disabled={isSubmitting}
238
+ >
239
+ Cancel
240
+ </Button>
241
+ <Button onClick={handleSubmitForReview} disabled={isSubmitting}>
242
+ {isSubmitting ? 'Submitting...' : 'Submit'}
243
+ </Button>
244
+ </DialogFooter>
245
+ </DialogContent>
246
+ </Dialog>
247
+
248
+ {/* Approve Dialog */}
249
+ <Dialog open={approveDialogOpen} onOpenChange={setApproveDialogOpen}>
250
+ <DialogContent className="max-w-md">
251
+ <DialogHeader>
252
+ <DialogTitle>Approve Pipeline</DialogTitle>
253
+ <DialogDescription>
254
+ This will publish the pipeline and make it active.
255
+ </DialogDescription>
256
+ </DialogHeader>
257
+ <div className="py-2 space-y-2 text-sm text-muted-foreground">
258
+ <p>By approving, you confirm:</p>
259
+ <ul className="list-disc pl-5 space-y-1">
260
+ <li>Configuration has been reviewed</li>
261
+ <li>Data mappings are correct</li>
262
+ <li>Ready for production use</li>
263
+ </ul>
264
+ </div>
265
+ <DialogFooter>
266
+ <Button
267
+ variant="outline"
268
+ onClick={() => setApproveDialogOpen(false)}
269
+ disabled={isSubmitting}
270
+ >
271
+ Cancel
272
+ </Button>
273
+ <Button onClick={handleApprove} disabled={isSubmitting}>
274
+ {isSubmitting ? 'Approving...' : 'Approve'}
275
+ </Button>
276
+ </DialogFooter>
277
+ </DialogContent>
278
+ </Dialog>
279
+
280
+ {/* Reject Dialog */}
281
+ <Dialog open={rejectDialogOpen} onOpenChange={setRejectDialogOpen}>
282
+ <DialogContent className="max-w-md">
283
+ <DialogHeader>
284
+ <DialogTitle>Reject Pipeline</DialogTitle>
285
+ <DialogDescription>
286
+ This will return the pipeline to draft status for further changes.
287
+ </DialogDescription>
288
+ </DialogHeader>
289
+ <DialogFooter>
290
+ <Button
291
+ variant="outline"
292
+ onClick={() => setRejectDialogOpen(false)}
293
+ disabled={isSubmitting}
294
+ >
295
+ Cancel
296
+ </Button>
297
+ <Button
298
+ variant="destructive"
299
+ onClick={handleReject}
300
+ disabled={isSubmitting}
301
+ >
302
+ {isSubmitting ? 'Rejecting...' : 'Reject'}
303
+ </Button>
304
+ </DialogFooter>
305
+ </DialogContent>
306
+ </Dialog>
307
+
308
+ {/* Archive Dialog */}
309
+ <Dialog open={archiveDialogOpen} onOpenChange={setArchiveDialogOpen}>
310
+ <DialogContent className="max-w-md">
311
+ <DialogHeader>
312
+ <DialogTitle>Archive Pipeline</DialogTitle>
313
+ <DialogDescription>
314
+ This will disable the pipeline and stop all scheduled runs.
315
+ </DialogDescription>
316
+ </DialogHeader>
317
+ <div className="py-2">
318
+ <p className="text-sm text-muted-foreground">
319
+ You can reactivate the pipeline later by submitting it for review again.
320
+ </p>
321
+ </div>
322
+ <DialogFooter>
323
+ <Button
324
+ variant="outline"
325
+ onClick={() => setArchiveDialogOpen(false)}
326
+ disabled={isSubmitting}
327
+ >
328
+ Cancel
329
+ </Button>
330
+ <Button
331
+ variant="destructive"
332
+ onClick={handleArchive}
333
+ disabled={isSubmitting}
334
+ >
335
+ {isSubmitting ? 'Archiving...' : 'Archive'}
336
+ </Button>
337
+ </DialogFooter>
338
+ </DialogContent>
339
+ </Dialog>
340
+ </>
341
+ );
342
+ }