@@ -24,8 +24,18 @@ export default class JobMonitor extends Pure {
2424 // }
2525 }
2626
27+ componentDidUpdate ( ) {
28+ const jobList = this . refs . jobList
29+ jobList . scrollTop = jobList . scrollHeight
30+ }
31+
32+ // Remove any retired jobs from the monitor
2733 removeAll = ( ) => {
28- this . props . jobMonitor . retired . forEach ( job => this . props . removeRetiredJob ( job ) )
34+ const { jobMonitor } = this . props
35+ jobMonitor . retired . forEach ( job => this . props . removeRetiredJob ( job ) )
36+
37+ // If no active jobs left, then close the popover
38+ if ( jobMonitor . jobs . length === 0 ) this . props . close ( )
2939 }
3040
3141 sortByDate = ( a , b ) => new Date ( b . status . initialized ) - new Date ( a . status . initialized )
@@ -37,41 +47,59 @@ export default class JobMonitor extends Pure {
3747 < SidebarPopover
3848 ref = { ( SidebarPopover ) => { this . popover = SidebarPopover } }
3949 title = 'Server Jobs'
40- { ...this . props } >
41- < ul className = 'list-unstyled job-list' >
42- { retired . sort ( this . sortByDate ) . map ( job => (
43- < RetiredJob
44- key = { `retired-${ job . jobId } ` }
45- job = { job }
46- removeRetiredJob = { removeRetiredJob } />
47- ) ) }
48- { jobs . sort ( this . sortByDate ) . map ( job => (
49- < li key = { job . jobId } className = 'job-container' >
50- < div className = 'job-spinner-div' >
51- < Icon type = 'spinner' className = 'fa-pulse' />
52- </ div >
53- < div className = 'job-container-inner' >
54- < div >
55- < strong > { job . name } </ strong >
56- </ div >
57- < ProgressBar
58- label = { `${ job . status ? job . status . percentComplete : 0 } %` }
59- active now = { job . status ? job . status . percentComplete : 0 }
60- className = 'job-status-progress-bar' />
61- < div className = 'job-status-message' >
62- { job . status ? job . status . message : 'waiting' }
63- </ div >
64- </ div >
65- </ li >
66- ) ) }
67- </ ul >
68- < p className = 'lead text-center' > { jobs . length ? jobs . length : 'No' } active jobs.</ p >
69- < Button
70- block
71- disabled = { retired . length === 0 }
72- onClick = { this . removeAll } >
73- < Icon type = 'times-circle' /> Clear completed
74- </ Button >
50+ fixedHeight = { 300 }
51+ minMarginBottom = { 75 }
52+ { ...this . props }
53+ >
54+ < div className = 'job-monitor' >
55+ { /* The main list of jobs */ }
56+ < div className = 'job-list' ref = 'jobList' > { /* TODO: replace w/ React 16 createRef() */ }
57+ < ul className = 'list-unstyled' >
58+ { retired . sort ( this . sortByDate ) . map ( job => (
59+ < RetiredJob
60+ key = { `retired-${ job . jobId } ` }
61+ job = { job }
62+ removeRetiredJob = { removeRetiredJob } />
63+ ) ) }
64+ { jobs . sort ( this . sortByDate ) . map ( job => {
65+ const pctComplete = Math . round ( job . status ? job . status . percentComplete : 0 )
66+ return (
67+ < li key = { job . jobId } className = 'job-container' >
68+ < div className = 'job-spinner-div' >
69+ < Icon type = 'spinner' className = 'fa-pulse' />
70+ </ div >
71+ < div className = 'job-container-inner' >
72+ < div >
73+ < strong > { job . name } </ strong >
74+ </ div >
75+ < ProgressBar active
76+ style = { { width : 190 } }
77+ label = { `${ pctComplete } %` }
78+ now = { pctComplete }
79+ className = 'job-status-progress-bar' />
80+ < div className = 'job-status-message' >
81+ { job . status ? job . status . message : 'waiting' }
82+ </ div >
83+ </ div >
84+ </ li >
85+ )
86+ } ) }
87+ </ ul >
88+ </ div >
89+ { /* Lower panel job count and clear-all button */ }
90+ < div style = { { marginTop : 8 } } >
91+ < Button
92+ style = { { float : 'right' } }
93+ bsSize = 'small'
94+ disabled = { retired . length === 0 }
95+ onClick = { this . removeAll } >
96+ < Icon type = 'times-circle' /> Clear completed
97+ </ Button >
98+ < div style = { { paddingTop : 6 , fontSize : 13 } } >
99+ { jobs . length ? jobs . length : 'No' } active job{ ! jobs . length || jobs . length > 1 ? 's' : '' }
100+ </ div >
101+ </ div >
102+ </ div >
75103 </ SidebarPopover >
76104 )
77105 }
@@ -98,61 +126,67 @@ class RetiredJob extends Pure {
98126 }
99127 </ div >
100128 < div className = 'job-container-inner' >
101- < div style = { {
102- overflow : 'hidden' ,
103- textOverflow : 'ellipsis' ,
104- width : '220px' ,
105- whiteSpace : 'nowrap'
106- } } >
129+ < div style = { { float : 'right' } } >
107130 < Button
131+ className = 'close-job-button'
108132 bsStyle = 'link'
109- className = 'pull-right'
133+ style = { { padding : 'none' } }
110134 onClick = { this . removeJob } >
111135 < Icon className = 'pull-right' type = 'times-circle' />
112136 </ Button >
113- < strong
114- title = { job . name } >
115- { job . name }
116- </ strong >
117137 </ div >
118- < div className = 'job-status-message' >
119- { job . status . message }
120- { job . status . exceptionDetails
121- ? < OverlayTrigger
122- trigger = 'click'
123- placement = 'right'
124- overlay = {
125- < Popover
126- id = 'job-exception-detail'
127- style = { {
128- minWidth : '400px'
129- } }
130- title = {
131- < span >
132- < Icon type = 'bug' /> Oh no! Looks like an error has occurred.
138+ < div >
139+ < div style = { {
140+ display : 'inline-block' ,
141+ overflow : 'hidden' ,
142+ textOverflow : 'ellipsis' ,
143+ width : 170 ,
144+ whiteSpace : 'nowrap'
145+ } } >
146+ < strong
147+ title = { job . name } >
148+ { job . name }
149+ </ strong >
150+ </ div >
151+ < div className = 'job-status-message' >
152+ { job . status . message }
153+ { job . status . exceptionDetails
154+ ? < OverlayTrigger
155+ trigger = 'click'
156+ placement = 'right'
157+ overlay = {
158+ < Popover
159+ id = 'job-exception-detail'
160+ style = { {
161+ minWidth : '400px'
162+ } }
163+ title = {
164+ < span >
165+ < Icon type = 'bug' /> Oh no! Looks like an error has occurred.
166+ </ span >
167+ } >
168+ < p >
169+ To submit an error report email a screenshot of your browser
170+ window, the following text (current URL and error details),
171+ and a detailed description of the steps you followed
172+ to < a href = 'mailto:support@conveyal.com' > support@conveyal.com</ a > .
173+ </ p >
174+ < p > { window . location . href } </ p >
175+ < span style = { { whiteSpace : 'pre' , fontSize : 'xx-small' } } >
176+ { job . status . exceptionDetails }
133177 </ span >
178+ </ Popover >
134179 } >
135- < p >
136- To submit an error report email a screenshot of your browser
137- window, the following text (current URL and error details),
138- and a detailed description of the steps you followed
139- to < a href = 'mailto:support@conveyal.com' > support@conveyal.com</ a > .
140- </ p >
141- < p > { window . location . href } </ p >
142- < span style = { { whiteSpace : 'pre' , fontSize : 'xx-small' } } >
143- { job . status . exceptionDetails }
144- </ span >
145- </ Popover >
146- } >
147- < Button
148- bsSize = 'small'
149- style = { { padding : '0px' } }
150- bsStyle = 'link' >
151- < Icon type = 'bug' />
152- </ Button >
153- </ OverlayTrigger >
154- : null
155- }
180+ < Button
181+ bsSize = 'small'
182+ style = { { padding : '0px' } }
183+ bsStyle = 'link' >
184+ < Icon type = 'bug' />
185+ </ Button >
186+ </ OverlayTrigger >
187+ : null
188+ }
189+ </ div >
156190 </ div >
157191 </ div >
158192 </ li >
0 commit comments