diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2016-12-19 14:07:32 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-12-19 14:07:32 +0100 |
commit | 2a91ab92dac4203e4410f5b2ab0ffbd1a6efac0e (patch) | |
tree | c711f5da56f2f4ef0dd90f897112372223ece703 /server/sonar-web/src/main/js/apps/overview | |
parent | afb6610a84b5f237bd036c900dd1a20ca8526311 (diff) | |
download | sonarqube-2a91ab92dac4203e4410f5b2ab0ffbd1a6efac0e.tar.gz sonarqube-2a91ab92dac4203e4410f5b2ab0ffbd1a6efac0e.zip |
SONAR-7674 Add Activity Stream interface (#1459)
Diffstat (limited to 'server/sonar-web/src/main/js/apps/overview')
-rw-r--r-- | server/sonar-web/src/main/js/apps/overview/actions.js (renamed from server/sonar-web/src/main/js/apps/overview/events/EventsListFilter.js) | 42 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/overview/events/AnalysesList.js | 118 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/overview/events/Analysis.js (renamed from server/sonar-web/src/main/js/apps/overview/events/Event.js) | 59 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/overview/events/EventsList.js | 149 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/overview/meta/Meta.js | 8 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/overview/styles.css | 10 |
6 files changed, 171 insertions, 215 deletions
diff --git a/server/sonar-web/src/main/js/apps/overview/events/EventsListFilter.js b/server/sonar-web/src/main/js/apps/overview/actions.js index cfd142fd3e8..99136099f50 100644 --- a/server/sonar-web/src/main/js/apps/overview/events/EventsListFilter.js +++ b/server/sonar-web/src/main/js/apps/overview/actions.js @@ -17,36 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; -import Select from 'react-select'; -import { translate } from '../../../helpers/l10n'; +// @flow +import * as api from '../../api/projectActivity'; +import { receiveProjectActivity } from '../../store/projectActivity/duck'; +import { onFail } from '../../store/rootActions'; -const TYPES = ['All', 'Version', 'Alert', 'Profile', 'Other']; +const PAGE_SIZE = 5; -const EventsListFilter = ({ currentFilter, onFilter }) => { - const handleChange = selected => onFilter(selected.value); - - const options = TYPES.map(type => { - return { - value: type, - label: translate('event.category', type) - }; - }); - - return ( - <Select - value={currentFilter} - options={options} - clearable={false} - searchable={false} - onChange={handleChange} - style={{ width: '125px' }}/> - ); -}; - -EventsListFilter.propTypes = { - onFilter: React.PropTypes.func.isRequired, - currentFilter: React.PropTypes.string.isRequired -}; - -export default EventsListFilter; +export const fetchRecentProjectActivity = (project: string) => (dispatch: Function) => ( + api.getProjectActivity(project, { pageSize: PAGE_SIZE }).then( + ({ analyses, paging }) => dispatch(receiveProjectActivity(project, analyses, paging)), + onFail(dispatch) + ) +); diff --git a/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.js b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.js new file mode 100644 index 00000000000..842a9680310 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/events/AnalysesList.js @@ -0,0 +1,118 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +// @flow +import React from 'react'; +import { Link } from 'react-router'; +import { connect } from 'react-redux'; +import Analysis from './Analysis'; +import { translate } from '../../../helpers/l10n'; +import { fetchRecentProjectActivity } from '../actions'; +import { getProjectActivity } from '../../../store/rootReducer'; +import { getAnalyses } from '../../../store/projectActivity/duck'; + +type Props = { + analyses?: Array<*>, + project: string; + fetchRecentProjectActivity: (project: string) => Promise<*>; +} + +class AnalysesList extends React.Component { + mounted: boolean; + props: Props; + + state = { + loading: true + }; + + componentDidMount () { + this.mounted = true; + this.fetchData(); + } + + componentDidUpdate (prevProps: Props) { + if (prevProps.project !== this.props.project) { + this.fetchData(); + } + } + + componentWillUnmount () { + this.mounted = false; + } + + fetchData () { + this.setState({ loading: true }); + this.props.fetchRecentProjectActivity(this.props.project).then(() => { + if (this.mounted) { + this.setState({ loading: false }); + } + }); + } + + renderList (analyses) { + if (!analyses.length) { + return ( + <p className="spacer-top note"> + {translate('no_results')} + </p> + ); + } + + return ( + <ul className="spacer-top"> + {analyses.map(analysis => ( + <Analysis key={analysis.key} analysis={analysis}/> + ))} + </ul> + ); + } + + render () { + const { analyses } = this.props; + const { loading } = this.state; + + if (loading || !analyses) { + return null; + } + + return ( + <div className="overview-meta-card"> + <h4 className="overview-meta-header"> + {translate('project_activity.page')} + </h4> + + {this.renderList(analyses)} + + <div className="spacer-top small"> + <Link to={{ pathname: '/project/activity', query: { id: this.props.project } }}> + {translate('show_more')} + </Link> + </div> + </div> + ); + } +} + +const mapStateToProps = (state, ownProps: Props) => ({ + analyses: getAnalyses(getProjectActivity(state), ownProps.project) +}); + +const mapDispatchToProps = { fetchRecentProjectActivity }; + +export default connect(mapStateToProps, mapDispatchToProps)(AnalysesList); diff --git a/server/sonar-web/src/main/js/apps/overview/events/Event.js b/server/sonar-web/src/main/js/apps/overview/events/Analysis.js index acef6b789cb..a168b08329d 100644 --- a/server/sonar-web/src/main/js/apps/overview/events/Event.js +++ b/server/sonar-web/src/main/js/apps/overview/events/Analysis.js @@ -18,39 +18,36 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import moment from 'moment'; - -import { EventType } from '../propTypes'; +import Events from '../../projectActivity/components/Events'; +import FormattedDate from '../../../components/ui/FormattedDate'; import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin'; import { translate } from '../../../helpers/l10n'; +import type { Analysis as AnalysisType } from '../../../store/projectActivity/duck'; -const Event = ({ event }) => { - return ( - <TooltipsContainer> - <li className="spacer-top"> - <p> - <strong className="js-event-type"> - {translate('event.category', event.type)} - </strong> - {': '} - <span className="js-event-name">{event.name}</span> - {event.text && ( - <i - className="spacer-left icon-help" - data-toggle="tooltip" - title={event.text}/> - )} - </p> - <p className="note little-spacer-top js-event-date"> - {moment(event.date).format('LL')} - </p> - </li> - </TooltipsContainer> - ); -}; +export default class Analysis extends React.Component { + props: { + analysis: AnalysisType + }; -Event.propTypes = { - event: EventType.isRequired -}; + render () { + const { analysis } = this.props; -export default Event; + return ( + <TooltipsContainer> + <li className="overview-analysis"> + <div className="small little-spacer-bottom"> + <strong> + <FormattedDate date={analysis.date} format="LL"/> + </strong> + </div> + + {analysis.events.length > 0 ? ( + <Events events={analysis.events} canAdmin={false}/> + ) : ( + <span className="note">{translate('project_activity.project_analyzed')}</span> + )} + </li> + </TooltipsContainer> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/overview/events/EventsList.js b/server/sonar-web/src/main/js/apps/overview/events/EventsList.js deleted file mode 100644 index 3b35a24d690..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/events/EventsList.js +++ /dev/null @@ -1,149 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import moment from 'moment'; -import React from 'react'; -import shallowCompare from 'react-addons-shallow-compare'; - -import Event from './Event'; -import EventsListFilter from './EventsListFilter'; -import { getEvents } from '../../../api/events'; -import { translate } from '../../../helpers/l10n'; - -const LIMIT = 5; - -export default class EventsList extends React.Component { - state = { - events: [], - limited: true, - filter: 'All' - }; - - componentDidMount () { - this.mounted = true; - this.fetchEvents(); - } - - shouldComponentUpdate (nextProps, nextState) { - return shallowCompare(this, nextProps, nextState); - } - - componentDidUpdate (nextProps) { - if (nextProps.component !== this.props.component) { - this.fetchEvents(); - } - } - - componentWillUnmount () { - this.mounted = false; - } - - fetchEvents () { - getEvents(this.props.component.key).then(events => { - if (this.mounted) { - const nextEvents = events.map(event => { - return { - id: event.id, - date: moment(event.dt).toDate(), - type: event.c, - name: event.n, - text: event.ds - }; - }); - - this.setState({ events: nextEvents }); - } - }); - } - - limitEvents (events) { - return this.state.limited ? events.slice(0, LIMIT) : events; - } - - filterEvents (events) { - if (this.state.filter === 'All') { - return events; - } else { - return events.filter(event => event.type === this.state.filter); - } - } - - handleClick (e) { - e.preventDefault(); - this.setState({ limited: !this.state.limited }); - } - - handleFilter (filter) { - this.setState({ filter }); - } - - renderMoreLink () { - const text = this.state.limited ? - translate('widget.events.show_all') : - translate('hide'); - - return ( - <p className="spacer-top note"> - <a onClick={this.handleClick.bind(this)} href="#">{text}</a> - </p> - ); - } - - renderList (events) { - if (events.length) { - return ( - <ul> - {events.map(event => ( - <Event key={event.id} event={event}/> - ))} - </ul> - ); - } else { - return ( - <p className="spacer-top note"> - {translate('no_results')} - </p> - ); - } - } - - render () { - const filteredEvents = this.filterEvents(this.state.events); - const events = this.limitEvents(filteredEvents); - - return ( - <div className="overview-meta-card"> - <div className="clearfix"> - <h4 className="pull-left overview-meta-header"> - {translate('widget.events.name')} - </h4> - <div className="pull-right"> - <EventsListFilter - currentFilter={this.state.filter} - onFilter={this.handleFilter.bind(this)}/> - </div> - </div> - - {this.renderList(events)} - - {filteredEvents.length > LIMIT && this.renderMoreLink()} - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/Meta.js b/server/sonar-web/src/main/js/apps/overview/meta/Meta.js index c034da4452b..26be7ed8ea2 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/Meta.js +++ b/server/sonar-web/src/main/js/apps/overview/meta/Meta.js @@ -23,7 +23,7 @@ import MetaKey from './MetaKey'; import MetaLinks from './MetaLinks'; import MetaQualityGate from './MetaQualityGate'; import MetaQualityProfiles from './MetaQualityProfiles'; -import EventsList from './../events/EventsList'; +import AnalysesList from '../events/AnalysesList'; import MetaSize from './MetaSize'; const Meta = ({ component, measures }) => { @@ -40,7 +40,7 @@ const Meta = ({ component, measures }) => { const shouldShowQualityProfiles = !isView && !isDeveloper && hasQualityProfiles; const shouldShowQualityGate = !isView && !isDeveloper && hasQualityGate; - const showShowEvents = isProject || isView || isDeveloper; + const showShowAnalyses = isProject || isView || isDeveloper; return ( <div className="overview-meta"> @@ -64,8 +64,8 @@ const Meta = ({ component, measures }) => { <MetaKey component={component}/> - {showShowEvents && ( - <EventsList component={component}/> + {showShowAnalyses && ( + <AnalysesList project={component.key}/> )} </div> ); diff --git a/server/sonar-web/src/main/js/apps/overview/styles.css b/server/sonar-web/src/main/js/apps/overview/styles.css index cb9c344b814..088b19ca7e2 100644 --- a/server/sonar-web/src/main/js/apps/overview/styles.css +++ b/server/sonar-web/src/main/js/apps/overview/styles.css @@ -319,6 +319,16 @@ box-sizing: border-box; } +.overview-analysis { + +} + +.overview-analysis + .overview-analysis { + margin-top: 8px; + padding-top: 8px; + border-top: 1px solid #e6e6e6; +} + /* * Other */ |