aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/overview
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2016-12-19 14:07:32 +0100
committerGitHub <noreply@github.com>2016-12-19 14:07:32 +0100
commit2a91ab92dac4203e4410f5b2ab0ffbd1a6efac0e (patch)
treec711f5da56f2f4ef0dd90f897112372223ece703 /server/sonar-web/src/main/js/apps/overview
parentafb6610a84b5f237bd036c900dd1a20ca8526311 (diff)
downloadsonarqube-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.js118
-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.js149
-rw-r--r--server/sonar-web/src/main/js/apps/overview/meta/Meta.js8
-rw-r--r--server/sonar-web/src/main/js/apps/overview/styles.css10
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
*/