146146 }
147147
148148 .status-STARTING {
149- background : # ffc107 ;
150- color : # 000 ;
149+ background : # 17a2b8 ;
150+ color : # fff ;
151151 }
152152
153153 .status-RUNNING {
154- background : rgba ( 40 , 167 , 69 , 0.3 ) ;
155- color : # 28a745 ;
154+ background : # ffc107 ;
155+ color : # 000 ;
156156 }
157157
158158 .status-SUCCEEDED {
315315 font-size : 0.85rem ;
316316 margin-left : auto;
317317 }
318+
319+ .status-counter {
320+ background : var (--bg-secondary );
321+ border : 1px solid var (--border-color );
322+ border-radius : 0.5rem ;
323+ margin-bottom : 1.5rem ;
324+ padding : 1rem ;
325+ }
326+
327+ .status-counter-title {
328+ font-weight : 600 ;
329+ color : var (--text-primary );
330+ display : flex;
331+ align-items : center;
332+ gap : 0.5rem ;
333+ margin-bottom : 1rem ;
334+ }
335+
336+ .status-counter-grid {
337+ display : grid;
338+ grid-template-columns : repeat (auto-fit, minmax (150px , 1fr ));
339+ gap : 0.75rem ;
340+ }
341+
342+ .status-count-item {
343+ background : var (--bg-tertiary );
344+ padding : 0.75rem ;
345+ border-radius : 0.375rem ;
346+ border-left : 3px solid;
347+ display : flex;
348+ flex-direction : column;
349+ gap : 0.25rem ;
350+ }
351+
352+ .status-count-item .SUBMITTED ,
353+ .status-count-item .PENDING ,
354+ .status-count-item .RUNNABLE {
355+ border-left-color : # 6c757d ;
356+ }
357+
358+ .status-count-item .STARTING {
359+ border-left-color : # 17a2b8 ;
360+ }
361+
362+ .status-count-item .RUNNING {
363+ border-left-color : # ffc107 ;
364+ }
365+
366+ .status-count-item .SUCCEEDED {
367+ border-left-color : # 28a745 ;
368+ }
369+
370+ .status-count-item .FAILED {
371+ border-left-color : # dc3545 ;
372+ }
373+
374+ .status-count-label {
375+ font-size : 0.75rem ;
376+ text-transform : uppercase;
377+ color : var (--text-secondary );
378+ font-weight : 600 ;
379+ letter-spacing : 0.05em ;
380+ }
381+
382+ .status-count-value {
383+ font-size : 1.5rem ;
384+ font-weight : 700 ;
385+ color : var (--text-primary );
386+ }
387+
388+ .rounds-count {
389+ display : inline-block;
390+ background-color : var (--accent-color );
391+ color : # 000000 ;
392+ padding : 0.25rem 0.5rem ;
393+ border-radius : 0.25rem ;
394+ font-size : 0.85rem ;
395+ font-weight : 700 ;
396+ }
397+
398+ .rounds-count-warning {
399+ background-color : # ffc107 ;
400+ }
401+
402+ .rounds-unknown {
403+ color : var (--text-muted );
404+ font-style : italic;
405+ }
318406 </ style >
319407</ head >
320408< body >
@@ -326,6 +414,16 @@ <h1><i class="bi bi-cloud"></i> AWS Batch Job Monitor</h1>
326414 </ a >
327415 </ div >
328416
417+ < div class ="status-counter ">
418+ < div class ="status-counter-title ">
419+ < i class ="bi bi-bar-chart "> </ i >
420+ < span > Job Status Summary (Last 24h)</ span >
421+ </ div >
422+ < div class ="status-counter-grid " id ="status-counter-grid ">
423+ <!-- Will be populated by JavaScript -->
424+ </ div >
425+ </ div >
426+
329427 < div class ="filter-container ">
330428 < label for ="status-filter "> Filter by status:</ label >
331429 < select id ="status-filter " class ="filter-select " onchange ="applyStatusFilter() ">
@@ -355,13 +453,47 @@ <h1><i class="bi bi-cloud"></i> AWS Batch Job Monitor</h1>
355453 let statusFilter = '' ;
356454 let lastRefreshTime = null ;
357455
456+ function updateStatusCounter ( ) {
457+ const statusCounts = {
458+ 'SUBMITTED' : 0 ,
459+ 'PENDING' : 0 ,
460+ 'RUNNABLE' : 0 ,
461+ 'STARTING' : 0 ,
462+ 'RUNNING' : 0 ,
463+ 'SUCCEEDED' : 0 ,
464+ 'FAILED' : 0
465+ } ;
466+
467+ allJobs . forEach ( job => {
468+ if ( statusCounts . hasOwnProperty ( job . status ) ) {
469+ statusCounts [ job . status ] ++ ;
470+ }
471+ } ) ;
472+
473+ const grid = document . getElementById ( 'status-counter-grid' ) ;
474+ let html = '' ;
475+
476+ Object . keys ( statusCounts ) . forEach ( status => {
477+ const count = statusCounts [ status ] ;
478+ html += `
479+ <div class="status-count-item ${ status } ">
480+ <div class="status-count-label">${ status } </div>
481+ <div class="status-count-value">${ count } </div>
482+ </div>
483+ ` ;
484+ } ) ;
485+
486+ grid . innerHTML = html ;
487+ }
488+
358489 async function fetchJobs ( ) {
359490 try {
360491 const response = await fetch ( '/batch/api/jobs' ) ;
361492 const data = await response . json ( ) ;
362493
363494 if ( data . success ) {
364495 allJobs = data . jobs ;
496+ updateStatusCounter ( ) ;
365497 renderJobs ( ) ;
366498 clearError ( ) ;
367499 lastRefreshTime = Date . now ( ) ;
@@ -434,6 +566,7 @@ <h1><i class="bi bi-cloud"></i> AWS Batch Job Monitor</h1>
434566 <th class="sortable" onclick="sortBy('time_running_seconds')">
435567 Time Running <i class="bi bi-arrow-down-up sort-icon"></i>
436568 </th>
569+ <th>Rounds</th>
437570 <th class="sortable" onclick="sortBy('job_name')">
438571 Job Name <i class="bi bi-arrow-down-up sort-icon"></i>
439572 </th>
@@ -450,11 +583,20 @@ <h1><i class="bi bi-cloud"></i> AWS Batch Job Monitor</h1>
450583 const escapedJobName = job . job_name . replace ( / ' / g, "\\'" ) ;
451584 const escapedJobId = job . job_id . replace ( / ' / g, "\\'" ) ;
452585
586+ let roundsHtml = '<span class="rounds-unknown">-</span>' ;
587+ if ( job . round_info && Array . isArray ( job . round_info ) && job . round_info . length === 2 ) {
588+ const completed = job . round_info [ 0 ] ;
589+ const total = job . round_info [ 1 ] ;
590+ const warningClass = completed < total ? ' rounds-count-warning' : '' ;
591+ roundsHtml = `<span class="rounds-count${ warningClass } ">${ completed } /${ total } </span>` ;
592+ }
593+
453594 html += `
454595 <tr>
455596 <td>${ job . created_at || '-' } </td>
456597 <td><span class="status-tag status-${ job . status } ">${ job . status } </span></td>
457598 <td>${ job . time_running || '-' } </td>
599+ <td>${ roundsHtml } </td>
458600 <td>
459601 <div class="copy-cell">
460602 <span class="job-name">${ job . job_name } </span>
0 commit comments