@kb-labs/studio-app 0.6.0 → 2.8.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.1d68e922.js +522 -0
  3. package/dist/main.1d68e922.js.map +1 -0
  4. package/dist/main.80b70197.css +2 -0
  5. package/dist/main.80b70197.css.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,486 +0,0 @@
1
- import {
2
- UIRow,
3
- UICol,
4
- UITable,
5
- UICard,
6
- UIStatistic,
7
- UIAlert,
8
- UITag,
9
- UIProgress,
10
- UITimeline,
11
- UIBadge,
12
- UIIcon,
13
- } from '@kb-labs/studio-ui-kit';
14
- import { usePrometheusMetrics } from '@kb-labs/studio-data-client';
15
- import { useDataSources } from '../../../providers/data-sources-provider';
16
- import type { PluginMetrics, TenantMetrics } from '@kb-labs/studio-data-client';
17
- import { UIPage, UIPageHeader } from '@kb-labs/studio-ui-kit';
18
-
19
- /**
20
- * Format uptime duration to human-readable string
21
- */
22
- function formatUptime(seconds: number): string {
23
- const hours = Math.floor(seconds / 3600);
24
- const minutes = Math.floor((seconds % 3600) / 60);
25
-
26
- if (hours > 0) {
27
- return `${hours}h ${minutes}m`;
28
- }
29
- return `${minutes}m`;
30
- }
31
-
32
- /**
33
- * Format number with optional decimal places
34
- */
35
- function formatNumber(value: number, decimals = 2): string {
36
- return value.toFixed(decimals);
37
- }
38
-
39
- /**
40
- * Format timestamp to relative time
41
- */
42
- function formatRelativeTime(timestamp: number): string {
43
- const now = Date.now();
44
- const diff = now - timestamp;
45
- const seconds = Math.floor(diff / 1000);
46
- const minutes = Math.floor(seconds / 60);
47
- const hours = Math.floor(minutes / 60);
48
-
49
- if (hours > 0) {
50
- return `${hours}h ${minutes % 60}m ago`;
51
- }
52
- if (minutes > 0) {
53
- return `${minutes}m ago`;
54
- }
55
- return `${seconds}s ago`;
56
- }
57
-
58
- /**
59
- * Get status code color
60
- */
61
- function getStatusCodeColor(code: number): string {
62
- if (code >= 500) {return '#ff4d4f';} // red
63
- if (code >= 400) {return '#faad14';} // orange
64
- if (code >= 300) {return '#1890ff';} // blue
65
- return '#52c41a'; // green
66
- }
67
-
68
- /**
69
- * Prometheus Metrics Observability Page
70
- *
71
- * Shows real-time metrics parsed from the canonical REST API /metrics endpoint:
72
- * - Request counts and error rates
73
- * - Latency percentiles (p50, p95, p99)
74
- * - Per-plugin and per-tenant breakdown
75
- * - Redis health status
76
- * - Plugin mount statistics
77
- */
78
- export function PrometheusMetricsPage() {
79
- const sources = useDataSources();
80
- const { data, isLoading, error, isError } = usePrometheusMetrics(sources.observability);
81
-
82
- if (isError) {
83
- return (
84
- <UIPage>
85
- <UIPageHeader
86
- title="Prometheus Metrics"
87
- description="REST API performance and health metrics"
88
- />
89
- <UIAlert
90
- message="Failed to load Prometheus metrics"
91
- description={error instanceof Error ? error.message : 'Unknown error. Make sure REST API is running on localhost:5050'}
92
- variant="error"
93
- showIcon
94
- style={{ marginBottom: 24 }}
95
- />
96
- </UIPage>
97
- );
98
- }
99
-
100
- if (isLoading || !data) {
101
- return (
102
- <UIPage>
103
- <UIPageHeader
104
- title="Prometheus Metrics"
105
- description="REST API performance and health metrics"
106
- />
107
- <div style={{ padding: '24px 0', textAlign: 'center' }}>
108
- Loading...
109
- </div>
110
- </UIPage>
111
- );
112
- }
113
-
114
- const errorRate = data.requests?.total > 0
115
- ? (((data.requests.clientErrors ?? 0) + (data.requests.serverErrors ?? 0)) / data.requests.total) * 100
116
- : 0;
117
-
118
- const successRate = data.requests?.total > 0
119
- ? ((data.requests.success ?? 0) / data.requests.total) * 100
120
- : 0;
121
-
122
- return (
123
- <UIPage width="full">
124
- <UIPageHeader
125
- title="Prometheus Metrics"
126
- description="REST API performance metrics - Auto-refresh every 10s"
127
- />
128
-
129
- {/* Overview Cards */}
130
- <UIRow gutter={[16, 16]} style={{ marginBottom: 24 }}>
131
- <UICol xs={24} sm={12} lg={6}>
132
- <UICard>
133
- <UIStatistic
134
- title="Total Requests"
135
- value={data.requests?.total ?? 0}
136
- prefix={<UIIcon name="ApiOutlined" />}
137
- />
138
- </UICard>
139
- </UICol>
140
- <UICol xs={24} sm={12} lg={6}>
141
- <UICard>
142
- <UIStatistic
143
- title="Avg Latency (p50)"
144
- value={formatNumber(data.latency?.p50 ?? 0, 1)}
145
- suffix="ms"
146
- prefix={<UIIcon name="ThunderboltOutlined" />}
147
- />
148
- </UICard>
149
- </UICol>
150
- <UICol xs={24} sm={12} lg={6}>
151
- <UICard>
152
- <UIStatistic
153
- title="Error Rate"
154
- value={formatNumber(errorRate, 1)}
155
- suffix="%"
156
- prefix={<UIIcon name="WarningOutlined" />}
157
- valueStyle={{ color: errorRate > 5 ? '#ff4d4f' : '#52c41a' }}
158
- />
159
- </UICard>
160
- </UICol>
161
- <UICol xs={24} sm={12} lg={6}>
162
- <UICard>
163
- <UIStatistic
164
- title="Uptime"
165
- value={formatUptime(data.uptime?.seconds ?? 0)}
166
- prefix={<UIIcon name="ClockCircleOutlined" />}
167
- />
168
- </UICard>
169
- </UICol>
170
- </UIRow>
171
-
172
- {/* Latency Percentiles */}
173
- <UIRow gutter={[16, 16]} style={{ marginBottom: 24 }}>
174
- <UICol xs={24} lg={12}>
175
- <UICard title="Latency Percentiles" extra={<UIIcon name="ThunderboltOutlined" style={{ color: '#1890ff' }} />}>
176
- <UIRow gutter={16}>
177
- <UICol span={8}>
178
- <UIStatistic
179
- title="p50 (Median)"
180
- value={formatNumber(data.latency?.p50 ?? 0, 1)}
181
- suffix="ms"
182
- valueStyle={{ fontSize: 20 }}
183
- />
184
- </UICol>
185
- <UICol span={8}>
186
- <UIStatistic
187
- title="p95"
188
- value={formatNumber(data.latency?.p95 ?? 0, 1)}
189
- suffix="ms"
190
- valueStyle={{ fontSize: 20, color: (data.latency?.p95 ?? 0) > 200 ? '#faad14' : '#52c41a' }}
191
- />
192
- </UICol>
193
- <UICol span={8}>
194
- <UIStatistic
195
- title="p99"
196
- value={formatNumber(data.latency?.p99 ?? 0, 1)}
197
- suffix="ms"
198
- valueStyle={{ fontSize: 20, color: (data.latency?.p99 ?? 0) > 500 ? '#ff4d4f' : '#faad14' }}
199
- />
200
- </UICol>
201
- </UIRow>
202
- <div style={{ marginTop: 16, fontSize: 12, color: '#8c8c8c' }}>
203
- Min: {formatNumber(data.latency?.min ?? 0, 1)}ms | Max: {formatNumber(data.latency?.max ?? 0, 1)}ms | Avg: {formatNumber(data.latency?.average ?? 0, 1)}ms
204
- </div>
205
- </UICard>
206
- </UICol>
207
-
208
- <UICol xs={24} lg={12}>
209
- <UICard title="Success Rate" extra={<UIIcon name="CheckCircleOutlined" style={{ color: successRate >= 95 ? '#52c41a' : '#faad14' }} />}>
210
- <UIProgress
211
- percent={Math.round(successRate)}
212
- status={successRate >= 95 ? 'success' : successRate >= 90 ? 'normal' : 'exception'}
213
- strokeColor={successRate >= 95 ? '#52c41a' : successRate >= 90 ? '#1890ff' : '#ff4d4f'}
214
- />
215
- <div style={{ marginTop: 16 }}>
216
- <UIRow gutter={16}>
217
- <UICol span={8}>
218
- <UIStatistic
219
- title="Success"
220
- value={data.requests?.success ?? 0}
221
- valueStyle={{ fontSize: 18, color: '#52c41a' }}
222
- />
223
- </UICol>
224
- <UICol span={8}>
225
- <UIStatistic
226
- title="4xx Errors"
227
- value={data.requests?.clientErrors ?? 0}
228
- valueStyle={{ fontSize: 18, color: '#faad14' }}
229
- />
230
- </UICol>
231
- <UICol span={8}>
232
- <UIStatistic
233
- title="5xx Errors"
234
- value={data.requests?.serverErrors ?? 0}
235
- valueStyle={{ fontSize: 18, color: '#ff4d4f' }}
236
- />
237
- </UICol>
238
- </UIRow>
239
- </div>
240
- </UICard>
241
- </UICol>
242
- </UIRow>
243
-
244
- {/* Per-Plugin Metrics */}
245
- <UICard title="Per-Plugin Metrics" style={{ marginBottom: 24 }}>
246
- <UITable<{ key: string; plugin: string } & PluginMetrics>
247
- dataSource={(data.perPlugin ?? []).map((item) => ({
248
- key: item.pluginId,
249
- plugin: item.pluginId,
250
- ...item,
251
- }))}
252
- columns={[
253
- {
254
- title: 'Plugin',
255
- dataIndex: 'plugin',
256
- key: 'plugin',
257
- render: (plugin: string) => <UITag color="blue">{plugin}</UITag>,
258
- },
259
- {
260
- title: 'Requests',
261
- dataIndex: 'requests',
262
- key: 'requests',
263
- align: 'right' as const,
264
- sorter: (a, b) => a.requests - b.requests,
265
- },
266
- {
267
- title: 'Errors',
268
- dataIndex: 'errors',
269
- key: 'errors',
270
- align: 'right' as const,
271
- render: (errors: number) => (
272
- <span style={{ color: errors > 10 ? '#ff4d4f' : errors > 0 ? '#faad14' : '#52c41a' }}>
273
- {errors}
274
- </span>
275
- ),
276
- },
277
- {
278
- title: 'Avg Latency',
279
- key: 'avgLatency',
280
- align: 'right' as const,
281
- render: (_: unknown, record: PluginMetrics) =>
282
- record.latency ? `${formatNumber(record.latency.average, 1)} ms` : 'N/A',
283
- sorter: (a, b) => (a.latency?.average ?? 0) - (b.latency?.average ?? 0),
284
- },
285
- {
286
- title: 'Min / Max',
287
- key: 'minMax',
288
- align: 'right' as const,
289
- render: (_: unknown, record: PluginMetrics) =>
290
- record.latency ? (
291
- <span style={{ fontSize: 12, color: '#8c8c8c' }}>
292
- {formatNumber(record.latency.min, 1)} / {formatNumber(record.latency.max, 1)} ms
293
- </span>
294
- ) : <span>N/A</span>,
295
- },
296
- ]}
297
- pagination={false}
298
- size="small"
299
- />
300
- </UICard>
301
-
302
- {/* Per-Tenant Metrics */}
303
- <UICard title="Per-Tenant Metrics" style={{ marginBottom: 24 }}>
304
- <UITable<{ key: string; tenant: string } & TenantMetrics>
305
- dataSource={(data.perTenant ?? []).map((item) => ({
306
- key: item.tenantId,
307
- tenant: item.tenantId,
308
- ...item,
309
- }))}
310
- columns={[
311
- {
312
- title: 'Tenant',
313
- dataIndex: 'tenant',
314
- key: 'tenant',
315
- render: (tenant: string) => <UITag color="green">{tenant}</UITag>,
316
- },
317
- {
318
- title: 'Requests',
319
- dataIndex: 'requests',
320
- key: 'requests',
321
- align: 'right' as const,
322
- },
323
- {
324
- title: 'Errors',
325
- dataIndex: 'errors',
326
- key: 'errors',
327
- align: 'right' as const,
328
- render: (errors: number) => (
329
- <span style={{ color: errors > 50 ? '#ff4d4f' : errors > 10 ? '#faad14' : '#52c41a' }}>
330
- {errors}
331
- </span>
332
- ),
333
- },
334
- {
335
- title: 'Avg Latency',
336
- key: 'avgLatency',
337
- align: 'right' as const,
338
- render: (_: unknown, record: TenantMetrics) =>
339
- record.latency ? `${formatNumber(record.latency.average, 1)} ms` : 'N/A',
340
- },
341
- {
342
- title: 'Error Rate',
343
- key: 'errorRate',
344
- align: 'right' as const,
345
- render: (_: unknown, record: TenantMetrics) => {
346
- const rate = record.requests > 0 ? (record.errors / record.requests) * 100 : 0;
347
- return (
348
- <span style={{ color: rate > 5 ? '#ff4d4f' : rate > 2 ? '#faad14' : '#52c41a' }}>
349
- {formatNumber(rate, 1)}%
350
- </span>
351
- );
352
- },
353
- },
354
- ]}
355
- pagination={false}
356
- size="small"
357
- />
358
- </UICard>
359
-
360
- {/* Error Analytics */}
361
- <UIRow gutter={[16, 16]} style={{ marginBottom: 24 }}>
362
- <UICol xs={24} lg={12}>
363
- <UICard title="Error Breakdown" extra={<UIIcon name="ExclamationCircleOutlined" style={{ color: '#faad14' }} />}>
364
- <UIRow gutter={16}>
365
- {Object.entries(data.errors?.byStatusCode ?? {})
366
- .sort(([a], [b]) => Number(b) - Number(a))
367
- .map(([code, count]) => (
368
- <UICol span={8} key={code}>
369
- <UIStatistic
370
- title={`${code} Errors`}
371
- value={count}
372
- valueStyle={{ fontSize: 18, color: getStatusCodeColor(Number(code)) }}
373
- />
374
- </UICol>
375
- ))}
376
- </UIRow>
377
- {Object.keys(data.errors?.byStatusCode ?? {}).length === 0 && (
378
- <UIAlert message="No errors recorded" variant="success" showIcon />
379
- )}
380
- </UICard>
381
- </UICol>
382
-
383
- <UICol xs={24} lg={12}>
384
- <UICard title="Recent Errors" extra={<UIBadge count={data.errors?.recent?.length ?? 0} />}>
385
- {data.errors?.recent && data.errors.recent.length > 0 ? (
386
- <UITimeline
387
- items={data.errors.recent.map((error) => ({
388
- color: getStatusCodeColor(error.statusCode),
389
- children: (
390
- <div>
391
- <div style={{ marginBottom: 4 }}>
392
- <UITag color={getStatusCodeColor(error.statusCode)}>{error.statusCode}</UITag>
393
- {error.errorCode && <UITag>{error.errorCode}</UITag>}
394
- <span style={{ fontSize: 12, color: '#8c8c8c', marginLeft: 8 }}>
395
- {formatRelativeTime(error.timestamp)}
396
- </span>
397
- </div>
398
- <div style={{ fontSize: 13 }}>{error.message}</div>
399
- </div>
400
- ),
401
- }))}
402
- />
403
- ) : (
404
- <UIAlert message="No recent errors" variant="success" showIcon />
405
- )}
406
- </UICard>
407
- </UICol>
408
- </UIRow>
409
-
410
- {/* Redis Status & Plugin Mounts */}
411
- <UIRow gutter={[16, 16]}>
412
- <UICol xs={24} lg={12}>
413
- <UICard title="Redis Status" extra={<UIIcon name="DatabaseOutlined" style={{ color: data.redis.lastStatus?.healthy ? '#52c41a' : '#ff4d4f' }} />}>
414
- {data.redis.lastStatus ? (
415
- <>
416
- <UIRow gutter={16}>
417
- <UICol span={12}>
418
- <UIStatistic
419
- title="Health"
420
- value={data.redis.lastStatus.healthy ? 'Healthy' : 'Unhealthy'}
421
- valueStyle={{ color: data.redis.lastStatus.healthy ? '#52c41a' : '#ff4d4f' }}
422
- />
423
- </UICol>
424
- <UICol span={12}>
425
- <UIStatistic
426
- title="Role"
427
- value={data.redis.lastStatus.role}
428
- valueStyle={{ fontSize: 20 }}
429
- />
430
- </UICol>
431
- </UIRow>
432
- <div style={{ marginTop: 16 }}>
433
- <p style={{ margin: 0, fontSize: 12, color: '#8c8c8c' }}>
434
- State: <strong>{data.redis.lastStatus.state}</strong>
435
- </p>
436
- <p style={{ margin: 0, fontSize: 12, color: '#8c8c8c' }}>
437
- Healthy transitions: {data.redis.healthyTransitions} | Unhealthy: {data.redis.unhealthyTransitions}
438
- </p>
439
- </div>
440
- </>
441
- ) : (
442
- <UIAlert message="No Redis status data available" variant="info" showIcon />
443
- )}
444
- </UICard>
445
- </UICol>
446
-
447
- <UICol xs={24} lg={12}>
448
- <UICard title="Plugin Mounts">
449
- {data.pluginMounts ? (
450
- <>
451
- <UIRow gutter={16}>
452
- <UICol span={8}>
453
- <UIStatistic
454
- title="Total"
455
- value={data.pluginMounts.total}
456
- valueStyle={{ fontSize: 20 }}
457
- />
458
- </UICol>
459
- <UICol span={8}>
460
- <UIStatistic
461
- title="Succeeded"
462
- value={data.pluginMounts.succeeded}
463
- valueStyle={{ fontSize: 20, color: '#52c41a' }}
464
- />
465
- </UICol>
466
- <UICol span={8}>
467
- <UIStatistic
468
- title="Failed"
469
- value={data.pluginMounts.failed}
470
- valueStyle={{ fontSize: 20, color: data.pluginMounts.failed > 0 ? '#ff4d4f' : '#52c41a' }}
471
- />
472
- </UICol>
473
- </UIRow>
474
- <div style={{ marginTop: 16, fontSize: 12, color: '#8c8c8c' }}>
475
- Elapsed time: {formatNumber(data.pluginMounts.elapsedMs, 0)}ms
476
- </div>
477
- </>
478
- ) : (
479
- <UIAlert message="No plugin mount data available" variant="info" showIcon />
480
- )}
481
- </UICard>
482
- </UICol>
483
- </UIRow>
484
- </UIPage>
485
- );
486
- }