@kb-labs/studio-app 0.7.0 → 2.9.0

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 (175) hide show
  1. package/dist/index.html +1 -0
  2. package/dist/main.80b70197.css +2 -0
  3. package/dist/main.80b70197.css.map +1 -0
  4. package/dist/main.ad42ef4a.js +522 -0
  5. package/dist/main.ad42ef4a.js.map +1 -0
  6. package/dist/mf-manifest.json +241 -0
  7. package/dist/mf-stats.json +271 -0
  8. package/package.json +56 -39
  9. package/server.js +68 -0
  10. package/.env.example +0 -10
  11. package/UI-KIT-COMPONENTS.md +0 -783
  12. package/UI-KIT-PLAN.md +0 -326
  13. package/eslint-rules/no-direct-antd-import.js +0 -151
  14. package/eslint.config.ts +0 -27
  15. package/index.html +0 -17
  16. package/postcss.config.js +0 -7
  17. package/public/favicon.ico +0 -0
  18. package/rspack.config.mjs +0 -118
  19. package/src/App.tsx +0 -46
  20. package/src/components/devtools/devtools-log-feed.module.css +0 -165
  21. package/src/components/devtools/devtools-log-feed.tsx +0 -300
  22. package/src/components/devtools/devtools-panel.module.css +0 -121
  23. package/src/components/devtools/devtools-panel.tsx +0 -115
  24. package/src/components/empty-state.tsx +0 -28
  25. package/src/components/error-boundary.tsx +0 -210
  26. package/src/components/health-banner.module.css +0 -103
  27. package/src/components/health-banner.tsx +0 -134
  28. package/src/components/health-indicator.tsx +0 -12
  29. package/src/components/page-header.tsx +0 -49
  30. package/src/components/page-transition.tsx +0 -24
  31. package/src/components/role-switcher.tsx +0 -148
  32. package/src/components/run-badge.tsx +0 -13
  33. package/src/components/ui/icons.tsx +0 -509
  34. package/src/components/ui/index.ts +0 -25
  35. package/src/components/ui/kb-config-provider.tsx +0 -130
  36. package/src/components/ui/kb-header.module.css +0 -51
  37. package/src/components/ui/kb-header.tsx +0 -112
  38. package/src/components/ui/kb-notification-bell.module.css +0 -103
  39. package/src/components/ui/kb-notification-bell.tsx +0 -199
  40. package/src/components/ui/kb-page-layout.tsx +0 -66
  41. package/src/components/ui/kb-quick-search.module.css +0 -86
  42. package/src/components/ui/kb-quick-search.tsx +0 -196
  43. package/src/components/ui/kb-sidebar.module.css +0 -47
  44. package/src/components/ui/kb-sidebar.tsx +0 -196
  45. package/src/components/ui/kb-stat-card.module.css +0 -21
  46. package/src/components/ui/kb-stat-card.tsx +0 -22
  47. package/src/components/ui/kb-stat-grid.tsx +0 -22
  48. package/src/components/ui/kb-status-bar.module.css +0 -49
  49. package/src/components/ui/kb-status-bar.tsx +0 -145
  50. package/src/components/ui/kb-system-health-indicator.tsx +0 -229
  51. package/src/components/ui/kb-theme-toggle.tsx +0 -42
  52. package/src/components/ui/theme-adapter.ts +0 -812
  53. package/src/components/ui/theme-transition-overlay.tsx +0 -70
  54. package/src/components/ui/utils.ts +0 -6
  55. package/src/components/widget-error-boundary.tsx +0 -172
  56. package/src/config/env.ts +0 -5
  57. package/src/config/feature-flags.ts +0 -482
  58. package/src/config/studio.config.schema.ts +0 -88
  59. package/src/config/studio.config.ts +0 -84
  60. package/src/env.d.ts +0 -23
  61. package/src/hooks/use-command-palette.ts +0 -6
  62. package/src/hooks/use-devtools-store.ts +0 -25
  63. package/src/hooks/use-feature-flags.ts +0 -101
  64. package/src/index.css +0 -10
  65. package/src/layouts/dashboard-grid.tsx +0 -122
  66. package/src/layouts/index.ts +0 -12
  67. package/src/layouts/two-pane.tsx +0 -134
  68. package/src/main.tsx +0 -15
  69. package/src/modules/analytics/components/analytics-daily-stats-chart.tsx +0 -257
  70. package/src/modules/analytics/components/analytics-date-range-picker.tsx +0 -121
  71. package/src/modules/analytics/pages/analytics-cache-page.tsx +0 -285
  72. package/src/modules/analytics/pages/analytics-embeddings-page.tsx +0 -274
  73. package/src/modules/analytics/pages/analytics-events-page.tsx +0 -425
  74. package/src/modules/analytics/pages/analytics-llm-page.tsx +0 -434
  75. package/src/modules/analytics/pages/analytics-overview-page.tsx +0 -336
  76. package/src/modules/analytics/pages/analytics-page.tsx +0 -20
  77. package/src/modules/analytics/pages/analytics-storage-page.tsx +0 -344
  78. package/src/modules/analytics/pages/analytics-vectorstore-page.tsx +0 -279
  79. package/src/modules/analytics/routes/index.tsx +0 -133
  80. package/src/modules/analytics/utils/formatters.test.ts +0 -148
  81. package/src/modules/analytics/utils/formatters.ts +0 -100
  82. package/src/modules/auth/pages/login-page.module.css +0 -113
  83. package/src/modules/auth/pages/login-page.tsx +0 -80
  84. package/src/modules/auth/routes/index.tsx +0 -24
  85. package/src/modules/dashboard/components/ActivityTimelineWidget.tsx +0 -199
  86. package/src/modules/dashboard/components/AiInsightsWidget.tsx +0 -358
  87. package/src/modules/dashboard/components/AnimatedMetricCard.css +0 -88
  88. package/src/modules/dashboard/components/AnimatedMetricCard.tsx +0 -125
  89. package/src/modules/dashboard/components/HeroMetricCard.css +0 -114
  90. package/src/modules/dashboard/components/HeroMetricCard.tsx +0 -152
  91. package/src/modules/dashboard/components/HeroMetricsWidget.tsx +0 -146
  92. package/src/modules/dashboard/components/SystemResourcesWidget.tsx +0 -197
  93. package/src/modules/dashboard/components/dashboard-empty.tsx +0 -22
  94. package/src/modules/dashboard/components/dashboard-kpis-widget.tsx +0 -59
  95. package/src/modules/dashboard/components/dashboard-kpis.tsx +0 -13
  96. package/src/modules/dashboard/components/dashboard-latency-percentiles-widget.tsx +0 -85
  97. package/src/modules/dashboard/components/dashboard-metrics-widget.tsx +0 -78
  98. package/src/modules/dashboard/components/dashboard-plugins-widget.tsx +0 -67
  99. package/src/modules/dashboard/components/dashboard-requests-widget.tsx +0 -62
  100. package/src/modules/dashboard/layouts/dashboard-layout.tsx +0 -16
  101. package/src/modules/dashboard/pages/ai-insights-page.tsx +0 -563
  102. package/src/modules/dashboard/pages/dashboard-animations.css +0 -317
  103. package/src/modules/dashboard/pages/dashboard-page.tsx +0 -13
  104. package/src/modules/dashboard/routes/index.tsx +0 -70
  105. package/src/modules/observability/pages/devkit-page.tsx +0 -263
  106. package/src/modules/observability/pages/incident-detail-page.tsx +0 -571
  107. package/src/modules/observability/pages/incidents-page.tsx +0 -370
  108. package/src/modules/observability/pages/log-detail-page.tsx +0 -508
  109. package/src/modules/observability/pages/logs-page.tsx +0 -1129
  110. package/src/modules/observability/pages/prometheus-metrics-page.tsx +0 -486
  111. package/src/modules/observability/pages/state-broker-page.tsx +0 -229
  112. package/src/modules/observability/pages/system-events-page.tsx +0 -255
  113. package/src/modules/observability/routes/index.tsx +0 -133
  114. package/src/modules/plugins/components/marketplace-card.tsx +0 -153
  115. package/src/modules/plugins/components/plugin-ai-assistant-modal.tsx +0 -374
  116. package/src/modules/plugins/components/plugin-card.tsx +0 -136
  117. package/src/modules/plugins/components/plugin-detail/capabilities-summary.tsx +0 -101
  118. package/src/modules/plugins/components/plugin-detail/cli-commands-table.tsx +0 -101
  119. package/src/modules/plugins/components/plugin-detail/index.ts +0 -7
  120. package/src/modules/plugins/components/plugin-detail/jobs-table.tsx +0 -64
  121. package/src/modules/plugins/components/plugin-detail/permissions/env-permissions.tsx +0 -30
  122. package/src/modules/plugins/components/plugin-detail/permissions/filesystem-permissions.tsx +0 -68
  123. package/src/modules/plugins/components/plugin-detail/permissions/index.ts +0 -6
  124. package/src/modules/plugins/components/plugin-detail/permissions/network-permissions.tsx +0 -43
  125. package/src/modules/plugins/components/plugin-detail/permissions/platform-services.tsx +0 -242
  126. package/src/modules/plugins/components/plugin-detail/permissions/resource-quotas.tsx +0 -96
  127. package/src/modules/plugins/components/plugin-detail/permissions/shell-permissions.tsx +0 -55
  128. package/src/modules/plugins/components/plugin-detail/permissions-view.tsx +0 -47
  129. package/src/modules/plugins/components/plugin-detail/plugin-header.tsx +0 -169
  130. package/src/modules/plugins/components/plugin-detail/rest-routes-table.tsx +0 -159
  131. package/src/modules/plugins/components/plugin-detail/workflows-table.tsx +0 -40
  132. package/src/modules/plugins/components/plugin-grid.tsx +0 -20
  133. package/src/modules/plugins/components/plugin-table.tsx +0 -178
  134. package/src/modules/plugins/pages/marketplace-page.tsx +0 -200
  135. package/src/modules/plugins/pages/plugin-detail-page.tsx +0 -1145
  136. package/src/modules/plugins/routes/index.tsx +0 -48
  137. package/src/modules/plugins/utils/index.ts +0 -1
  138. package/src/modules/plugins/utils/schema-helpers.ts +0 -21
  139. package/src/modules/settings/components/api-routes-viewer.tsx +0 -415
  140. package/src/modules/settings/components/appearance-settings.tsx +0 -200
  141. package/src/modules/settings/components/configuration-settings.tsx +0 -321
  142. package/src/modules/settings/components/data-privacy-settings.tsx +0 -111
  143. package/src/modules/settings/components/experimental-settings.tsx +0 -243
  144. package/src/modules/settings/components/navigation-settings.tsx +0 -198
  145. package/src/modules/settings/components/notifications-settings.tsx +0 -40
  146. package/src/modules/settings/pages/settings-page.tsx +0 -298
  147. package/src/modules/settings/routes/index.tsx +0 -47
  148. package/src/pages/README-assistant.md +0 -91
  149. package/src/pages/login-page.tsx +0 -177
  150. package/src/pages/not-found-page.tsx +0 -85
  151. package/src/providers/auth-provider.tsx +0 -40
  152. package/src/providers/command-palette-provider.tsx +0 -193
  153. package/src/providers/data-sources-provider.tsx +0 -27
  154. package/src/providers/devtools-provider.tsx +0 -72
  155. package/src/providers/navigation-items-provider.tsx +0 -38
  156. package/src/providers/registry-v2-provider.tsx +0 -93
  157. package/src/providers/settings-provider.tsx +0 -158
  158. package/src/router.tsx +0 -302
  159. package/src/routes/helpers.tsx +0 -25
  160. package/src/routes/plugin-page-v2.tsx +0 -52
  161. package/src/routes/types.ts +0 -18
  162. package/src/styles/antd-overrides.css +0 -211
  163. package/src/styles/base.css +0 -190
  164. package/src/styles/settings.css +0 -75
  165. package/src/styles/variables.css +0 -94
  166. package/src/utils/analytics.ts +0 -183
  167. package/src/utils/apply-navigation-categories.ts +0 -83
  168. package/src/utils/commands.ts +0 -252
  169. package/src/utils/export.ts +0 -174
  170. package/src/utils/format-date.ts +0 -166
  171. package/src/utils/logger.ts +0 -110
  172. package/tailwind.config.ts +0 -13
  173. package/tsconfig.json +0 -13
  174. package/vitest-setup.ts +0 -1
  175. /package/{public → dist}/favicon.svg +0 -0
