Skip to content

Commit 3121b7f

Browse files
feat(ProjectViewer): show labels in project
1 parent cb55d3f commit 3121b7f

File tree

5 files changed

+195
-96
lines changed

5 files changed

+195
-96
lines changed

lib/common/components/Label.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React from 'react'
2+
import tinycolor from 'tinycolor2'
3+
4+
const getComplementaryColor = (cssHex, strength) => {
5+
const color = tinycolor(cssHex)
6+
7+
const complementary = color.isDark()
8+
? color.lighten(strength)
9+
: color.darken(strength + 10)
10+
return complementary.toHexString()
11+
}
12+
13+
export default function Label (props) {
14+
const { name, color } = props
15+
16+
return (
17+
<span
18+
className='feedLabel'
19+
style={{
20+
backgroundColor: color,
21+
color: getComplementaryColor(color, 45),
22+
borderColor: getComplementaryColor(color, 10)
23+
}}
24+
>
25+
{name}
26+
</span>
27+
)
28+
}

lib/manager/components/ProjectViewer.js

Lines changed: 93 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@ import WatchButton from '../../common/containers/WatchButton'
2727
import {getComponentMessages, getConfigProperty, isModuleEnabled} from '../../common/util/config'
2828
import DeploymentsPanel from '../containers/DeploymentsPanel'
2929
import FeedSourceTable from '../containers/FeedSourceTable'
30-
import ProjectSettings from './ProjectSettings'
31-
import CreateFeedSource from './CreateFeedSource'
32-
30+
import Label from '../../common/components/Label'
3331
import type {Props as ContainerProps} from '../containers/ActiveProjectViewer'
3432
import type {Feed, Project} from '../../types'
3533
import type {ManagerUserState} from '../../types/reducers'
3634

