-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathQueueTab.tsx
More file actions
145 lines (137 loc) · 5.56 KB
/
Copy pathQueueTab.tsx
File metadata and controls
145 lines (137 loc) · 5.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import React from 'react';
import { AlertCircle } from 'lucide-react';
import Card from '../ui/Card.js';
import type { IQueueAnalytics, IQueueStatus } from '../../api.js';
import ProviderLanesChart from './ProviderLanesChart.js';
import ProviderBucketSummary from './ProviderBucketSummary.js';
import RecentRunsChart from './RecentRunsChart.js';
const ERROR_MESSAGES = {
queueStatus: 'Failed to load queue status',
analytics: 'Failed to load analytics',
} as const;
const ErrorMessage: React.FC<{ message: string; className?: string }> = ({ message, className }) => (
<div className={`flex items-center gap-2 text-red-400 ${className ?? 'py-2'}`}>
<AlertCircle className="h-4 w-4" />
<span className="text-sm">{message}</span>
</div>
);
interface IQueueTabProps {
queueStatus: IQueueStatus | null;
queueAnalytics: IQueueAnalytics | null;
queueStatusError: Error | null;
queueAnalyticsError: Error | null;
}
const QueueTab: React.FC<IQueueTabProps> = ({
queueStatus,
queueAnalytics,
queueStatusError,
queueAnalyticsError,
}) => {
return (
<div className="space-y-6">
{/* Queue Overview Card */}
<Card className="p-6">
<h3 className="text-lg font-semibold text-slate-200 mb-4">Queue Overview</h3>
{queueStatusError ? (
<ErrorMessage message={ERROR_MESSAGES.queueStatus} className="py-4" />
) : (
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="bg-slate-950/40 rounded-lg p-4 border border-slate-800">
<div className="text-xs uppercase tracking-wide text-slate-500 mb-1">Running</div>
<div className="text-2xl font-bold text-green-400">
{queueStatus?.running ? 1 : 0}
</div>
{queueStatus?.running && (
<div className="text-xs text-slate-400 mt-1 truncate" title={queueStatus.running.projectName}>
{queueStatus.running.jobType} · {queueStatus.running.projectName}
</div>
)}
</div>
<div className="bg-slate-950/40 rounded-lg p-4 border border-slate-800">
<div className="text-xs uppercase tracking-wide text-slate-500 mb-1">Pending</div>
<div className="text-2xl font-bold text-blue-400">
{queueStatus?.pending.total ?? 0}
</div>
</div>
<div className="bg-slate-950/40 rounded-lg p-4 border border-slate-800">
<div className="text-xs uppercase tracking-wide text-slate-500 mb-1">Avg Wait</div>
<div className="text-2xl font-bold text-slate-200">
{queueStatus?.averageWaitSeconds != null
? `${Math.floor(queueStatus.averageWaitSeconds / 60)}m`
: '—'}
</div>
</div>
<div className="bg-slate-950/40 rounded-lg p-4 border border-slate-800">
<div className="text-xs uppercase tracking-wide text-slate-500 mb-1">Oldest Pending</div>
<div className="text-2xl font-bold text-slate-200">
{queueStatus?.oldestPendingAge != null
? `${Math.floor(queueStatus.oldestPendingAge / 60)}m`
: '—'}
</div>
</div>
</div>
)}
</Card>
{/* Provider Lanes */}
<Card className="p-6">
<div className="flex items-center justify-between mb-4">
<div>
<h3 className="text-lg font-semibold text-slate-200">Provider Lanes</h3>
<p className="text-xs text-slate-500 mt-0.5">
Running and pending jobs grouped by provider bucket
</p>
</div>
</div>
{queueStatusError ? (
<ErrorMessage message={ERROR_MESSAGES.queueStatus} />
) : queueStatus ? (
<ProviderLanesChart status={queueStatus} />
) : (
<div className="text-sm text-slate-500 py-2">Loading queue status...</div>
)}
</Card>
{/* Provider Bucket Summary */}
<Card className="p-6">
<div className="flex items-center justify-between mb-4">
<div>
<h3 className="text-lg font-semibold text-slate-200">Provider Buckets</h3>
<p className="text-xs text-slate-500 mt-0.5">
Running and pending counts per provider bucket
</p>
</div>
</div>
{queueAnalyticsError ? (
<ErrorMessage message={ERROR_MESSAGES.analytics} />
) : queueAnalytics ? (
<ProviderBucketSummary analytics={queueAnalytics} />
) : (
<div className="text-sm text-slate-500 py-2">Loading analytics...</div>
)}
</Card>
{/* Recent Runs */}
<Card className="p-6">
<div className="flex items-center justify-between mb-4">
<div>
<h3 className="text-lg font-semibold text-slate-200">Recent Runs</h3>
<p className="text-xs text-slate-500 mt-0.5">
Last 24 hours of job executions
</p>
</div>
{queueAnalytics?.averageWaitSeconds != null && (
<div className="text-xs text-slate-500">
Avg wait: {Math.floor(queueAnalytics.averageWaitSeconds / 60)}m
</div>
)}
</div>
{queueAnalyticsError ? (
<ErrorMessage message={ERROR_MESSAGES.analytics} />
) : queueAnalytics ? (
<RecentRunsChart analytics={queueAnalytics} />
) : (
<div className="text-sm text-slate-500 py-2">Loading analytics...</div>
)}
</Card>
</div>
);
};
export default QueueTab;