@@ -22,6 +22,9 @@ import Select from '../components/ui/Select';
2222import Switch from '../components/ui/Switch' ;
2323import Tabs from '../components/ui/Tabs' ;
2424import 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' ;
2528import { useStore } from '../store/useStore' ;
2629import type { INightWatchConfig , IQueueAnalytics , IQueueStatus , QueueMode } from '../api' ;
2730import {
@@ -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