35+
import CreateFeedSource from './CreateFeedSource'
36+
import ProjectSettings from './ProjectSettings'
37+
3738
type Props = ContainerProps & {
3839
activeComponent: ?string,
3940
activeSubComponent: ?string,
@@ -175,72 +176,81 @@ export default class ProjectViewer extends Component<Props, State> {
175176
}
176177
const projectEditDisabled = this._isProjectEditDisabled()
177178
return (
178-
<ManagerPage
179-
ref='page'
180-
title={project.name}>
179+
<ManagerPage ref='page' title={project.name}>
181180
<Grid fluid>
182181
<Row className='project-header'>
183182
<Col xs={12}>
184183
<h3>
185184
<Icon className='icon-link' type='folder-open-o' />
186185
<Link to={`/project/${project.id}`}>{project.name}</Link>
187186
<ButtonToolbar className={`pull-right`}>
188-
{getConfigProperty('application.notifications_enabled')
189-
? <WatchButton
187+
{getConfigProperty('application.notifications_enabled') ? (
188+
<WatchButton
190189
isWatching={this._isWatchingProject()}
191190
user={user}
192191
target={project.id}
193-
subscriptionType='project-updated' />
194-
: null
195-
}
192+
subscriptionType='project-updated'
193+
/>
194+
) : null}
196195
</ButtonToolbar>
197196
</h3>
198-
<ul className='list-unstyled list-inline small' style={{marginBottom: '0px'}}>
199-
<li><Icon type='cloud-download' /> {project.autoFetchFeeds ? `${project.autoFetchHour}:${project.autoFetchMinute < 10 ? '0' + project.autoFetchMinute : project.autoFetchMinute}` : 'Auto fetch disabled'}</li>
197+
<ul
198+
className='list-unstyled list-inline small'
199+
style={{ marginBottom: '0px' }}
200+
>
201+
<li>
202+
<Icon type='cloud-download' />{' '}
203+
{project.autoFetchFeeds
204+
? `${project.autoFetchHour}:${
205+
project.autoFetchMinute < 10
206+
? '0' + project.autoFetchMinute
207+
: project.autoFetchMinute
208+
}`
209+
: 'Auto fetch disabled'}
210+
</li>
200211
</ul>
201212
</Col>
202213
</Row>
203214
<Tabs
204215
id='project-viewer-tabs'
205216
activeKey={activeComponent || 'sources'}
206217
mountOnEnter
207-
onSelect={this._selectTab}>
218+
onSelect={this._selectTab}
219+
>
208220
<Tab
209221
eventKey='sources'
210222
title={
211223
<span>
212224
<Glyphicon className='icon-link' glyph='list' />
213-
<span className='hidden-xs'>{this.messages('feeds.title')}</span>
225+
<span className='hidden-xs'>
226+
{this.messages('feeds.title')}
227+
</span>
214228
</span>
215-
}>
229+
}
230+
>
216231
<Row>
217232
<Col xs={12} sm={10}>
218-
{this.state.createMode
219-
? (
220-
<CreateFeedSource
221-
createFeedSource={createFeedSource}
222-
onCancel={this._toggleCreateView}
223-
projectId={project.id}
224-
/>
225-
)
226-
: (
227-
<FeedSourceTable
228-
onNewFeedSourceClick={this._toggleCreateView}
229-
project={project}
230-
/>
231-
)
232-
}
233+
{this.state.createMode ? (
234+
<CreateFeedSource
235+
createFeedSource={createFeedSource}
236+
onCancel={this._toggleCreateView}
237+
projectId={project.id}
238+
/>
239+
) : (
240+
<FeedSourceTable
241+
onNewFeedSourceClick={this._toggleCreateView}
242+
project={project}
243+
/>
244+
)}
233245
</Col>
234246
<Col xs={12} sm={2}>
235247
{this._renderPublicFeeds()}
236248
<ProjectSummaryPanel
237249
project={project}
238-
feedSources={project.feedSources || []} />
239-
<Panel header={<h3>What is a feed source?</h3>}>
240-
A feed source defines the location or upstream source of a{' '}
241-
GTFS feed. GTFS can be populated via automatic fetch,{' '}
242-
directly editing or uploading a zip file.
243-
</Panel>
250+
feedSources={project.feedSources || []}
251+
/>
252+
<ExplanatoryPanel project={project} />
253+
<LabelPanel project={project} />
244254
</Col>
245255
</Row>
246256
</Tab>
@@ -253,15 +263,17 @@ export default class ProjectViewer extends Component<Props, State> {
253263
<Glyphicon className='icon-link' glyph='cog' />
254264
<span className='hidden-xs'>{this.messages('settings')}</span>
255265
</span>
256-
}>
257-
{// Prevent rendering component if not active to ensure that
258-
// keyboard listener is not active while form is not visible.
259-
activeComponent === 'settings' &&
260-
<ProjectSettings
261-
activeSettingsPanel={activeSubComponent}
262-
projectEditDisabled={projectEditDisabled}
263-
{...this.props} />
264266
}
267+
>
268+
{// Prevent rendering component if not active to ensure that
269+
// keyboard listener is not active while form is not visible.
270+
activeComponent === 'settings' && (
271+
<ProjectSettings
272+
activeSettingsPanel={activeSubComponent}
273+
projectEditDisabled={projectEditDisabled}
274+
{...this.props}
275+
/>
276+
)}
265277
</Tab>
266278
</Tabs>
267279
</Grid>
@@ -295,3 +307,39 @@ class ProjectSummaryPanel extends Component<{feedSources: Array<Feed>, project:
295307
)
296308
}
297309
}
310+
311+
const LabelPanel = ({ project }) => {
312+
const { labels } = project
313+
314+
let labelBody = 'There are no labels in this project.'
315+
if (labels.length > 0) {
316+
labelBody = labels.map((label) => (
317+
// Disable this rule as it is not a form label
318+
// eslint-disable-next-line jsx-a11y/label-has-for
319+
<Label key={label.id} {...label} />
320+
))
321+
}
322+
323+
return (
324+
<Panel header={<h3>Labels</h3>}>
325+
<div className='feedLabelContainer'>
326+
{labelBody}
327+
</div>
328+
</Panel>
329+
)
330+
}
331+
332+
const ExplanatoryPanel = ({ project }) => {
333+
// If user has more than 3 labels, hide the feed source instruction
334+
if (project.labels.length <= 3) {
335+
return (
336+
<Panel header={<h3>What is a feed source?</h3>}>
337+
A feed source defines the location or upstream source of a GTFS feed.
338+
GTFS can be populated via automatic fetch, directly editing or uploading
339+
a zip file.
340+
</Panel>
341+
)
342+
}
343+
344+
return <div />
345+
}

0 commit comments

Comments
 (0)