Skip to content

Commit 2fe9e37

Browse files
committed
feat(FeedVersion): show loading spinner when version validating
1 parent d2de962 commit 2fe9e37

File tree

5 files changed

+123
-54
lines changed

5 files changed

+123
-54
lines changed

lib/manager/components/FeedVersionNavigator.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ export default class FeedVersionNavigator extends Component {
2626
notesRequestedForVersion: PropTypes.func,
2727
createDeploymentFromFeedSource: PropTypes.func,
2828
fetchValidationResult: PropTypes.func,
29-
setVersionIndex: PropTypes.func
29+
setVersionIndex: PropTypes.func,
30+
validationJob: PropTypes.object
3031
}
3132
constructor (props) {
3233
super(props)
@@ -38,7 +39,6 @@ export default class FeedVersionNavigator extends Component {
3839
}
3940
}
4041
render () {
41-
console.log(this.props)
4242
const versionTitleStyle = {
4343
fontSize: '24px',
4444
fontWeight: 'bold'

lib/manager/components/FeedVersionReport.js

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@ export default class FeedVersionReport extends Component {
5151
const poly = bounds && bboxPoly([bounds.west, bounds.south, bounds.east, bounds.east])
5252
return poly && area(poly)
5353
}
54-
getVersionDateLabel (version) {
54+
getVersionDates (version, validationJob) {
55+
const text = validationJob
56+
? <span className='loading-ellipsis'>Processing feed ({validationJob.status.percentComplete}%)</span>
57+
: `Valid from ${moment(version.validationSummary.startDate).format(dateFormat)} to ${moment(version.validationSummary.endDate).format(dateFormat)}`
58+
return <span><Icon type='calendar' /> {text}</span>
59+
}
60+
getVersionDateLabel (version, validationJob) {
61+
if (validationJob) return null
5562
const now = +moment()
5663
const future = version.validationSummary && version.validationSummary.startDate > now
5764
const expired = version.validationSummary && version.validationSummary.endDate < now
@@ -159,7 +166,14 @@ export default class FeedVersionReport extends Component {
159166
))
160167
}
161168
render () {
162-
const version = this.props.version
169+
const {
170+
version,
171+
validationJob,
172+
isPublic,
173+
feedVersionRenamed,
174+
isPublished,
175+
publishFeedVersion
176+
} = this.props
163177
const messages = getComponentMessages('FeedVersionReport')
164178

165179
if (!version) return <p>{getMessage(messages, 'noVersionsExist')}</p>
@@ -170,20 +184,22 @@ export default class FeedVersionReport extends Component {
170184
style={{margin: '0px'}}
171185
>
172186
{/* Name Display / Editor */}
173-
{version.validationSummary.loadStatus === 'SUCCESS' && version.validationSummary.errorCount === 0
187+
{validationJob
188+
? <Icon title='Feed is processing...' className='fa-spin' type='refresh' />
189+
: version.validationSummary.loadStatus === 'SUCCESS' && version.validationSummary.errorCount === 0
174190
? <Icon title='Feed loaded successfully, and is error-free!' className='text-success' type='check' style={{marginRight: 10}} />
175191
: version.validationSummary.errorCount > 0
176192
? <Icon title='Feed loaded successfully, but has errors.' className='text-warning' type='exclamation-triangle' style={{marginRight: 10}} />
177193
: <Icon title='Feed did not load successfully, something has gone wrong!' className='text-danger' type='times' style={{marginRight: 10}} />
178194
}
179-
{this.props.isPublic
195+
{isPublic
180196
? <span>{version.name}</span>
181197
: <EditableTextField
182198
inline
183199
value={version.name}
184200
maxWidth={40}
185-
disabled={this.props.isPublic}
186-
onChange={(value) => this.props.feedVersionRenamed(version, value)} />
201+
disabled={isPublic}
202+
onChange={(value) => feedVersionRenamed(version, value)} />
187203
}
188204
<VersionButtonToolbar
189205
{...this.props}
@@ -227,7 +243,7 @@ export default class FeedVersionReport extends Component {
227243
</ButtonGroup>
228244
<ActiveGtfsMap
229245
ref='map'
230-
version={this.props.version}
246+
version={version}
231247
disableRefresh
232248
disableScroll
233249
disablePopup
@@ -243,21 +259,21 @@ export default class FeedVersionReport extends Component {
243259
<h4>
244260
{isExtensionEnabled('mtc')
245261
? <Button
246-
disabled={this.props.isPublished}
262+
disabled={isPublished}
247263
className='pull-right'
248-
bsStyle={this.props.isPublished ? 'success' : 'warning'}
249-
onClick={() => this.props.publishFeedVersion(version)}
264+
bsStyle={isPublished ? 'success' : 'warning'}
265+
onClick={() => publishFeedVersion(version)}
250266
>
251-
{this.props.isPublished
267+
{isPublished
252268
? <span><Icon type='check-circle' /> Published</span>
253269
: <span>Publish to MTC</span>
254270
}
255271
</Button>
256272
: null
257273
}
258-
<Icon type='calendar' /> {`Valid from ${moment(version.validationSummary.startDate).format(dateFormat)} to ${moment(version.validationSummary.endDate).format(dateFormat)}`}
274+
{this.getVersionDates(version, validationJob)}
259275
{' '}
260-
{this.getVersionDateLabel(version)}
276+
{this.getVersionDateLabel(version, validationJob)}
261277
</h4>
262278
<p>
263279
{version.validationSummary && version.validationSummary.avgDailyRevenueTime
@@ -294,21 +310,21 @@ export default class FeedVersionReport extends Component {
294310
</Tab>
295311
<Tab eventKey={'routes'} title='Routes'>
296312
<Routes
297-
version={this.props.version}
313+
version={version}
298314
selectTab={(key) => this.selectTab(key)}
299315
tableOptions={tableOptions}
300316
/>
301317
</Tab>
302318
<Tab eventKey={'patterns'} title='Patterns'>
303319
<Patterns
304-
version={this.props.version}
320+
version={version}
305321
selectTab={(key) => this.selectTab(key)}
306322
tableOptions={tableOptions}
307323
/>
308324
</Tab>
309325
<Tab eventKey={'stops'} title='Stops'>
310326
<Stops
311-
version={this.props.version}
327+
version={version}
312328
selectTab={(key) => this.selectTab(key)}
313329
tableOptions={tableOptions}
314330
/>

lib/manager/components/FeedVersionViewer.js

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,21 @@ export default class FeedVersionViewer extends Component {
2828
notesRequested: PropTypes.func,
2929
fetchValidationResult: PropTypes.func,
3030
downloadFeedClicked: PropTypes.func,
31-
loadFeedVersionForEditing: PropTypes.func
31+
loadFeedVersionForEditing: PropTypes.func,
32+
validationJob: PropTypes.object
3233
}
3334
render () {
34-
const version = this.props.version
35+
const {
36+
version,
37+
feedVersionIndex,
38+
versionSection,
39+
feedSource,
40+
fetchValidationResult,
41+
user,
42+
validationJob,
43+
notesRequested,
44+
newNotePosted
45+
} = this.props
3546
const messages = getComponentMessages('FeedVersionViewer')
3647

3748
if (!version) return <p className='text-center lead'>{getMessage(messages, 'noVersionsExist')}</p>
@@ -61,37 +72,38 @@ export default class FeedVersionViewer extends Component {
6172
<Row>
6273
<Col xs={12} sm={3}>
6374
<VersionSectionSelector
64-
version={this.props.version}
65-
feedVersionIndex={this.props.feedVersionIndex}
66-
versionSection={this.props.versionSection}
75+
version={version}
76+
feedVersionIndex={feedVersionIndex}
77+
validationJob={validationJob}
78+
versionSection={versionSection}
6779
/>
6880
</Col>
6981
<Col xs={12} sm={9}>
70-
{!this.props.versionSection
82+
{!versionSection
7183
? <FeedVersionReport
72-
isPublished={version.id === this.props.feedSource.publishedVersionId}
84+
isPublished={version.id === feedSource.publishedVersionId}
7385
{...this.props}
7486
/>
75-
: this.props.versionSection === 'issues'
87+
: versionSection === 'issues'
7688
? <GtfsValidationViewer
7789
validationResult={version.validationResult}
7890
version={version}
79-
fetchValidationResult={() => { this.props.fetchValidationResult(version) }}
91+
fetchValidationResult={() => { fetchValidationResult(version) }}
8092
/>
81-
: this.props.versionSection === 'gtfsplus' && isModuleEnabled('gtfsplus')
93+
: versionSection === 'gtfsplus' && isModuleEnabled('gtfsplus')
8294
? <ActiveGtfsPlusVersionSummary
8395
version={version}
8496
/>
85-
: this.props.versionSection === 'comments'
97+
: versionSection === 'comments'
8698
? <NotesViewer
8799
type='feed-version'
88100
stacked
89-
user={this.props.user}
90-
version={this.props.version}
101+
user={user}
102+
version={version}
91103
notes={version.notes}
92104
noteCount={version.noteCount}
93-
notesRequested={() => { this.props.notesRequested() }}
94-
newNotePosted={(note) => { this.props.newNotePosted(note) }}
105+
notesRequested={() => { notesRequested() }}
106+
newNotePosted={(note) => { newNotePosted(note) }}
95107
/>
96108
: null
97109
}
@@ -114,10 +126,19 @@ export class VersionButtonToolbar extends Component {
114126

115127
downloadFeedClicked: PropTypes.func,
116128
deleteFeedVersionConfirmed: PropTypes.func,
117-
loadFeedVersionForEditing: PropTypes.func
129+
loadFeedVersionForEditing: PropTypes.func,
130+
validationJob: PropTypes.object
118131
}
119132
render () {
120-
const version = this.props.version
133+
const {
134+
version,
135+
hasVersions,
136+
downloadFeedClicked,
137+
isPublic,
138+
loadFeedVersionForEditing,
139+
deleteDisabled,
140+
deleteFeedVersionConfirmed
141+
} = this.props
121142
const messages = getComponentMessages('FeedVersionViewer')
122143
return (
123144
<div style={{display: 'inline'}}>
@@ -127,21 +148,21 @@ export class VersionButtonToolbar extends Component {
127148
{/* "Download Feed" Button */}
128149
<Button
129150
bsStyle='primary'
130-
disabled={!this.props.hasVersions}
131-
onClick={(evt) => this.props.downloadFeedClicked(version, this.props.isPublic)}
151+
disabled={!hasVersions}
152+
onClick={(evt) => downloadFeedClicked(version, isPublic)}
132153
>
133154
<Glyphicon glyph='download' /><span className='hidden-xs'> {getMessage(messages, 'download')}</span><span className='hidden-xs hidden-sm'> {getMessage(messages, 'feed')}</span>
134155
</Button>
135156

136157
{/* "Load for Editing" Button */}
137-
{isModuleEnabled('editor') && !this.props.isPublic
158+
{isModuleEnabled('editor') && !isPublic
138159
? <Button bsStyle='success'
139-
disabled={!this.props.hasVersions}
160+
disabled={!hasVersions}
140161
onClick={(evt) => {
141162
this.refs.confirm.open({
142163
title: getMessage(messages, 'load'),
143164
body: getMessage(messages, 'confirmLoad'),
144-
onConfirm: () => { this.props.loadFeedVersionForEditing(version) }
165+
onConfirm: () => { loadFeedVersionForEditing(version) }
145166
})
146167
}}
147168
>
@@ -151,15 +172,15 @@ export class VersionButtonToolbar extends Component {
151172
}
152173

153174
{/* "Delete Version" Button */}
154-
{!this.props.isPublic
175+
{!isPublic
155176
? <Button
156177
bsStyle='danger'
157-
disabled={this.props.deleteDisabled || !this.props.hasVersions || typeof this.props.deleteFeedVersionConfirmed === 'undefined'}
178+
disabled={deleteDisabled || !hasVersions || typeof deleteFeedVersionConfirmed === 'undefined'}
158179
onClick={(evt) => {
159180
this.refs.confirm.open({
160181
title: `${getMessage(messages, 'delete')} ${getMessage(messages, 'version')}`,
161182
body: getMessage(messages, 'confirmDelete'),
162-
onConfirm: () => { this.props.deleteFeedVersionConfirmed(version) }
183+
onConfirm: () => { deleteFeedVersionConfirmed(version) }
163184
})
164185
}}
165186
>
@@ -176,17 +197,22 @@ export class VersionButtonToolbar extends Component {
176197

177198
class VersionSectionSelector extends Component {
178199
static propTypes = {
200+
validationJob: PropTypes.object,
179201
version: PropTypes.object,
180202
feedVersionIndex: PropTypes.number,
181203
versionSection: PropTypes.string
182204
}
183205
renderIssuesLabel (version) {
184-
const color = version.validationSummary.loadStatus !== 'SUCCESS'
206+
const color = this.props.validationJob
207+
? 'warning'
208+
: version.validationSummary.loadStatus !== 'SUCCESS'
185209
? 'danger'
186210
: version.validationSummary.errorCount
187211
? 'warning'
188212
: 'success'
189-
const text = version.validationSummary.loadStatus !== 'SUCCESS'
213+
const text = this.props.validationJob
214+
? <span>processing <Icon className='fa-spin' type='refresh' /></span>
215+
: version.validationSummary.loadStatus !== 'SUCCESS'
190216
? 'critical error'
191217
: version.validationSummary.errorCount
192218
return (

lib/manager/containers/ActiveFeedVersionNavigator.js

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,33 @@ import { downloadGtfsPlusFeed } from '../../gtfsplus/actions/gtfsplus'
1717

1818
const mapStateToProps = (state, ownProps) => {
1919
let feedVersionIndex
20-
const routeVersionIndex = parseInt(ownProps.routeParams.feedVersionIndex, 10)
21-
const hasVersionIndex = typeof ownProps.routeParams.feedVersionIndex !== 'undefined'
20+
const {routeParams, feedSource} = ownProps
21+
const {feedVersions, id} = feedSource
22+
const {feedVersionIndex: fvi, subpage} = routeParams
23+
const routeVersionIndex = parseInt(fvi, 10)
24+
const hasVersionIndex = typeof fvi !== 'undefined'
2225

23-
if (ownProps.feedSource && typeof ownProps.feedSource.feedVersions !== 'undefined') {
26+
if (feedSource && typeof feedVersions !== 'undefined') {
2427
if ((hasVersionIndex && isNaN(routeVersionIndex)) ||
25-
routeVersionIndex > ownProps.feedSource.feedVersions.length ||
28+
routeVersionIndex > feedVersions.length ||
2629
routeVersionIndex < 0) {
2730
console.log(`version index ${routeVersionIndex} is invalid`)
2831

29-
// CANNOT use browserHistory.push in middle of state transition
32+
// TODO: CANNOT use browserHistory.push in middle of state transition
3033
// browserHistory.push(`/feed/${feedSourceId}`)
31-
window.location.href = `/feed/${ownProps.feedSource.id}`
34+
window.location.href = `/feed/${id}`
3235
} else {
3336
feedVersionIndex = hasVersionIndex
3437
? routeVersionIndex
35-
: ownProps.feedSource.feedVersions.length
38+
: feedVersions.length
3639
}
3740
}
38-
41+
const { jobs } = state.status.jobMonitor
42+
const validationJob = feedVersionIndex >= 1 && jobs.find(j => j.type === 'VALIDATE_FEED' && j.feedVersion.id === feedVersions[feedVersionIndex - 1].id)
3943
return {
4044
feedVersionIndex,
41-
versionSection: ownProps.routeParams.subpage
45+
validationJob,
46+
versionSection: subpage
4247
}
4348
}
4449

lib/style.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,25 @@
128128
.gtfs-map-select > .Select > .Select-menu-outer {
129129
z-index: 2000;
130130
}
131+
132+
.loading-ellipsis:after {
133+
overflow: hidden;
134+
display: inline-block;
135+
vertical-align: bottom;
136+
-webkit-animation: ellipsis steps(4,end) 900ms infinite;
137+
animation: ellipsis steps(4,end) 900ms infinite;
138+
content: "\2026"; /* ascii code for the ellipsis character */
139+
width: 0px;
140+
}
141+
142+
@keyframes ellipsis {
143+
to {
144+
width: 1.25em;
145+
}
146+
}
147+
148+
@-webkit-keyframes ellipsis {
149+
to {
150+
width: 1.25em;
151+
}
152+
}

0 commit comments

Comments
 (0)