@@ -1,571 +0,0 @@
1
- import { useState } from 'react';
2
- import { useParams, useNavigate } from 'react-router-dom';
3
- import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
4
- import {
5
- UICard,
6
- UIAlert,
7
- UIBadge,
8
- UITag,
9
- UIButton,
10
- UISpace,
11
- UIDescriptions,
12
- UIDescriptionsItem,
13
- UITimeline,
14
- UITimelineItem,
15
- UIProgress,
16
- UIModal, UIInputTextArea,
17
- UIMessage,
18
- UISpin,
19
- UIDivider,
20
- UIRow,
21
- UICol,
22
- UIStatistic,
23
- UIEmptyState,
24
- UITable,
25
- UITooltip,
26
- UIIcon,
27
- } from '@kb-labs/studio-ui-kit';
28
- import type {
29
- IncidentDetailResponse,
30
- IncidentAnalysisResponse,
31
- IncidentSeverity,
32
- IncidentType,
33
- RootCauseItem,
34
- } from '@kb-labs/studio-data-client';
35
- import { useDataSources } from '../../../providers/data-sources-provider';
36
- import dayjs from 'dayjs';
37
- import relativeTime from 'dayjs/plugin/relativeTime';
38
- import { UIPage, UIPageHeader } from '@kb-labs/studio-ui-kit';
39
-
40
- dayjs.extend(relativeTime);
41
-
42
- /**
43
- * Format timestamp to human-readable string
44
- */
45
- function formatTime(timestamp: number): string {
46
- return dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss');
47
- }
48
-
49
- /**
50
- * Get severity icon and color
51
- */
52
- function getSeverityConfig(severity: IncidentSeverity): { icon: React.ReactElement; color: string } {
53
- switch (severity) {
54
- case 'critical':
55
- return { icon: <UIIcon name="FireOutlined" />, color: '#ff4d4f' };
56
- case 'warning':
57
- return { icon: <UIIcon name="WarningOutlined" />, color: '#faad14' };
58
- case 'info':
59
- return { icon: <UIIcon name="InfoCircleOutlined" />, color: '#1890ff' };
60
- }
61
- }
62
-
63
- /**
64
- * Get incident type label
65
- */
66
- function getTypeLabel(type: IncidentType): string {
67
- switch (type) {
68
- case 'error_rate':
69
- return 'Error Rate';
70
- case 'latency_spike':
71
- return 'Latency Spike';
72
- case 'plugin_failure':
73
- return 'Plugin Failure';
74
- case 'adapter_failure':
75
- return 'Adapter Failure';
76
- case 'system_health':
77
- return 'System Health';
78
- case 'custom':
79
- return 'Custom';
80
- }
81
- }
82
-
83
- /**
84
- * Incident Detail Page
85
- *
86
- * Shows detailed information about an incident including AI analysis
87
- */
88
- export function IncidentDetailPage() {
89
- const { id } = useParams<{ id: string }>();
90
- const navigate = useNavigate();
91
- const sources = useDataSources();
92
- const queryClient = useQueryClient();
93
-
94
- const [resolveModalVisible, setResolveModalVisible] = useState(false);
95
- const [resolutionNotes, setResolutionNotes] = useState('');
96
-
97
- // Fetch incident details
98
- const {
99
- data: incidentData,
100
- isLoading: incidentLoading,
101
- error: incidentError,
102
- } = useQuery<IncidentDetailResponse>({
103
- queryKey: ['incidents', 'detail', id],
104
- queryFn: async () => {
105
- if (!id) {throw new Error('Incident ID is required');}
106
- return sources.observability.getIncident(id);
107
- },
108
- enabled: !!id,
109
- });
110
-
111
- const incident = incidentData?.data;
112
-
113
- // Fetch or trigger AI analysis
114
- const {
115
- data: analysisData,
116
- isLoading: analysisLoading,
117
- error: analysisError,
118
- refetch: triggerAnalysis,
119
- } = useQuery<IncidentAnalysisResponse>({
120
- queryKey: ['incidents', 'analysis', id],
121
- queryFn: async () => {
122
- if (!id) {throw new Error('Incident ID is required');}
123
- return sources.observability.analyzeIncident(id);
124
- },
125
- enabled: false, // Manual trigger
126
- });
127
-
128
- const analysis = analysisData?.data;
129
-
130
- // Resolve incident mutation
131
- const resolveMutation = useMutation({
132
- mutationFn: async () => {
133
- if (!id) {throw new Error('Incident ID is required');}
134
- return sources.observability.resolveIncident(id, resolutionNotes);
135
- },
136
- onSuccess: () => {
137
- UIMessage.success('Incident resolved successfully');
138
- setResolveModalVisible(false);
139
- setResolutionNotes('');
140
- queryClient.invalidateQueries({ queryKey: ['incidents'] });
141
- },
142
- onError: (error: Error) => {
143
- UIMessage.error(`Failed to resolve incident: ${error.message}`);
144
- },
145
- });
146
-
147
- const handleResolve = () => {
148
- resolveMutation.mutate();
149
- };
150
-
151
- const handleAnalyze = () => {
152
- triggerAnalysis();
153
- };
154
-
155
- const handleGoBack = () => {
156
- navigate('/observability/incidents');
157
- };
158
-
159
- if (incidentLoading) {
160
- return (
161
- <UIPage>
162
- <div style={{ textAlign: 'center', padding: '100px 0' }}>
163
- <UISpin size="large" />
164
- </div>
165
- </UIPage>
166
- );
167
- }
168
-
169
- if (incidentError || !incident) {
170
- return (
171
- <UIPage>
172
- <UIAlert
173
- message="Failed to load incident"
174
- description={(incidentError as Error)?.message ?? 'Incident not found'}
175
- variant="error"
176
- showIcon
177
- />
178
- </UIPage>
179
- );
180
- }
181
-
182
- const severityConfig = getSeverityConfig(incident.severity);
183
-
184
- return (
185
- <UIPage>
186
- <UIPageHeader
187
- title={incident.title}
188
- description={`Incident ID: ${incident.id}`}
189
- actions={[
190
- <UIButton key="back" icon={<UIIcon name="ArrowLeftOutlined" />} onClick={handleGoBack} />,
191
- !incident.resolvedAt && (
192
- <UIButton
193
- key="resolve"
194
- variant="primary"
195
- icon={<UIIcon name="CheckCircleOutlined" />}
196
- onClick={() => setResolveModalVisible(true)}
197
- >
198
- Resolve
199
- </UIButton>
200
- ),
201
- <UIButton
202
- key="analyze"
203
- icon={<UIIcon name="ThunderboltOutlined" />}
204
- onClick={handleAnalyze}
205
- loading={analysisLoading}
206
- disabled={!!analysis && !analysisData?.data.cached}
207
- >
208
- {analysis ? 'Re-analyze' : 'Analyze with AI'}
209
- </UIButton>,
210
- ].filter(Boolean)}
211
- />
212
-
213
- {/* Overview */}
214
- <UICard title="Overview" style={{ marginBottom: 16 }}>
215
- <UIDescriptions column={2} bordered>
216
- <UIDescriptionsItem label="Severity">
217
- <UITag color={severityConfig.color} icon={severityConfig.icon}>
218
- {incident.severity.toUpperCase()}
219
- </UITag>
220
- </UIDescriptionsItem>
221
- <UIDescriptionsItem label="Type">
222
- <UITag>{getTypeLabel(incident.type)}</UITag>
223
- </UIDescriptionsItem>
224
- <UIDescriptionsItem label="Time">
225
- {formatTime(incident.timestamp)}
226
- <span style={{ marginLeft: 8, color: '#888' }}>
227
- ({dayjs(incident.timestamp).fromNow()})
228
- </span>
229
- </UIDescriptionsItem>
230
- <UIDescriptionsItem label="Status">
231
- {incident.resolvedAt ? (
232
- <UITag color="success" icon={<UIIcon name="CheckCircleOutlined" />}>
233
- RESOLVED
234
- </UITag>
235
- ) : (
236
- <UITag color="error" icon={<UIIcon name="ClockCircleOutlined" />}>
237
- ACTIVE
238
- </UITag>
239
- )}
240
- </UIDescriptionsItem>
241
- {incident.resolvedAt && (
242
- <UIDescriptionsItem label="Resolved At">
243
- {formatTime(incident.resolvedAt)}
244
- <span style={{ marginLeft: 8, color: '#888' }}>
245
- (Duration: {Math.floor((incident.resolvedAt - incident.timestamp) / 60000)}m)
246
- </span>
247
- </UIDescriptionsItem>
248
- )}
249
- {incident.affectedServices && incident.affectedServices.length > 0 && (
250
- <UIDescriptionsItem label="Affected Services" span={2}>
251
- <UISpace wrap>
252
- {incident.affectedServices.map((service) => (
253
- <UITag key={service}>{service}</UITag>
254
- ))}
255
- </UISpace>
256
- </UIDescriptionsItem>
257
- )}
258
- <UIDescriptionsItem label="Details" span={2}>
259
- {incident.details}
260
- </UIDescriptionsItem>
261
- {incident.resolutionNotes && (
262
- <UIDescriptionsItem label="Resolution Notes" span={2}>
263
- {incident.resolutionNotes}
264
- </UIDescriptionsItem>
265
- )}
266
- </UIDescriptions>
267
- </UICard>
268
-
269
- {/* Related Data - Slow Requests (for latency incidents) */}
270
- {incident.type === 'latency_spike' && incident.relatedData?.metrics?.topSlowest && incident.relatedData.metrics.topSlowest.length > 0 && (
271
- <UICard title="Slowest Requests" style={{ marginBottom: 16 }}>
272
- <UITable
273
- dataSource={incident.relatedData.metrics.topSlowest}
274
- columns={[
275
- {
276
- title: 'Method',
277
- dataIndex: 'method',
278
- key: 'method',
279
- width: 80,
280
- render: (method: string) => <UITag color="blue">{method}</UITag>,
281
- },
282
- {
283
- title: 'Endpoint',
284
- dataIndex: 'endpoint',
285
- key: 'endpoint',
286
- render: (endpoint: string) => <code>{endpoint}</code>,
287
- },
288
- {
289
- title: 'Duration',
290
- dataIndex: 'durationMs',
291
- key: 'durationMs',
292
- width: 120,
293
- sorter: (a: any, b: any) => b.durationMs - a.durationMs,
294
- render: (ms: number) => (
295
- <span style={{ color: ms > 1000 ? '#ff4d4f' : ms > 500 ? '#faad14' : undefined }}>
296
- {ms.toLocaleString()}ms
297
- </span>
298
- ),
299
- },
300
- {
301
- title: 'Status',
302
- dataIndex: 'statusCode',
303
- key: 'statusCode',
304
- width: 80,
305
- render: (code?: number) => (
306
- code ? (
307
- <UITag color={code >= 500 ? 'error' : code >= 400 ? 'warning' : 'success'}>
308
- {code}
309
- </UITag>
310
- ) : '-'
311
- ),
312
- },
313
- ]}
314
- pagination={false}
315
- size="small"
316
- rowKey={(record: { endpoint: string; method: string; durationMs: number; statusCode?: number }) => record.endpoint}
317
- />
318
- </UICard>
319
- )}
320
-
321
- {/* Related Data - Metrics Comparison */}
322
- {incident.relatedData?.metrics && (
323
- <UICard title={<><UIIcon name="LineChartOutlined" /> Metrics Comparison</>} style={{ marginBottom: 16 }}>
324
- <UIRow gutter={16}>
325
- {incident.relatedData.metrics.before && (
326
- <UICol span={12}>
327
- <UICard type="inner" title="Before Incident">
328
- {Object.entries(incident.relatedData.metrics.before).map(([key, value]) => (
329
- <UIStatistic
330
- key={key}
331
- title={key}
332
- value={typeof value === 'number' ? value.toFixed(2) : value}
333
- style={{ marginBottom: 8 }}
334
- />
335
- ))}
336
- </UICard>
337
- </UICol>
338
- )}
339
- {incident.relatedData.metrics.during && (
340
- <UICol span={12}>
341
- <UICard type="inner" title="During Incident">
342
- {Object.entries(incident.relatedData.metrics.during).map(([key, value]) => (
343
- <UIStatistic
344
- key={key}
345
- title={key}
346
- value={typeof value === 'number' ? value.toFixed(2) : value}
347
- valueStyle={{
348
- color:
349
- incident.relatedData?.metrics?.before?.[key] &&
350
- typeof value === 'number' &&
351
- value > incident.relatedData.metrics.before[key]
352
- ? '#ff4d4f'
353
- : undefined,
354
- }}
355
- style={{ marginBottom: 8 }}
356
- />
357
- ))}
358
- </UICard>
359
- </UICol>
360
- )}
361
- </UIRow>
362
- </UICard>
363
- )}
364
-
365
- {/* Related Data - Timeline */}
366
- {incident.relatedData?.timeline && incident.relatedData.timeline.length > 0 && (
367
- <UICard title="Event Timeline" style={{ marginBottom: 16 }}>
368
- <UITimeline mode="left">
369
- {incident.relatedData.timeline.map((event, idx) => (
370
- <UITimelineItem
371
- key={idx}
372
- color={event.source === 'detector' ? 'red' : event.source === 'logs' ? 'orange' : 'blue'}
373
- label={formatTime(event.timestamp)}
374
- >
375
- <UIBadge
376
- variant={event.source === 'detector' ? 'error' : 'info'}
377
- />{event.source.toUpperCase()}
378
- <div style={{ marginTop: 4 }}>{event.event}</div>
379
- </UITimelineItem>
380
- ))}
381
- </UITimeline>
382
- </UICard>
383
- )}
384
-
385
- {/* Related Data - Error Breakdown (top endpoints with errors) */}
386
- {incident.relatedData?.logs?.topEndpoints && incident.relatedData.logs.topEndpoints.length > 0 && (
387
- <UICard title="Error Breakdown by Endpoint" style={{ marginBottom: 16 }}>
388
- <UITable
389
- dataSource={incident.relatedData.logs.topEndpoints}
390
- columns={[
391
- {
392
- title: 'Endpoint',
393
- dataIndex: 'endpoint',
394
- key: 'endpoint',
395
- render: (endpoint: string) => <code>{endpoint}</code>,
396
- },
397
- {
398
- title: 'Error Count',
399
- dataIndex: 'count',
400
- key: 'count',
401
- width: 120,
402
- sorter: (a: any, b: any) => b.count - a.count,
403
- render: (count: number) => (
404
- <UIBadge count={count} style={{ backgroundColor: '#ff4d4f' }} />
405
- ),
406
- },
407
- {
408
- title: 'Sample Error',
409
- dataIndex: 'sample',
410
- key: 'sample',
411
- ellipsis: true,
412
- render: (sample: string) => (
413
- <UITooltip title={sample}>
414
- <span style={{ color: '#666' }}>{sample}</span>
415
- </UITooltip>
416
- ),
417
- },
418
- ]}
419
- pagination={false}
420
- size="small"
421
- rowKey={(record: { endpoint: string; count: number; sample: string }) => record.endpoint}
422
- />
423
- </UICard>
424
- )}
425
-
426
- {/* Related Data - Error Logs */}
427
- {incident.relatedData?.logs && incident.relatedData.logs.sampleErrors.length > 0 && (
428
- <UICard title={`Error Logs (${incident.relatedData.logs.errorCount} total)`} style={{ marginBottom: 16 }}>
429
- <UISpace direction="vertical" style={{ width: '100%' }}>
430
- {incident.relatedData.logs.sampleErrors.map((error, idx) => (
431
- <UIAlert
432
- key={idx}
433
- message={`Error ${idx + 1}`}
434
- description={<pre style={{ margin: 0, whiteSpace: 'pre-wrap', fontSize: 12 }}>{error}</pre>}
435
- variant="error"
436
- showIcon
437
- />
438
- ))}
439
- </UISpace>
440
- </UICard>
441
- )}
442
-
443
- {/* AI Analysis */}
444
- {analysisLoading && (
445
- <UICard title={<><UIIcon name="ThunderboltOutlined" /> AI Analysis</>} style={{ marginBottom: 16 }}>
446
- <div style={{ textAlign: 'center', padding: '40px 0' }}>
447
- <UISpin size="large" tip="Analyzing incident with AI..." />
448
- </div>
449
- </UICard>
450
- )}
451
-
452
- {analysisError && (
453
- <UICard title={<><UIIcon name="ThunderboltOutlined" /> AI Analysis</>} style={{ marginBottom: 16 }}>
454
- <UIAlert
455
- message="Analysis Failed"
456
- description={(analysisError as Error).message}
457
- variant="error"
458
- showIcon
459
- />
460
- </UICard>
461
- )}
462
-
463
- {analysis && !analysisLoading && (
464
- <UICard
465
- title={<><UIIcon name="ThunderboltOutlined" /> AI Analysis</>}
466
- style={{ marginBottom: 16 }}
467
- extra={
468
- analysisData?.data.cached && (
469
- <UIBadge variant="success">Cached</UIBadge>
470
- )
471
- }
472
- >
473
- {/* Summary */}
474
- <UIAlert
475
- message="Summary"
476
- description={analysis.summary}
477
- variant="info"
478
- showIcon
479
- icon={<UIIcon name="BulbOutlined" />}
480
- style={{ marginBottom: 16 }}
481
- />
482
-
483
- {/* Root Causes */}
484
- {analysis.rootCauses && analysis.rootCauses.length > 0 && (
485
- <>
486
- <UIDivider orientation="left">Root Causes</UIDivider>
487
- <UISpace direction="vertical" style={{ width: '100%', marginBottom: 16 }}>
488
- {analysis.rootCauses.map((cause: RootCauseItem, idx: number) => (
489
- <UICard key={idx} type="inner" size="small">
490
- <UISpace direction="vertical" style={{ width: '100%' }}>
491
- <div>
492
- <strong>{cause.factor}</strong>
493
- <UIProgress
494
- percent={Math.round(cause.confidence * 100)}
495
- size="small"
496
- style={{ marginTop: 4 }}
497
- strokeColor={
498
- cause.confidence >= 0.8
499
- ? '#52c41a'
500
- : cause.confidence >= 0.5
501
- ? '#faad14'
502
- : '#ff4d4f'
503
- }
504
- />
505
- </div>
506
- <div style={{ color: '#666', fontSize: 13 }}>{cause.evidence}</div>
507
- </UISpace>
508
- </UICard>
509
- ))}
510
- </UISpace>
511
- </>
512
- )}
513
-
514
- {/* Patterns */}
515
- {analysis.patterns && analysis.patterns.length > 0 && (
516
- <>
517
- <UIDivider orientation="left">Detected Patterns</UIDivider>
518
- <ul style={{ marginBottom: 16 }}>
519
- {analysis.patterns.map((pattern: string, idx: number) => (
520
- <li key={idx}>{pattern}</li>
521
- ))}
522
- </ul>
523
- </>
524
- )}
525
-
526
- {/* Recommendations */}
527
- {analysis.recommendations && analysis.recommendations.length > 0 && (
528
- <>
529
- <UIDivider orientation="left">Recommendations</UIDivider>
530
- <UISpace direction="vertical" style={{ width: '100%' }}>
531
- {analysis.recommendations.map((rec: string, idx: number) => (
532
- <UIAlert
533
- key={idx}
534
- message={`Recommendation ${idx + 1}`}
535
- description={rec}
536
- variant="success"
537
- showIcon
538
- />
539
- ))}
540
- </UISpace>
541
- </>
542
- )}
543
-
544
- {analysis.rootCauses.length === 0 && analysis.patterns.length === 0 && analysis.recommendations.length === 0 && (
545
- <UIEmptyState description="No detailed analysis available" />
546
- )}
547
- </UICard>
548
- )}
549
-
550
- {/* Resolve Modal */}
551
- <UIModal
552
- title="Resolve Incident"
553
- open={resolveModalVisible}
554
- onOk={handleResolve}
555
- onCancel={() => setResolveModalVisible(false)}
556
- confirmLoading={resolveMutation.isPending}
557
- okText="Resolve"
558
- >
559
- <UISpace direction="vertical" style={{ width: '100%' }}>
560
- <p>Are you sure you want to mark this incident as resolved?</p>
561
- <UIInputTextArea
562
- rows={4}
563
- placeholder="Resolution notes (optional)"
564
- value={resolutionNotes}
565
- onChange={(e) => setResolutionNotes(e.target.value)}
566
- />
567
- </UISpace>
568
- </UIModal>
569
- </UIPage>
570
- );
571
- }