@elevasis/ui 2.0.0 → 2.0.1

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.
@@ -1,635 +0,0 @@
1
- import { ResourceHealthChart } from './chunk-LGKLC5MG.js';
2
- import { ExecutionStats } from './chunk-F2J7675J.js';
3
- import { HeroStatsRow, useCyberColors } from './chunk-JHVKGZ2P.js';
4
- import { CardHeader, EmptyState, PageTitleCaption, TabCountBadge, GlowDot } from './chunk-MCA6LOGM.js';
5
- import { AppShellCenteredContainer, AppShellLoader } from './chunk-YYBM5LNJ.js';
6
- import { useTimeRangeDates } from './chunk-BYZ7VTSH.js';
7
- import { useDashboardMetrics, useResources, useUnresolvedErrors, useRecentExecutionsByResource, useCommandQueue, useScheduledTasks, useResourcesHealth } from './chunk-EINVPEHK.js';
8
- import { getTimeRangeDates } from './chunk-LXHZYSMQ.js';
9
- import { ResourceStatusColors } from './chunk-ELJIFLCB.js';
10
- import { formatTimeAgo, formatRelativeTime } from './chunk-IOKL7BKE.js';
11
- import { useInitialization } from './chunk-TUXTSEAF.js';
12
- import { useMemo, useState } from 'react';
13
- import { Paper, Group, Badge, ActionIcon, Center, Loader, SimpleGrid, Stack, Title, Text, Tabs, Box, Card, Grid, ThemeIcon } from '@mantine/core';
14
- import { IconDashboard, IconArrowUpRight, IconLayoutDashboard, IconActivity, IconAlertTriangle, IconHandStop, IconCalendarEvent, IconPlayerPlay, IconCircleCheck, IconBrain, IconGitBranch } from '@tabler/icons-react';
15
- import '@mantine/charts/styles.css';
16
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
17
-
18
- var DEFAULT_LIMIT = 6;
19
- var typeIcons = {
20
- workflow: IconGitBranch,
21
- agent: IconBrain
22
- };
23
- function ResourceCard({ resource, onClick }) {
24
- const [hovered, setHovered] = useState(false);
25
- const Icon = typeIcons[resource.type] ?? IconActivity;
26
- const cyberColors = useCyberColors();
27
- return /* @__PURE__ */ jsxs(
28
- Card,
29
- {
30
- withBorder: true,
31
- style: {
32
- cursor: "pointer",
33
- transition: "all 150ms ease",
34
- borderColor: hovered ? "color-mix(in srgb, var(--color-primary) 40%, var(--color-border))" : "var(--color-border)",
35
- boxShadow: hovered ? "var(--card-shadow), 0 0 12px color-mix(in srgb, var(--color-primary) 25%, transparent)" : "var(--card-shadow)",
36
- transform: hovered ? "translateY(-1px)" : "none"
37
- },
38
- onMouseEnter: () => setHovered(true),
39
- onMouseLeave: () => setHovered(false),
40
- onClick,
41
- children: [
42
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", mb: "sm", wrap: "nowrap", align: "flex-start", children: [
43
- /* @__PURE__ */ jsxs(Group, { gap: "sm", wrap: "nowrap", style: { minWidth: 0 }, children: [
44
- /* @__PURE__ */ jsx(ThemeIcon, { size: 32, radius: "md", variant: "light", children: /* @__PURE__ */ jsx(Icon, { size: 18 }) }),
45
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, truncate: true, children: resource.name })
46
- ] }),
47
- resource.active && resource.execution ? (() => {
48
- const rate = resource.execution.successRate;
49
- const color = rate >= 95 ? cyberColors.green : rate >= 80 ? cyberColors.yellow : cyberColors.red;
50
- return /* @__PURE__ */ jsxs(Group, { gap: 8, wrap: "nowrap", style: { flexShrink: 0 }, children: [
51
- /* @__PURE__ */ jsxs(Text, { size: "xs", fw: 500, style: { color, letterSpacing: "0.03em" }, children: [
52
- rate.toFixed(0),
53
- "%"
54
- ] }),
55
- /* @__PURE__ */ jsx(GlowDot, { size: "md", color })
56
- ] });
57
- })() : null
58
- ] }),
59
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [
60
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
61
- /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", children: resource.type }),
62
- /* @__PURE__ */ jsx(
63
- Badge,
64
- {
65
- size: "xs",
66
- variant: "light",
67
- color: ResourceStatusColors[resource.status],
68
- children: resource.status
69
- }
70
- )
71
- ] }),
72
- resource.active && resource.execution ? /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", style: { flexShrink: 0 }, children: [
73
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
74
- resource.execution.totalExecutions,
75
- " runs"
76
- ] }),
77
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "\xB7" }),
78
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
79
- "Last run ",
80
- formatTimeAgo(resource.execution.lastExecution)
81
- ] })
82
- ] }) : /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No recent activity" })
83
- ] })
84
- ]
85
- }
86
- );
87
- }
88
- function ResourceOverview({
89
- recentExecutions,
90
- allResources,
91
- isLoading,
92
- limit = DEFAULT_LIMIT,
93
- onResourceClick
94
- }) {
95
- const { displayResources, totalCount } = useMemo(() => {
96
- const items = [];
97
- const seenIds = /* @__PURE__ */ new Set();
98
- const resourceLookup = /* @__PURE__ */ new Map();
99
- if (allResources) {
100
- for (const r of [...allResources.workflows, ...allResources.agents]) {
101
- resourceLookup.set(r.resourceId, r);
102
- }
103
- }
104
- if (recentExecutions) {
105
- for (const exec of recentExecutions) {
106
- const def = resourceLookup.get(exec.resourceId);
107
- items.push({
108
- resourceId: exec.resourceId,
109
- name: def?.name ?? exec.resourceName ?? exec.resourceId,
110
- type: def?.type ?? exec.resourceType ?? "",
111
- status: def?.status ?? "dev",
112
- active: true,
113
- execution: exec
114
- });
115
- seenIds.add(exec.resourceId);
116
- }
117
- }
118
- if (allResources) {
119
- for (const r of [...allResources.workflows, ...allResources.agents]) {
120
- if (!seenIds.has(r.resourceId)) {
121
- items.push({
122
- resourceId: r.resourceId,
123
- name: r.name,
124
- type: r.type,
125
- status: r.status,
126
- active: false
127
- });
128
- seenIds.add(r.resourceId);
129
- }
130
- }
131
- }
132
- return { displayResources: items.slice(0, limit), totalCount: items.length };
133
- }, [recentExecutions, allResources, limit]);
134
- const handleClick = (resource) => {
135
- onResourceClick?.(resource);
136
- };
137
- return /* @__PURE__ */ jsxs(Paper, { withBorder: true, children: [
138
- /* @__PURE__ */ jsx(
139
- CardHeader,
140
- {
141
- icon: /* @__PURE__ */ jsx(IconLayoutDashboard, { size: 18 }),
142
- title: "Resources",
143
- subtitle: totalCount > limit ? `Showing ${limit} of ${totalCount}` : void 0,
144
- rightSection: /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
145
- totalCount > 0 && /* @__PURE__ */ jsxs(Badge, { variant: "light", size: "sm", children: [
146
- totalCount,
147
- " total"
148
- ] }),
149
- /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", onClick: () => onResourceClick?.(displayResources[0]), children: /* @__PURE__ */ jsx(IconArrowUpRight, { size: 16 }) })
150
- ] })
151
- }
152
- ),
153
- isLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : displayResources.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { icon: IconLayoutDashboard, title: "No resources registered" }) : /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2, lg: 3 }, children: displayResources.map((resource) => /* @__PURE__ */ jsx(ResourceCard, { resource, onClick: () => handleClick(resource) }, resource.resourceId)) })
154
- ] });
155
- }
156
- var severityColor = {
157
- critical: "red",
158
- warning: "yellow",
159
- info: "blue"
160
- };
161
- function Dashboard({
162
- timeRange,
163
- onResourceClick,
164
- onErrorsNavigate,
165
- onCommandQueueNavigate,
166
- onScheduledTasksNavigate,
167
- renderTrendChart
168
- }) {
169
- const { organizationReady, error: initError } = useInitialization();
170
- const { data: dashboardDataRaw, isLoading, error } = useDashboardMetrics(timeRange);
171
- const dashboardData = dashboardDataRaw;
172
- const { data: resourcesData, isLoading: resourcesLoading } = useResources();
173
- const { startDate, endDate } = useTimeRangeDates(timeRange);
174
- const { data: errorData, isLoading: errorsLoading } = useUnresolvedErrors({ startDate, endDate });
175
- const { data: recentExecData, isLoading: recentExecLoading } = useRecentExecutionsByResource({
176
- timeRange,
177
- limit: 6
178
- });
179
- if (initError?.layer === "organization") {
180
- return /* @__PURE__ */ jsx(AppShellCenteredContainer, { children: /* @__PURE__ */ jsxs(Stack, { align: "center", justify: "center", children: [
181
- /* @__PURE__ */ jsx(Title, { order: 3, c: "dimmed", children: "No organization detected" }),
182
- /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: initError?.message })
183
- ] }) });
184
- }
185
- if (!organizationReady || isLoading || resourcesLoading) return /* @__PURE__ */ jsx(AppShellLoader, {});
186
- return /* @__PURE__ */ jsxs(Fragment, { children: [
187
- /* @__PURE__ */ jsx(PageTitleCaption, { title: "Dashboard", caption: "AI Operations Health & Performance" }),
188
- /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
189
- /* @__PURE__ */ jsx(
190
- HeroStatsRow,
191
- {
192
- resourcesData,
193
- resourcesLoading,
194
- dashboardData,
195
- dashboardLoading: isLoading,
196
- unresolvedErrorCount: errorData?.total ?? null,
197
- errorsLoading
198
- }
199
- ),
200
- renderTrendChart?.(dashboardData?.executionHealth, isLoading, error ?? void 0)
201
- ] }),
202
- /* @__PURE__ */ jsx(
203
- ResourceOverview,
204
- {
205
- recentExecutions: recentExecData?.resources,
206
- allResources: resourcesData,
207
- isLoading: resourcesLoading || recentExecLoading,
208
- limit: 6,
209
- onResourceClick: onResourceClick ? (resource) => onResourceClick({
210
- resourceId: resource.resourceId,
211
- resourceType: resource.type
212
- }) : void 0
213
- }
214
- ),
215
- /* @__PURE__ */ jsx(
216
- OperationalOverview,
217
- {
218
- timeRange,
219
- onErrorsNavigate,
220
- onCommandQueueNavigate,
221
- onScheduledTasksNavigate
222
- }
223
- )
224
- ] });
225
- }
226
- function OperationalOverview({
227
- timeRange,
228
- onErrorsNavigate,
229
- onCommandQueueNavigate,
230
- onScheduledTasksNavigate
231
- }) {
232
- const { startDate, endDate } = useTimeRangeDates(timeRange);
233
- const { data: errorData, isLoading: errorsLoading } = useUnresolvedErrors({ startDate, endDate });
234
- const errors = errorData?.errors ?? [];
235
- const errorCount = errorData?.total ?? 0;
236
- const { data: allCommands } = useCommandQueue({ status: "pending", limit: 100 });
237
- const commands = allCommands ?? [];
238
- const commandCount = commands.length;
239
- const { data: allTasks, isLoading: tasksLoading } = useScheduledTasks();
240
- const tasks = allTasks ?? [];
241
- const now = /* @__PURE__ */ new Date();
242
- const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
243
- const { totalScheduled, upcomingTasks } = useMemo(() => {
244
- const totalScheduled2 = tasks.filter((t) => t.status === "active").length;
245
- const upcomingTasks2 = tasks.filter((t) => t.status === "active" && t.nextRunAt).sort((a, b) => new Date(a.nextRunAt).getTime() - new Date(b.nextRunAt).getTime()).slice(0, 5);
246
- return { totalScheduled: totalScheduled2, upcomingTasks: upcomingTasks2 };
247
- }, [tasks]);
248
- const defaultTab = useMemo(() => {
249
- if (errorCount > 0) return "errors";
250
- if (commandCount > 0) return "commands";
251
- if (totalScheduled > 0) return "scheduled";
252
- return "errors";
253
- }, [errorCount, commandCount, totalScheduled]);
254
- const [activeTab, setActiveTab] = useState(null);
255
- const effectiveTab = activeTab ?? defaultTab;
256
- const formatDaysUntil = (runAt) => {
257
- const diffMs = runAt.getTime() - now.getTime();
258
- const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
259
- const diffHours = Math.floor(diffMs / (1e3 * 60 * 60));
260
- if (diffHours < 0) return "Overdue";
261
- if (diffHours < 1) return "Soon";
262
- if (diffDays === 0) return "Today";
263
- if (diffDays === 1) return "Tomorrow";
264
- if (diffDays <= 7) return `In ${diffDays}d`;
265
- return `In ${Math.floor(diffDays / 7)}w`;
266
- };
267
- return /* @__PURE__ */ jsxs(Paper, { withBorder: true, children: [
268
- /* @__PURE__ */ jsx(CardHeader, { icon: /* @__PURE__ */ jsx(IconActivity, { size: 18 }), title: "Operational Overview" }),
269
- /* @__PURE__ */ jsxs(Tabs, { value: effectiveTab, onChange: setActiveTab, variant: "default", children: [
270
- /* @__PURE__ */ jsxs(Tabs.List, { children: [
271
- /* @__PURE__ */ jsx(
272
- Tabs.Tab,
273
- {
274
- value: "errors",
275
- leftSection: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 16 }),
276
- rightSection: /* @__PURE__ */ jsx(TabCountBadge, { count: errorCount, isLoading: errorsLoading }),
277
- children: "Unresolved Errors"
278
- }
279
- ),
280
- /* @__PURE__ */ jsx(
281
- Tabs.Tab,
282
- {
283
- value: "commands",
284
- leftSection: /* @__PURE__ */ jsx(IconHandStop, { size: 16 }),
285
- rightSection: /* @__PURE__ */ jsx(TabCountBadge, { count: commandCount }),
286
- children: "Command Queue"
287
- }
288
- ),
289
- /* @__PURE__ */ jsx(
290
- Tabs.Tab,
291
- {
292
- value: "scheduled",
293
- leftSection: /* @__PURE__ */ jsx(IconCalendarEvent, { size: 16 }),
294
- rightSection: /* @__PURE__ */ jsx(TabCountBadge, { count: totalScheduled, isLoading: tasksLoading }),
295
- children: "Scheduled Tasks"
296
- }
297
- )
298
- ] }),
299
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "errors", pt: "sm", children: errorsLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : errors.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No unresolved errors" }) : /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
300
- errors.map((error) => /* @__PURE__ */ jsx(
301
- Box,
302
- {
303
- px: "sm",
304
- py: 6,
305
- onClick: onErrorsNavigate,
306
- style: {
307
- borderRadius: 4,
308
- cursor: onErrorsNavigate ? "pointer" : "default"
309
- },
310
- children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [
311
- /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", style: { minWidth: 200, width: 200, flexShrink: 0 }, children: [
312
- /* @__PURE__ */ jsx(
313
- Box,
314
- {
315
- style: {
316
- width: 8,
317
- height: 8,
318
- borderRadius: "50%",
319
- backgroundColor: `var(--mantine-color-${severityColor[error.severity]}-6)`,
320
- flexShrink: 0
321
- }
322
- }
323
- ),
324
- /* @__PURE__ */ jsxs(Stack, { gap: 0, style: { minWidth: 0 }, children: [
325
- /* @__PURE__ */ jsx(Text, { size: "xs", lineClamp: 1, children: error.resourceName || error.resourceId }),
326
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 1, children: error.errorType })
327
- ] })
328
- ] }),
329
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 1, style: { flex: "1 1 auto", minWidth: 0 }, children: error.message }),
330
- /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", style: { flexShrink: 0 }, children: [
331
- /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: severityColor[error.severity], children: error.severity }),
332
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatTimeAgo(error.timestamp) })
333
- ] })
334
- ] })
335
- },
336
- error.id
337
- )),
338
- errorCount > 5 && /* @__PURE__ */ jsx(
339
- Group,
340
- {
341
- gap: 4,
342
- justify: "center",
343
- pt: 4,
344
- onClick: onErrorsNavigate,
345
- style: { cursor: onErrorsNavigate ? "pointer" : "default" },
346
- children: /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "See all errors" })
347
- }
348
- )
349
- ] }) }),
350
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "commands", pt: "sm", children: commands.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No pending decisions" }) : /* @__PURE__ */ jsx(Stack, { gap: 4, children: commands.slice(0, 5).map((task) => /* @__PURE__ */ jsx(
351
- Box,
352
- {
353
- px: "sm",
354
- py: 6,
355
- onClick: onCommandQueueNavigate,
356
- style: {
357
- borderRadius: 4,
358
- cursor: onCommandQueueNavigate ? "pointer" : "default"
359
- },
360
- children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [
361
- /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", style: { minWidth: 0 }, children: [
362
- /* @__PURE__ */ jsx(
363
- Box,
364
- {
365
- style: {
366
- width: 8,
367
- height: 8,
368
- borderRadius: "50%",
369
- backgroundColor: `var(--mantine-color-${task.priority >= 8 ? "red" : task.priority >= 4 ? "yellow" : "blue"}-6)`,
370
- flexShrink: 0
371
- }
372
- }
373
- ),
374
- /* @__PURE__ */ jsx(Text, { size: "xs", lineClamp: 1, children: task.description || "No description" })
375
- ] }),
376
- /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", children: task.status })
377
- ] })
378
- },
379
- task.id
380
- )) }) }),
381
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "scheduled", pt: "sm", children: tasksLoading ? /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) }) : upcomingTasks.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No scheduled tasks" }) : /* @__PURE__ */ jsx(Stack, { gap: 4, children: upcomingTasks.map((task) => {
382
- const runAt = new Date(task.nextRunAt);
383
- const daysUntilNum = Math.floor((runAt.getTime() - today.getTime()) / (1e3 * 60 * 60 * 24));
384
- return /* @__PURE__ */ jsx(
385
- Box,
386
- {
387
- px: "sm",
388
- py: 6,
389
- onClick: onScheduledTasksNavigate,
390
- style: {
391
- borderRadius: 4,
392
- cursor: onScheduledTasksNavigate ? "pointer" : "default"
393
- },
394
- children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [
395
- /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", style: { minWidth: 0 }, children: [
396
- /* @__PURE__ */ jsx(
397
- Box,
398
- {
399
- style: {
400
- width: 8,
401
- height: 8,
402
- borderRadius: "50%",
403
- backgroundColor: `var(--mantine-color-${daysUntilNum < 0 ? "red" : daysUntilNum <= 1 ? "yellow" : "blue"}-6)`,
404
- flexShrink: 0
405
- }
406
- }
407
- ),
408
- /* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, children: task.name || task.target.resourceId })
409
- ] }),
410
- /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: daysUntilNum <= 1 ? void 0 : "gray", children: formatDaysUntil(runAt) })
411
- ] })
412
- },
413
- task.id
414
- );
415
- }) }) })
416
- ] })
417
- ] });
418
- }
419
- var entityTypeConfig = {
420
- workflow: { icon: IconGitBranch, hasPath: true },
421
- agent: { icon: IconBrain, hasPath: true }
422
- };
423
- function getEntityConfig(entityType) {
424
- return entityTypeConfig[entityType] || { icon: IconActivity, hasPath: false };
425
- }
426
- function RecentExecutionsByResource({ timeRange, limit = 5, onResourceClick }) {
427
- const { data, isLoading, isFetching, error } = useRecentExecutionsByResource({
428
- timeRange,
429
- limit
430
- });
431
- const showLoading = (isLoading || isFetching) && !data;
432
- const { data: resourcesData } = useResources();
433
- const resourceDefLookup = useMemo(() => {
434
- if (!resourcesData) return /* @__PURE__ */ new Map();
435
- const allResources = [...resourcesData.workflows || [], ...resourcesData.agents || []];
436
- return new Map(allResources.map((r) => [r.resourceId, r]));
437
- }, [resourcesData]);
438
- const enrichedExecutions = useMemo(() => {
439
- if (!data?.resources) return [];
440
- return data.resources.map((exec) => {
441
- const resourceDef = resourceDefLookup.get(exec.resourceId);
442
- return {
443
- ...exec,
444
- resourceType: resourceDef?.type || "",
445
- resourceName: resourceDef?.name || exec.resourceId
446
- };
447
- });
448
- }, [data, resourceDefLookup]);
449
- const resourceIds = useMemo(
450
- () => enrichedExecutions.map((r) => ({
451
- entityType: r.resourceType,
452
- entityId: r.resourceId
453
- })),
454
- [enrichedExecutions]
455
- );
456
- const { startDate, endDate } = useMemo(() => getTimeRangeDates(timeRange), [timeRange]);
457
- const granularity = timeRange === "1h" || timeRange === "24h" ? "hour" : "day";
458
- const { data: healthData } = useResourcesHealth({
459
- resources: resourceIds,
460
- startDate,
461
- endDate,
462
- granularity
463
- });
464
- const healthLookup = useMemo(() => {
465
- if (!healthData?.resources) return /* @__PURE__ */ new Map();
466
- return new Map(healthData.resources.map((r) => [`${r.entityType}-${r.entityId}`, r]));
467
- }, [healthData]);
468
- const handleResourceClick = (resource) => {
469
- const config = getEntityConfig(resource.resourceType);
470
- if (config.hasPath) {
471
- onResourceClick?.({ resourceType: resource.resourceType, resourceId: resource.resourceId });
472
- }
473
- };
474
- if (showLoading) {
475
- return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) });
476
- }
477
- if (error) {
478
- return /* @__PURE__ */ jsx(Center, { py: "md", children: /* @__PURE__ */ jsxs(Text, { size: "sm", c: "red.6", children: [
479
- "Failed to load: ",
480
- error.message
481
- ] }) });
482
- }
483
- if (enrichedExecutions.length === 0) {
484
- return /* @__PURE__ */ jsx(EmptyState, { icon: IconPlayerPlay, title: "No executions in this time range" });
485
- }
486
- return /* @__PURE__ */ jsx(Stack, { gap: "sm", children: enrichedExecutions.map((resource) => {
487
- const config = getEntityConfig(resource.resourceType);
488
- const Icon = config.icon;
489
- const resourceKey = `${resource.resourceType}-${resource.resourceId}`;
490
- const resourceHealth = healthLookup.get(resourceKey);
491
- const resourceDef = resourceDefLookup.get(resource.resourceId);
492
- return /* @__PURE__ */ jsx(
493
- Card,
494
- {
495
- withBorder: true,
496
- style: {
497
- cursor: config.hasPath ? "pointer" : "default",
498
- transition: "box-shadow var(--duration-fast) var(--easing)"
499
- },
500
- onClick: () => handleResourceClick(resource),
501
- children: /* @__PURE__ */ jsxs(Grid, { gutter: "sm", align: "center", children: [
502
- /* @__PURE__ */ jsx(Grid.Col, { span: 8, children: /* @__PURE__ */ jsxs(Group, { gap: "sm", wrap: "nowrap", children: [
503
- /* @__PURE__ */ jsx(Icon, { size: 24, color: "var(--color-primary)" }),
504
- /* @__PURE__ */ jsxs(Stack, { gap: 4, style: { flex: 1, minWidth: 0 }, children: [
505
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, truncate: true, children: resource.resourceName }),
506
- resourceDef?.description && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", truncate: true, children: resourceDef.description }),
507
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
508
- resourceDef?.version && /* @__PURE__ */ jsxs(Badge, { variant: "outline", size: "xs", children: [
509
- "v",
510
- resourceDef.version
511
- ] }),
512
- resourceDef?.status && /* @__PURE__ */ jsx(Badge, { color: ResourceStatusColors[resourceDef.status], variant: "outline", size: "xs", children: resourceDef.status.toUpperCase() })
513
- ] }),
514
- /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
515
- "Last run ",
516
- formatRelativeTime(resource.lastExecution)
517
- ] })
518
- ] })
519
- ] }) }),
520
- /* @__PURE__ */ jsx(Grid.Col, { span: 4, children: /* @__PURE__ */ jsxs(Group, { justify: "flex-end", gap: "md", wrap: "nowrap", children: [
521
- /* @__PURE__ */ jsx(
522
- ExecutionStats,
523
- {
524
- totalExecutions: resource.totalExecutions,
525
- successCount: resource.successCount,
526
- failureCount: resource.failureCount,
527
- warningCount: resource.warningCount,
528
- successRate: resource.successRate
529
- }
530
- ),
531
- /* @__PURE__ */ jsx(
532
- ResourceHealthChart,
533
- {
534
- healthData: resourceHealth,
535
- hasExecutions: resource.totalExecutions > 0,
536
- width: 300,
537
- height: 60
538
- }
539
- )
540
- ] }) })
541
- ] })
542
- },
543
- resource.resourceId
544
- );
545
- }) });
546
- }
547
- var severityColor2 = {
548
- critical: "red",
549
- warning: "yellow",
550
- info: "blue"
551
- };
552
- function UnresolvedErrorsTeaser({ timeRange, onNavigateToAllErrors }) {
553
- const { startDate, endDate } = useTimeRangeDates(timeRange);
554
- const { data, isLoading } = useUnresolvedErrors({ startDate, endDate });
555
- const errors = data?.errors ?? [];
556
- const total = data?.total ?? 0;
557
- if (isLoading) {
558
- return /* @__PURE__ */ jsx(
559
- Paper,
560
- {
561
- style: {
562
- background: "var(--glass-background)",
563
- backdropFilter: "var(--glass-blur)",
564
- border: "1px solid var(--color-border)",
565
- borderRadius: 8
566
- },
567
- children: /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) })
568
- }
569
- );
570
- }
571
- return /* @__PURE__ */ jsxs(
572
- Paper,
573
- {
574
- style: {
575
- background: "var(--glass-background)",
576
- backdropFilter: "var(--glass-blur)",
577
- border: "1px solid var(--color-border)",
578
- borderRadius: 8
579
- },
580
- children: [
581
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", mb: errors.length > 0 ? "sm" : 0, children: [
582
- /* @__PURE__ */ jsxs(Group, { gap: "sm", children: [
583
- /* @__PURE__ */ jsx(ThemeIcon, { size: 28, radius: "md", variant: "light", color: total > 0 ? "red" : "green", children: total > 0 ? /* @__PURE__ */ jsx(IconAlertTriangle, { size: 16 }) : /* @__PURE__ */ jsx(IconCircleCheck, { size: 16 }) }),
584
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, children: "Unresolved Errors" })
585
- ] }),
586
- /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
587
- total > 0 && /* @__PURE__ */ jsxs(Badge, { size: "xs", variant: "light", color: "red", children: [
588
- total,
589
- " unresolved"
590
- ] }),
591
- /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", onClick: onNavigateToAllErrors, children: /* @__PURE__ */ jsx(IconArrowUpRight, { size: 14 }) })
592
- ] })
593
- ] }),
594
- errors.length === 0 && /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: "No unresolved errors" }),
595
- errors.length > 0 && /* @__PURE__ */ jsx(Stack, { gap: 4, children: errors.map((error) => /* @__PURE__ */ jsx(
596
- Box,
597
- {
598
- px: "sm",
599
- py: 6,
600
- onClick: onNavigateToAllErrors,
601
- style: {
602
- borderLeft: `2px solid var(--mantine-color-${severityColor2[error.severity]}-6)`,
603
- borderRadius: 4,
604
- cursor: onNavigateToAllErrors ? "pointer" : "default",
605
- textDecoration: "none",
606
- color: "inherit"
607
- },
608
- children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", children: [
609
- /* @__PURE__ */ jsxs(Stack, { gap: 0, style: { minWidth: 0 }, children: [
610
- /* @__PURE__ */ jsx(Text, { size: "xs", lineClamp: 1, children: error.resourceName || error.resourceId }),
611
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", lineClamp: 1, children: error.errorType })
612
- ] }),
613
- /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", style: { flexShrink: 0 }, children: [
614
- /* @__PURE__ */ jsx(Badge, { size: "xs", variant: "light", color: severityColor2[error.severity], children: error.severity }),
615
- /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", children: formatTimeAgo(error.timestamp) })
616
- ] })
617
- ] })
618
- },
619
- error.id
620
- )) })
621
- ]
622
- }
623
- );
624
- }
625
- var dashboardManifest = {
626
- key: "dashboard",
627
- label: "Dashboard",
628
- navEntry: {
629
- label: "Dashboard",
630
- icon: IconDashboard,
631
- link: "/"
632
- }
633
- };
634
-
635
- export { Dashboard, RecentExecutionsByResource, ResourceOverview, UnresolvedErrorsTeaser, dashboardManifest };