Skip to content

Commit 0a29338

Browse files
Test Userclaude
andcommitted
feat(scheduling): add Queue tab with provider lanes and execution analytics
Adds a new 'Queue' tab to the Scheduling page that visualizes: - Queue overview (running, pending, avg wait, oldest pending) - Provider lanes chart showing jobs grouped by provider bucket - Provider bucket summary with running/pending counts - Recent runs chart with execution history This completes Phase 4 of the Provider-Aware Queue PRD. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 66bebb7 commit 0a29338

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

web/pages/Scheduling.tsx

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import Select from '../components/ui/Select';
2222
import Switch from '../components/ui/Switch';
2323
import Tabs from '../components/ui/Tabs';
2424
import ScheduleTimeline from '../components/scheduling/ScheduleTimeline.js';
25+
import ProviderLanesChart from '../components/scheduling/ProviderLanesChart.js';
26+
import ProviderBucketSummary from '../components/scheduling/ProviderBucketSummary.js';
27+
import RecentRunsChart from '../components/scheduling/RecentRunsChart.js';
2528
import { useStore } from '../store/useStore';
2629
import type { INightWatchConfig, IQueueAnalytics, IQueueStatus, QueueMode } from '../api';
2730
import {
@@ -913,6 +916,109 @@ const Scheduling: React.FC = () => {
913916
</div>
914917
),
915918
},
919+
{
920+
id: 'queue',
921+
label: 'Queue',
922+
content: (
923+
<div className="space-y-6">
924+
{/* Queue Overview Card */}
925+
<Card className="p-6">
926+
<h3 className="text-lg font-semibold text-slate-200 mb-4">Queue Overview</h3>
927+
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
928+
<div className="bg-slate-950/40 rounded-lg p-4 border border-slate-800">
929+
<div className="text-xs uppercase tracking-wide text-slate-500 mb-1">Running</div>
930+
<div className="text-2xl font-bold text-green-400">
931+
{queueStatus?.running ? 1 : 0}
932+
</div>
933+
{queueStatus?.running && (
934+
<div className="text-xs text-slate-400 mt-1 truncate" title={queueStatus.running.projectName}>
935+
{queueStatus.running.jobType} · {queueStatus.running.projectName}
936+
</div>
937+
)}
938+
</div>
939+
<div className="bg-slate-950/40 rounded-lg p-4 border border-slate-800">
940+
<div className="text-xs uppercase tracking-wide text-slate-500 mb-1">Pending</div>
941+
<div className="text-2xl font-bold text-blue-400">
942+
{queueStatus?.pending.total ?? 0}
943+
</div>
944+
</div>
945+
<div className="bg-slate-950/40 rounded-lg p-4 border border-slate-800">
946+
<div className="text-xs uppercase tracking-wide text-slate-500 mb-1">Avg Wait</div>
947+
<div className="text-2xl font-bold text-slate-200">
948+
{queueStatus?.averageWaitSeconds != null
949+
? `${Math.floor(queueStatus.averageWaitSeconds / 60)}m`
950+
: '—'}
951+
</div>
952+
</div>
953+
<div className="bg-slate-950/40 rounded-lg p-4 border border-slate-800">
954+
<div className="text-xs uppercase tracking-wide text-slate-500 mb-1">Oldest Pending</div>
955+
<div className="text-2xl font-bold text-slate-200">
956+
{queueStatus?.oldestPendingAge != null
957+
? `${Math.floor(queueStatus.oldestPendingAge / 60)}m`
958+
: '—'}
959+
</div>
960+
</div>
961+
</div>
962+
</Card>
963+
964+
{/* Provider Lanes */}
965+
<Card className="p-6">
966+
<div className="flex items-center justify-between mb-4">
967+
<div>
968+
<h3 className="text-lg font-semibold text-slate-200">Provider Lanes</h3>
969+
<p className="text-xs text-slate-500 mt-0.5">
970+
Running and pending jobs grouped by provider bucket
971+
</p>
972+
</div>
973+
</div>
974+
{queueStatus ? (
975+
<ProviderLanesChart status={queueStatus} />
976+
) : (
977+
<div className="text-sm text-slate-500 py-2">Loading queue status...</div>
978+
)}
979+
</Card>
980+
981+
{/* Provider Bucket Summary */}
982+
<Card className="p-6">
983+
<div className="flex items-center justify-between mb-4">
984+
<div>
985+
<h3 className="text-lg font-semibold text-slate-200">Provider Buckets</h3>
986+
<p className="text-xs text-slate-500 mt-0.5">
987+
Running and pending counts per provider bucket
988+
</p>
989+
</div>
990+
</div>
991+
{queueAnalytics ? (
992+
<ProviderBucketSummary analytics={queueAnalytics} />
993+
) : (
994+
<div className="text-sm text-slate-500 py-2">Loading analytics...</div>
995+
)}
996+
</Card>
997+
998+
{/* Recent Runs */}
999+
<Card className="p-6">
1000+
<div className="flex items-center justify-between mb-4">
1001+
<div>
1002+
<h3 className="text-lg font-semibold text-slate-200">Recent Runs</h3>
1003+
<p className="text-xs text-slate-500 mt-0.5">
1004+
Last 24 hours of job executions
1005+
</p>
1006+
</div>
1007+
{queueAnalytics?.averageWaitSeconds != null && (
1008+
<div className="text-xs text-slate-500">
1009+
Avg wait: {Math.floor(queueAnalytics.averageWaitSeconds / 60)}m
1010+
</div>
1011+
)}
1012+
</div>
1013+
{queueAnalytics ? (
1014+
<RecentRunsChart analytics={queueAnalytics} />
1015+
) : (
1016+
<div className="text-sm text-slate-500 py-2">Loading analytics...</div>
1017+
)}
1018+
</Card>
1019+
</div>
1020+
),
1021+
},
9161022
];
9171023

9181024
return (

0 commit comments

Comments
 (0)