@@ -23,6 +23,9 @@ import Tabs from '../components/ui/Tabs';
2323import ScheduleConfig from '../components/scheduling/ScheduleConfig.js' ;
2424import type { IScheduleConfigForm } from '../components/scheduling/ScheduleConfig.js' ;
2525import ScheduleTimeline from '../components/scheduling/ScheduleTimeline.js' ;
26+ import ProviderLanesChart from '../components/scheduling/ProviderLanesChart.js' ;
27+ import ProviderBucketSummary from '../components/scheduling/ProviderBucketSummary.js' ;
28+ import RecentRunsChart from '../components/scheduling/RecentRunsChart.js' ;
2629import { useStore } from '../store/useStore' ;
2730import type { INightWatchConfig , IQueueAnalytics , IQueueStatus , QueueMode } from '../api' ;
2831import {
@@ -1023,6 +1026,109 @@ const Scheduling: React.FC = () => {
10231026 </ div >
10241027 ) ,
10251028 } ,
1029+ {
1030+ id : 'queue' ,
1031+ label : 'Queue' ,
1032+ content : (
1033+ < div className = "space-y-6" >
1034+ { /* Queue Overview Card */ }
1035+ < Card className = "p-6" >
1036+ < h3 className = "text-lg font-semibold text-slate-200 mb-4" > Queue Overview</ h3 >
1037+ < div className = "grid grid-cols-2 md:grid-cols-4 gap-4" >
1038+ < div className = "bg-slate-950/40 rounded-lg p-4 border border-slate-800" >
1039+ < div className = "text-xs uppercase tracking-wide text-slate-500 mb-1" > Running</ div >
1040+ < div className = "text-2xl font-bold text-green-400" >
1041+ { queueStatus ?. running ? 1 : 0 }
1042+ </ div >
1043+ { queueStatus ?. running && (
1044+ < div className = "text-xs text-slate-400 mt-1 truncate" title = { queueStatus . running . projectName } >
1045+ { queueStatus . running . jobType } · { queueStatus . running . projectName }
1046+ </ div >
1047+ ) }
1048+ </ div >
1049+ < div className = "bg-slate-950/40 rounded-lg p-4 border border-slate-800" >
1050+ < div className = "text-xs uppercase tracking-wide text-slate-500 mb-1" > Pending</ div >
1051+ < div className = "text-2xl font-bold text-blue-400" >
1052+ { queueStatus ?. pending . total ?? 0 }
1053+ </ div >
1054+ </ div >
1055+ < div className = "bg-slate-950/40 rounded-lg p-4 border border-slate-800" >
1056+ < div className = "text-xs uppercase tracking-wide text-slate-500 mb-1" > Avg Wait</ div >
1057+ < div className = "text-2xl font-bold text-slate-200" >
1058+ { queueStatus ?. averageWaitSeconds != null
1059+ ? `${ Math . floor ( queueStatus . averageWaitSeconds / 60 ) } m`
1060+ : '—' }
1061+ </ div >
1062+ </ div >
1063+ < div className = "bg-slate-950/40 rounded-lg p-4 border border-slate-800" >
1064+ < div className = "text-xs uppercase tracking-wide text-slate-500 mb-1" > Oldest Pending</ div >
1065+ < div className = "text-2xl font-bold text-slate-200" >
1066+ { queueStatus ?. oldestPendingAge != null
1067+ ? `${ Math . floor ( queueStatus . oldestPendingAge / 60 ) } m`
1068+ : '—' }
1069+ </ div >
1070+ </ div >
1071+ </ div >
1072+ </ Card >
1073+
1074+ { /* Provider Lanes */ }
1075+ < Card className = "p-6" >
1076+ < div className = "flex items-center justify-between mb-4" >
1077+ < div >
1078+ < h3 className = "text-lg font-semibold text-slate-200" > Provider Lanes</ h3 >
1079+ < p className = "text-xs text-slate-500 mt-0.5" >
1080+ Running and pending jobs grouped by provider bucket
1081+ </ p >
1082+ </ div >
1083+ </ div >
1084+ { queueStatus ? (
1085+ < ProviderLanesChart status = { queueStatus } />
1086+ ) : (
1087+ < div className = "text-sm text-slate-500 py-2" > Loading queue status...</ div >
1088+ ) }
1089+ </ Card >
1090+
1091+ { /* Provider Bucket Summary */ }
1092+ < Card className = "p-6" >
1093+ < div className = "flex items-center justify-between mb-4" >
1094+ < div >
1095+ < h3 className = "text-lg font-semibold text-slate-200" > Provider Buckets</ h3 >
1096+ < p className = "text-xs text-slate-500 mt-0.5" >
1097+ Running and pending counts per provider bucket
1098+ </ p >
1099+ </ div >
1100+ </ div >
1101+ { queueAnalytics ? (
1102+ < ProviderBucketSummary analytics = { queueAnalytics } />
1103+ ) : (
1104+ < div className = "text-sm text-slate-500 py-2" > Loading analytics...</ div >
1105+ ) }
1106+ </ Card >
1107+
1108+ { /* Recent Runs */ }
1109+ < Card className = "p-6" >
1110+ < div className = "flex items-center justify-between mb-4" >
1111+ < div >
1112+ < h3 className = "text-lg font-semibold text-slate-200" > Recent Runs</ h3 >
1113+ < p className = "text-xs text-slate-500 mt-0.5" >
1114+ Last 24 hours of job executions
1115+ </ p >
1116+ </ div >
1117+ { queueAnalytics ?. averageWaitSeconds != null && (
1118+ < div className = "text-xs text-slate-500" >
1119+ Avg wait: { Math . floor ( queueAnalytics . averageWaitSeconds / 60 ) } m
1120+ </ div >
1121+ ) }
1122+ </ div >
1123+ { queueAnalytics ? (
1124+ < RecentRunsChart analytics = { queueAnalytics } />
1125+ ) : (
1126+ < div className = "text-sm text-slate-500 py-2" > Loading analytics...</ div >
1127+ ) }
1128+ </ Card >
1129+ </ div >
1130+ ) ,
1131+ } ,
10261132 ] ;
10271133
10281134 return (
0 commit comments