@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,178 @@
1
+ import * as React from 'react';
2
+ import {
3
+ Button,
4
+ Card,
5
+ CardContent,
6
+ CardDescription,
7
+ CardHeader,
8
+ CardTitle,
9
+ } from '@vendure/dashboard';
10
+ import {
11
+ Activity,
12
+ AlertCircle,
13
+ AlertTriangle,
14
+ Clock,
15
+ RefreshCw,
16
+ TrendingUp,
17
+ } from 'lucide-react';
18
+ import {
19
+ useLogStats,
20
+ usePipelines,
21
+ useLoadMore,
22
+ } from '../../../hooks';
23
+ import { useOptionValues } from '../../../hooks/api/use-config-options';
24
+ import { ErrorState, LoadingState, StatCard, LoadMoreButton } from '../../../components/shared';
25
+ import { QUERY_LIMITS, UI_DEFAULTS } from '../../../constants';
26
+ import { LevelBadge } from './LogLevelBadge';
27
+ import type { DataHubPipeline } from '../../../types';
28
+
29
+ /**
30
+ * Pipeline statistics card showing log counts and performance metrics
31
+ */
32
+ const PipelineStatCard = React.memo(function PipelineStatCard({ pipeline }: { pipeline: Pick<DataHubPipeline, 'id' | 'code' | 'name'> }) {
33
+ const { data: stats, isLoading } = useLogStats(pipeline.id);
34
+
35
+ return (
36
+ <div className="border rounded-lg p-3">
37
+ <div className="flex items-center justify-between mb-2">
38
+ <div className="font-medium">{pipeline.name}</div>
39
+ <code className="text-xs text-muted-foreground">{pipeline.code}</code>
40
+ </div>
41
+ <div className="flex gap-3 text-sm">
42
+ <span className="text-muted-foreground">
43
+ <span className="font-medium text-foreground">{isLoading ? '\u2014' : (stats?.total ?? 0)}</span> logs
44
+ </span>
45
+ {!isLoading && (stats?.errorsToday ?? 0) > 0 && (
46
+ <span className="text-red-600">
47
+ {stats?.errorsToday} errors today
48
+ </span>
49
+ )}
50
+ {!isLoading && (stats?.avgDurationMs ?? 0) > 0 && (
51
+ <span className="text-muted-foreground">
52
+ avg {stats?.avgDurationMs}ms
53
+ </span>
54
+ )}
55
+ </div>
56
+ </div>
57
+ );
58
+ });
59
+
60
+ const PIPELINE_HEALTH_PAGE_SIZE = 6;
61
+
62
+ /**
63
+ * Overview tab displaying analytics dashboard with log statistics and pipeline health.
64
+ * Shows total logs, errors, warnings, average duration, and per-pipeline metrics.
65
+ */
66
+ export function LogsOverviewTab() {
67
+ const { options: logLevels } = useOptionValues('logLevels');
68
+ const statsQuery = useLogStats();
69
+ const pipelinesQuery = usePipelines({ take: QUERY_LIMITS.ALL_ITEMS });
70
+ const stats = statsQuery.data;
71
+ const pipelines = pipelinesQuery.data?.items ?? [];
72
+
73
+ const { displayed: displayedPipelines, hasMore: hasMorePipelines, remaining, loadMore } = useLoadMore(pipelines, { pageSize: PIPELINE_HEALTH_PAGE_SIZE });
74
+
75
+ const handleRefetch = React.useCallback(() => statsQuery.refetch(), [statsQuery.refetch]);
76
+
77
+ if (statsQuery.isError) {
78
+ return (
79
+ <ErrorState
80
+ title="Failed to load log statistics"
81
+ message={statsQuery.error?.message || 'An unexpected error occurred'}
82
+ onRetry={handleRefetch}
83
+ />
84
+ );
85
+ }
86
+
87
+ if (statsQuery.isLoading) {
88
+ return <LoadingState type="card" rows={2} message="Loading analytics..." />;
89
+ }
90
+
91
+ return (
92
+ <div className="space-y-6">
93
+ <Card>
94
+ <CardHeader className="pb-3">
95
+ <div className="flex items-center justify-between">
96
+ <div className="flex items-center gap-2">
97
+ <Activity className="w-5 h-5 text-primary" />
98
+ <CardTitle>Analytics Dashboard</CardTitle>
99
+ </div>
100
+ <Button variant="ghost" size="sm" onClick={handleRefetch} disabled={statsQuery.isLoading}>
101
+ <RefreshCw className={`w-4 h-4 mr-2 ${statsQuery.isLoading ? 'animate-spin' : ''}`} />
102
+ Refresh
103
+ </Button>
104
+ </div>
105
+ <CardDescription>
106
+ Pipeline execution metrics and log statistics
107
+ </CardDescription>
108
+ </CardHeader>
109
+ <CardContent>
110
+ <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
111
+ <StatCard
112
+ title="Total Logs"
113
+ value={stats?.total ?? 0}
114
+ icon={<TrendingUp className="w-4 h-4" />}
115
+ variant="info"
116
+ />
117
+ <StatCard
118
+ title="Errors Today"
119
+ value={stats?.errorsToday ?? 0}
120
+ icon={<AlertCircle className="w-4 h-4" />}
121
+ variant="error"
122
+ />
123
+ <StatCard
124
+ title="Warnings Today"
125
+ value={stats?.warningsToday ?? 0}
126
+ icon={<AlertTriangle className="w-4 h-4" />}
127
+ variant="warning"
128
+ />
129
+ <StatCard
130
+ title="Avg Duration"
131
+ value={`${stats?.avgDurationMs ?? 0}ms`}
132
+ icon={<Clock className="w-4 h-4" />}
133
+ variant="success"
134
+ />
135
+ <div className="border rounded-lg p-3 bg-muted/30">
136
+ <div className="text-xs text-muted-foreground mb-2">By Level</div>
137
+ <div className="flex gap-2">
138
+ {logLevels.map(level => (
139
+ <LevelBadge
140
+ key={level.value}
141
+ level={level.value}
142
+ count={(stats?.byLevel as Record<string, number> | undefined)?.[level.value] ?? 0}
143
+ />
144
+ ))}
145
+ </div>
146
+ </div>
147
+ </div>
148
+ </CardContent>
149
+ </Card>
150
+
151
+ <Card>
152
+ <CardHeader className="pb-3">
153
+ <div className="flex items-center justify-between">
154
+ <div>
155
+ <CardTitle className="text-base">Pipeline Health</CardTitle>
156
+ <CardDescription>
157
+ Log statistics for each pipeline ({pipelines.length} total)
158
+ </CardDescription>
159
+ </div>
160
+ </div>
161
+ </CardHeader>
162
+ <CardContent>
163
+ <div className="grid grid-cols-2 gap-4">
164
+ {displayedPipelines.map((p) => (
165
+ <PipelineStatCard key={p.id} pipeline={p} />
166
+ ))}
167
+ </div>
168
+ {hasMorePipelines && <LoadMoreButton remaining={remaining} onClick={loadMore} />}
169
+ {pipelines.length === 0 && (
170
+ <div className="text-center py-8 text-muted-foreground">
171
+ No pipelines found
172
+ </div>
173
+ )}
174
+ </CardContent>
175
+ </Card>
176
+ </div>
177
+ );
178
+ }
@@ -0,0 +1,122 @@
1
+ import * as React from 'react';
2
+ import {
3
+ Button,
4
+ Card,
5
+ CardContent,
6
+ CardDescription,
7
+ CardHeader,
8
+ CardTitle,
9
+ } from '@vendure/dashboard';
10
+ import {
11
+ RefreshCw,
12
+ Zap,
13
+ } from 'lucide-react';
14
+ import { useRecentLogs } from '../../../hooks';
15
+ import { ErrorState } from '../../../components/shared';
16
+ import { formatSmartDateTime } from '../../../utils';
17
+ import { LogLevelBadge } from './LogLevelBadge';
18
+ import { UI_LIMITS, SCROLL_HEIGHTS, POLLING_INTERVALS } from '../../../constants';
19
+
20
+ /**
21
+ * Real-time log feed tab that auto-refreshes every 3 seconds.
22
+ * Displays the latest 50 log entries across all pipelines.
23
+ */
24
+ export function RealtimeLogTab() {
25
+ const recentLogsQuery = useRecentLogs(UI_LIMITS.REALTIME_LOG_LIMIT);
26
+ const logs = recentLogsQuery.data ?? [];
27
+
28
+ const handleRefetch = React.useCallback(() => recentLogsQuery.refetch(), [recentLogsQuery.refetch]);
29
+
30
+ if (recentLogsQuery.isError) {
31
+ return (
32
+ <ErrorState
33
+ title="Failed to load real-time logs"
34
+ message={recentLogsQuery.error?.message || 'An unexpected error occurred'}
35
+ onRetry={handleRefetch}
36
+ />
37
+ );
38
+ }
39
+
40
+ return (
41
+ <Card>
42
+ <CardHeader className="pb-3">
43
+ <div className="flex items-center justify-between">
44
+ <div className="flex items-center gap-2">
45
+ <Zap className="w-5 h-5 text-primary" />
46
+ <CardTitle>Real-time Log Feed</CardTitle>
47
+ </div>
48
+ <div className="flex items-center gap-2">
49
+ <div
50
+ className={`w-2 h-2 rounded-full ${recentLogsQuery.isLoading ? 'bg-amber-500 animate-pulse' : 'bg-green-500'}`}
51
+ aria-label={recentLogsQuery.isLoading ? 'Loading logs' : 'Connected and receiving logs'}
52
+ role="status"
53
+ />
54
+ <span className="text-sm text-muted-foreground">
55
+ {recentLogsQuery.isLoading ? 'Loading...' : `Auto-refreshing every ${POLLING_INTERVALS.LIVE_LOGS / 1000}s`}
56
+ </span>
57
+ <Button variant="ghost" size="sm" onClick={handleRefetch} disabled={recentLogsQuery.isLoading} data-testid="datahub-realtime-log-refresh-button" aria-label="Refresh real-time logs">
58
+ <RefreshCw className={`w-4 h-4 ${recentLogsQuery.isLoading ? 'animate-spin' : ''}`} />
59
+ </Button>
60
+ </div>
61
+ </div>
62
+ <CardDescription>
63
+ Latest log entries across all pipelines
64
+ </CardDescription>
65
+ </CardHeader>
66
+ <CardContent>
67
+ <div className={`space-y-2 ${SCROLL_HEIGHTS.REALTIME_LOGS} overflow-y-auto`} role="list" aria-label="Real-time log entries">
68
+ {logs.map((log) => (
69
+ <div
70
+ key={log.id}
71
+ className="flex items-start gap-3 p-3 border rounded-lg hover:bg-muted/30"
72
+ role="listitem"
73
+ >
74
+ <LogLevelBadge level={log.level} />
75
+ <div className="flex-1 min-w-0">
76
+ <div className="flex items-center gap-2 mb-1">
77
+ <span className="font-mono text-xs text-muted-foreground">
78
+ {log.pipeline?.code ?? 'system'}
79
+ </span>
80
+ {log.stepKey && (
81
+ <>
82
+ <span className="text-muted-foreground">-&gt;</span>
83
+ <span className="font-mono text-xs text-muted-foreground">
84
+ {log.stepKey}
85
+ </span>
86
+ </>
87
+ )}
88
+ <span className="text-muted-foreground text-xs ml-auto">
89
+ {formatSmartDateTime(log.createdAt)}
90
+ </span>
91
+ </div>
92
+ <div className="text-sm">{log.message}</div>
93
+ {(log.recordsProcessed != null || log.durationMs != null) && (
94
+ <div className="flex gap-3 mt-1 text-xs text-muted-foreground">
95
+ {log.durationMs != null && (
96
+ <span>{log.durationMs}ms</span>
97
+ )}
98
+ {log.recordsProcessed != null && (
99
+ <span>
100
+ {log.recordsProcessed} records
101
+ {log.recordsFailed > 0 && (
102
+ <span className="text-red-600 ml-1">
103
+ ({log.recordsFailed} failed)
104
+ </span>
105
+ )}
106
+ </span>
107
+ )}
108
+ </div>
109
+ )}
110
+ </div>
111
+ </div>
112
+ ))}
113
+ {logs.length === 0 && (
114
+ <div className="text-center py-8 text-muted-foreground">
115
+ No recent logs. Pipeline activity will appear here.
116
+ </div>
117
+ )}
118
+ </div>
119
+ </CardContent>
120
+ </Card>
121
+ );
122
+ }
@@ -0,0 +1 @@
1
+ export { logsPage } from './Logs';
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+ import { Json } from '@vendure/dashboard';
3
+ import { useErrorAudits } from '../../hooks';
4
+ import { formatDateTime } from '../../utils';
5
+
6
+ export function ErrorAuditList({ errorId }: { errorId: string }) {
7
+ const { data: audits } = useErrorAudits(errorId);
8
+
9
+ if (!audits?.length) return null;
10
+
11
+ return (
12
+ <div className="mt-2 border rounded p-2">
13
+ <div className="text-xs font-medium mb-1">Retry audit trail</div>
14
+ <div className="space-y-2">
15
+ {audits.map(a => (
16
+ <div key={a.id} className="text-xs">
17
+ <div className="text-muted-foreground">
18
+ {formatDateTime(String(a.createdAt))} · user {a.userId ?? '—'}
19
+ </div>
20
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-2">
21
+ <div>
22
+ <div className="font-medium">Previous</div>
23
+ <Json value={a.previousPayload} />
24
+ </div>
25
+ <div>
26
+ <div className="font-medium">Patch</div>
27
+ <Json value={a.patch} />
28
+ </div>
29
+ <div>
30
+ <div className="font-medium">Resulting</div>
31
+ <Json value={a.resultingPayload} />
32
+ </div>
33
+ </div>
34
+ </div>
35
+ ))}
36
+ </div>
37
+ </div>
38
+ );
39
+ }
@@ -0,0 +1,96 @@
1
+ import * as React from 'react';
2
+ import {
3
+ DashboardRouteDefinition,
4
+ Page,
5
+ PageTitle,
6
+ PermissionGuard,
7
+ api,
8
+ } from '@vendure/dashboard';
9
+ import { useNavigate } from '@tanstack/react-router';
10
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
11
+ import { toast } from 'sonner';
12
+ import { DATAHUB_PERMISSIONS, ROUTES, TOAST_WIZARD } from '../../constants';
13
+ import { ExportWizard } from '../../components/wizards';
14
+ import type { ExportConfiguration } from '../../components/wizards';
15
+ import { createPipelineDocument, pipelineKeys, useAdapterCodeMappings, useAdaptersByType } from '../../hooks';
16
+ import { useDestinationSchemas, useTriggerTypeSchemas } from '../../hooks/api/use-config-options';
17
+ import { generatePipelineCode, exportConfigToPipelineDefinition } from '../../utils';
18
+ import type { AdapterResolver } from '../../utils/wizard-to-pipeline';
19
+ import { getErrorMessage } from '../../../shared';
20
+
21
+ export const exportWizardPage: DashboardRouteDefinition = {
22
+ path: `${ROUTES.PIPELINES}/export-wizard`,
23
+ loader: () => ({
24
+ breadcrumb: [
25
+ { path: ROUTES.PIPELINES, label: 'Data Hub' },
26
+ 'Export Wizard',
27
+ ],
28
+ }),
29
+ component: () => (
30
+ <PermissionGuard requires={[DATAHUB_PERMISSIONS.CREATE_PIPELINE]}>
31
+ <ExportWizardPageContent />
32
+ </PermissionGuard>
33
+ ),
34
+ };
35
+
36
+ function ExportWizardPageContent() {
37
+ const navigate = useNavigate();
38
+ const queryClient = useQueryClient();
39
+ const { mappings: exportMappings } = useAdapterCodeMappings('exportAdapterCodes');
40
+ const { mappings: feedMappings } = useAdapterCodeMappings('feedAdapterCodes');
41
+ const { data: exporterAdapters } = useAdaptersByType('EXPORTER');
42
+ const { data: feedAdapters } = useAdaptersByType('FEED');
43
+ const { schemas: destinationSchemas } = useDestinationSchemas();
44
+ const { schemas: triggerSchemas } = useTriggerTypeSchemas();
45
+
46
+ const resolver = React.useMemo<AdapterResolver>(() => ({
47
+ getLoaderAdapterCode: () => undefined,
48
+ getExportAdapterCode: (formatType) =>
49
+ exportMappings.find(m => m.value === formatType)?.adapterCode
50
+ ?? exporterAdapters?.find(a => a.formatType === formatType)?.code,
51
+ getFeedAdapterCode: (formatType) =>
52
+ feedMappings.find(m => m.value === formatType)?.adapterCode
53
+ ?? feedAdapters?.find(a => a.formatType === formatType)?.code,
54
+ }), [exportMappings, feedMappings, exporterAdapters, feedAdapters]);
55
+
56
+ const createMutation = useMutation({
57
+ mutationFn: (config: ExportConfiguration) => {
58
+ const definition = exportConfigToPipelineDefinition(config, resolver, triggerSchemas, destinationSchemas);
59
+ return api.mutate(createPipelineDocument, {
60
+ input: {
61
+ code: generatePipelineCode(config.name),
62
+ name: config.name,
63
+ definition,
64
+ enabled: false,
65
+ },
66
+ }).then(res => res.createDataHubPipeline);
67
+ },
68
+ onSuccess: async (data) => {
69
+ await queryClient.invalidateQueries({ queryKey: pipelineKeys.lists() });
70
+ toast.success(TOAST_WIZARD.EXPORT_CREATED);
71
+ void navigate({ to: `${ROUTES.PIPELINES}/${data.id}` });
72
+ },
73
+ onError: (err) => {
74
+ toast.error(TOAST_WIZARD.CREATE_FAILED, {
75
+ description: getErrorMessage(err),
76
+ });
77
+ },
78
+ });
79
+
80
+ const handleComplete = React.useCallback((config: ExportConfiguration) => {
81
+ createMutation.mutate(config);
82
+ }, [createMutation.mutate]);
83
+
84
+ const handleCancel = React.useCallback(() => {
85
+ void navigate({ to: ROUTES.PIPELINES });
86
+ }, [navigate]);
87
+
88
+ return (
89
+ <Page>
90
+ <PageTitle title="Export Wizard" />
91
+ <div className="p-6">
92
+ <ExportWizard onComplete={handleComplete} onCancel={handleCancel} isSubmitting={createMutation.isPending} />
93
+ </div>
94
+ </Page>
95
+ );
96
+ }
@@ -0,0 +1,104 @@
1
+ import * as React from 'react';
2
+ import {
3
+ DashboardRouteDefinition,
4
+ Page,
5
+ PageTitle,
6
+ PermissionGuard,
7
+ api,
8
+ } from '@vendure/dashboard';
9
+ import { useNavigate } from '@tanstack/react-router';
10
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
11
+ import { toast } from 'sonner';
12
+ import { DATAHUB_PERMISSIONS, ROUTES, TOAST_WIZARD } from '../../constants';
13
+ import { ImportWizard } from '../../components/wizards';
14
+ import type { ImportConfiguration } from '../../components/wizards';
15
+ import { createPipelineDocument, pipelineKeys, useAdapterCodeMappings, useAdaptersByType, useWizardStrategyMappings } from '../../hooks';
16
+ import { useEntityLoaders } from '../../hooks/api/use-entity-loaders';
17
+ import { useTriggerTypeSchemas } from '../../hooks/api/use-config-options';
18
+ import { generatePipelineCode, importConfigToPipelineDefinition } from '../../utils';
19
+ import type { AdapterResolver, LoaderAdapterInfo } from '../../utils/wizard-to-pipeline';
20
+ import { getErrorMessage } from '../../../shared';
21
+
22
+ export const importWizardPage: DashboardRouteDefinition = {
23
+ path: `${ROUTES.PIPELINES}/import-wizard`,
24
+ loader: () => ({
25
+ breadcrumb: [
26
+ { path: ROUTES.PIPELINES, label: 'Data Hub' },
27
+ 'Import Wizard',
28
+ ],
29
+ }),
30
+ component: () => (
31
+ <PermissionGuard requires={[DATAHUB_PERMISSIONS.CREATE_PIPELINE]}>
32
+ <ImportWizardPageContent />
33
+ </PermissionGuard>
34
+ ),
35
+ };
36
+
37
+ function ImportWizardPageContent() {
38
+ const navigate = useNavigate();
39
+ const queryClient = useQueryClient();
40
+ const { getLoaderAdapterCode } = useEntityLoaders();
41
+ const { data: loaderAdapters } = useAdaptersByType('LOADER');
42
+ const { mappings: exportMappings } = useAdapterCodeMappings('exportAdapterCodes');
43
+ const { mappings: feedMappings } = useAdapterCodeMappings('feedAdapterCodes');
44
+ const { mappings: strategyMappings } = useWizardStrategyMappings();
45
+ const { schemas: triggerSchemas } = useTriggerTypeSchemas();
46
+
47
+ const resolver = React.useMemo<AdapterResolver>(() => ({
48
+ getLoaderAdapterCode,
49
+ getExportAdapterCode: (formatType) =>
50
+ exportMappings.find(m => m.value === formatType)?.adapterCode,
51
+ getFeedAdapterCode: (formatType) =>
52
+ feedMappings.find(m => m.value === formatType)?.adapterCode,
53
+ }), [getLoaderAdapterCode, exportMappings, feedMappings]);
54
+
55
+ const loaderAdapterInfos = React.useMemo<LoaderAdapterInfo[] | undefined>(
56
+ () => loaderAdapters?.map(a => ({
57
+ code: a.code,
58
+ entityType: a.entityType,
59
+ schema: a.schema ? { fields: a.schema.fields.map(f => ({ key: f.key })) } : undefined,
60
+ })),
61
+ [loaderAdapters],
62
+ );
63
+
64
+ const createMutation = useMutation({
65
+ mutationFn: (config: ImportConfiguration) => {
66
+ const definition = importConfigToPipelineDefinition(config, resolver, loaderAdapterInfos, triggerSchemas, strategyMappings);
67
+ return api.mutate(createPipelineDocument, {
68
+ input: {
69
+ code: generatePipelineCode(config.name),
70
+ name: config.name,
71
+ definition,
72
+ enabled: false,
73
+ },
74
+ }).then(res => res.createDataHubPipeline);
75
+ },
76
+ onSuccess: async (data) => {
77
+ await queryClient.invalidateQueries({ queryKey: pipelineKeys.lists() });
78
+ toast.success(TOAST_WIZARD.IMPORT_CREATED);
79
+ void navigate({ to: `${ROUTES.PIPELINES}/${data.id}` });
80
+ },
81
+ onError: (err) => {
82
+ toast.error(TOAST_WIZARD.CREATE_FAILED, {
83
+ description: getErrorMessage(err),
84
+ });
85
+ },
86
+ });
87
+
88
+ const handleComplete = React.useCallback((config: ImportConfiguration) => {
89
+ createMutation.mutate(config);
90
+ }, [createMutation.mutate]);
91
+
92
+ const handleCancel = React.useCallback(() => {
93
+ void navigate({ to: ROUTES.PIPELINES });
94
+ }, [navigate]);
95
+
96
+ return (
97
+ <Page>
98
+ <PageTitle title="Import Wizard" />
99
+ <div className="p-6">
100
+ <ImportWizard onComplete={handleComplete} onCancel={handleCancel} isSubmitting={createMutation.isPending} />
101
+ </div>
102
+ </Page>
103
+ );
104
+ }