66< link rel ="stylesheet " href ="https://cdn.datatables.net/2.3.4/css/dataTables.dataTables.min.css " />
77< link rel ="stylesheet " href ="https://cdn.datatables.net/rowgroup/1.5.1/css/rowGroup.dataTables.min.css " />
88< style >
9+ @keyframes barber-pole {
10+ /* This animation is used to make the status indicator next to
11+ pending pulls look like a barber poll. We do that with a
12+ diagonal linear gradient. CSS does not allow us to animate a
13+ gradient, so instead we make the indicator a little taller
14+ than shown, and then animate the whole thing upward. */
15+ from {
16+ transform : translate (0 , 0 );
17+ }
18+ to {
19+ /* The magic number 11.314 is sqrt(8^2 + 8^2), based on how
20+ far vertically it takes to repeat a 45 degree gradient
21+ that is 8 pixels long before it repeats. */
22+ transform : translate (0 , -11.314px );
23+ }
24+ }
925 main {
1026 max-width : 100rem ;
1127 width : 100% ;
3450 td .select-checkbox {
3551 width : 2.5rem ;
3652 }
53+ table .dataTable > tbody > tr > td .status {
54+ position : relative;
55+ overflow : hidden;
56+ padding-left : 16px ;
57+ white-space : nowrap;
58+ }
59+ td .status : before {
60+ content : " " ;
61+ position : absolute;
62+ display : block;
63+ left : 6px ;
64+ width : 6px ;
65+ top : 0 ;
66+ bottom : 0 ;
67+ }
68+ td .status [data-status = "pending" ]: before {
69+ /* Give the pending state a little bit of animation to make it
70+ clear that these items are the ones that are being tested
71+ right now. */
72+ bottom : -20px ;
73+ background-color : # F0DE57 ;
74+ background-image : repeating-linear-gradient (135deg , # F0DE57 0 , # F0DE57 4px , # FBEE97 4px , # FBEE97 8px , # F0DE57 0 );
75+ animation : barber-pole 1s linear infinite;
76+ }
3777
3878 # rollupModal {
3979 display : none;
90130 </ div >
91131
92132 < div class ="table-wrapper ">
93- < table id ="table ">
133+ < table id ="table " class =" stripe " >
94134 < thead >
95135 < tr >
96136 < th class ="select-checkbox "> </ th >
105145 < th > Rollup</ th >
106146 </ tr >
107147 </ thead >
108- < table id ="table " class ="stripe ">
109148
110149 < tbody >
111150 {% for pr in prs %}
@@ -114,12 +153,19 @@ <h1>
114153 < td data-pr-number ="{{ pr.number.0 }} ">
115154 < a href ="{{ repo_url }}/pull/{{ pr.number }} "> {{ pr.number.0 }}</ a >
116155 </ td >
117- < td data-status ="{{ crate::templates::status_text(pr) }} ">
156+ {% let pending_build = crate::templates::get_pending_build(pr) %}
157+ < td class ="status " data-status ="{{ crate::templates::status_text(pr) }} "
158+ {% if let Some(build) = pending_build %}
159+ title ="Started at {{ to_local_time(*build.created_at).format( "%d.%m.%Y %H:%M:%S ") }}"
160+ data-created-at="{{ build.created_at.timestamp_millis() }} "
161+ {% endif %} >
162+
118163 {% if let Some(try_build) = pr.try_build %}
119164 < a href ="../results/{{ repo_name }}/{{ pr.number }} "> {{ crate::templates::status_text(pr) }}</ a > (try)
120165 {% else %}
121166 {{ crate::templates::status_text(pr) }}
122167 {% endif %}
168+ < span class ="elapsed-time-display "> </ span >
123169 </ td >
124170 < td >
125171 {% match pr.mergeable_state %}
@@ -364,5 +410,44 @@ <h1>
364410
365411 window . location . href = oauthUrl . toString ( ) ;
366412 } ) ;
413+
414+ function formatElapsedTime ( ms ) {
415+ const totalSeconds = Math . floor ( ms / 1000 ) ;
416+ const hours = Math . floor ( totalSeconds / 3600 ) ;
417+ const remainingSeconds = totalSeconds % 3600 ;
418+ const minutes = Math . floor ( remainingSeconds / 60 ) ;
419+
420+ let output = '' ;
421+ if ( hours > 0 ) {
422+ output += `${ hours } h` ;
423+ }
424+
425+ if ( minutes > 0 ) {
426+ if ( output . length > 0 ) {
427+ output += ' ' ;
428+ }
429+ output += `${ minutes } m` ;
430+ }
431+ if ( output . length > 0 ) {
432+ return ` (${ output } )` ;
433+ }
434+ return '' ;
435+ }
436+
437+ function updateElapsedTimes ( ) {
438+ const now = Date . now ( ) ;
439+ document . querySelectorAll ( '[data-created-at]' ) . forEach ( td => {
440+ const createdAtMs = parseInt ( td . dataset . createdAt , 10 ) ;
441+ if ( createdAtMs > 0 ) {
442+ const elapsedMs = now - createdAtMs ;
443+ const formattedTime = formatElapsedTime ( elapsedMs ) ;
444+ let display = td . querySelector ( '.elapsed-time-display' ) ;
445+ display . textContent = formattedTime ;
446+ }
447+ } ) ;
448+ }
449+
450+ updateElapsedTimes ( ) ;
451+ setInterval ( updateElapsedTimes , 1000 ) ;
367452</ script >
368453{% endblock %}
0 commit comments