summaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/account
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2016-02-01 14:08:20 +0100
committerStas Vilchik <vilchiks@gmail.com>2016-02-01 16:36:00 +0100
commitec156cb28a340cc394ec28ccf5d10033f80168b6 (patch)
tree20570c5f0f39abf1c7b3dd27cfa8cea5e12dd2bc /server/sonar-web/src/main/js/apps/account
parent1d52c906629322026b3ddaea26b37d7c0da38f7e (diff)
downloadsonarqube-ec156cb28a340cc394ec28ccf5d10033f80168b6.tar.gz
sonarqube-ec156cb28a340cc394ec28ccf5d10033f80168b6.zip
SONAR-7252 display widgets to my issues on "My Account" page
Diffstat (limited to 'server/sonar-web/src/main/js/apps/account')
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/Home.js6
-rw-r--r--server/sonar-web/src/main/js/apps/account/components/IssueWidgets.js211
-rw-r--r--server/sonar-web/src/main/js/apps/account/styles/account.css18
3 files changed, 231 insertions, 4 deletions
diff --git a/server/sonar-web/src/main/js/apps/account/components/Home.js b/server/sonar-web/src/main/js/apps/account/components/Home.js
index 2561d8864af..593618038db 100644
--- a/server/sonar-web/src/main/js/apps/account/components/Home.js
+++ b/server/sonar-web/src/main/js/apps/account/components/Home.js
@@ -22,6 +22,7 @@ import React from 'react';
import Favorites from './Favorites';
import FavoriteIssueFilters from './FavoriteIssueFilters';
import FavoriteMeasureFilters from './FavoriteMeasureFilters';
+import IssueWidgets from './IssueWidgets';
import { translate } from '../../../helpers/l10n';
const Home = ({ user, favorites, issueFilters, measureFilters }) => (
@@ -34,10 +35,7 @@ const Home = ({ user, favorites, issueFilters, measureFilters }) => (
</div>
<div className="column-third">
- <section>
- <h2 className="spacer-bottom">{translate('issues.page')}</h2>
- <p>Some cool issue widgets go here...</p>
- </section>
+ <IssueWidgets/>
</div>
<div className="column-third">
diff --git a/server/sonar-web/src/main/js/apps/account/components/IssueWidgets.js b/server/sonar-web/src/main/js/apps/account/components/IssueWidgets.js
new file mode 100644
index 00000000000..3fec0fe2cff
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/account/components/IssueWidgets.js
@@ -0,0 +1,211 @@
+/*
+ * 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 _ from 'underscore';
+import moment from 'moment';
+import React, { Component } from 'react';
+
+import SeverityHelper from '../../../components/shared/severity-helper';
+import { BarChart } from '../../../components/charts/bar-chart';
+import { getFacets, getFacet } from '../../../api/issues';
+import { translate } from '../../../helpers/l10n';
+import { formatMeasure } from '../../../helpers/measures';
+
+
+const BASE_QUERY = { resolved: false, assignees: '__me__' };
+
+
+function getTotalUrl () {
+ return window.baseUrl + '/account/issues#resolved=false';
+}
+
+function getSeverityUrl (severity) {
+ return window.baseUrl + '/account/issues#resolved=false|severities=' + severity;
+}
+
+function getProjectUrl (project) {
+ return window.baseUrl + '/account/issues#resolved=false|projectUuids=' + project;
+}
+
+function getPeriodUrl (createdAfter, createdBefore) {
+ return window.baseUrl + `/account/issues#resolved=false|createdAfter=${createdAfter}|createdBefore=${createdBefore}`;
+}
+
+
+export default class IssueWidgets extends Component {
+ state = {
+ loading: true
+ };
+
+ componentDidMount () {
+ this.fetchIssues();
+ }
+
+ fetchIssues () {
+ Promise.all([
+ this.fetchFacets(),
+ this.fetchByDate()
+ ]).then(responses => {
+ const facets = responses[0];
+ const byDate = responses[1];
+
+ this.setState({
+ loading: false,
+ total: facets.total,
+ severities: facets.severities,
+ projects: facets.projects,
+ byDate
+ });
+ });
+ }
+
+ fetchFacets () {
+ return getFacets(BASE_QUERY, ['severities', 'projectUuids']).then(r => {
+ const severities = _.sortBy(
+ _.findWhere(r.facets, { property: 'severities' }).values,
+ (facet) => window.severityComparator(facet.val)
+ );
+
+ const projects = _.findWhere(r.facets, { property: 'projectUuids' }).values.map(p => {
+ const base = _.findWhere(r.response.components, { uuid: p.val });
+ return Object.assign({}, p, base);
+ });
+
+ const total = r.response.total;
+
+ return { severities, projects, total };
+ });
+ }
+
+ fetchByDate () {
+ return getFacet(Object.assign({ createdInLast: '1w' }, BASE_QUERY), 'createdAt').then(r => r.facet);
+ }
+
+ handleByDateClick ({ value }) {
+ const created = moment(value);
+ const createdAfter = created.format('YYYY-MM-DD');
+ const createdBefore = created.add(1, 'days').format('YYYY-MM-DD');
+ window.location = getPeriodUrl(createdAfter, createdBefore);
+ }
+
+ renderByDate () {
+ const data = this.state.byDate.map((d, x) => {
+ return { x, y: d.count, value: d.val };
+ });
+ const xTicks = this.state.byDate.map(d => moment(d.val).format('dd'));
+ const xValues = this.state.byDate.map(d => d.count);
+
+ return (
+ <section className="abs-width-300 huge-spacer-top account-bar-chart">
+ <h4 className="spacer-bottom">
+ {translate('my_account.issue_widget.by_creation_date')}
+ </h4>
+ <BarChart
+ data={data}
+ xTicks={xTicks}
+ xValues={xValues}
+ barsWidth={20}
+ padding={[25, 0, 25, 0]}
+ height={80}
+ onBarClick={this.handleByDateClick.bind(this)}/>
+ </section>
+ );
+ }
+
+ render () {
+ return (
+ <section>
+ <h2 className="spacer-bottom">{translate('issues.page')}</h2>
+
+ {this.state.loading && (
+ <i className="spinner"/>
+ )}
+
+ {!this.state.loading && (
+ <section className="abs-width-300">
+ <table className="data zebra">
+ <tbody>
+ <tr>
+ <td>
+ <strong>{translate('total')}</strong>
+ </td>
+ <td className="thin nowrap text-right">
+ <a href={getTotalUrl()}>
+ {formatMeasure(this.state.total, 'SHORT_INT')}
+ </a>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </section>
+ )}
+
+ {!this.state.loading && (
+ <section className="abs-width-300 huge-spacer-top">
+ <h4 className="spacer-bottom">
+ {translate('my_account.issue_widget.by_severity')}
+ </h4>
+ <table className="data zebra">
+ <tbody>
+ {this.state.severities.map(s => (
+ <tr key={s.val}>
+ <td>
+ <SeverityHelper severity={s.val}/>
+ </td>
+ <td className="thin nowrap text-right">
+ <a href={getSeverityUrl(s.val)}>
+ {formatMeasure(s.count, 'SHORT_INT')}
+ </a>
+ </td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
+ </section>
+ )}
+
+ {!this.state.loading && (
+ <section className="abs-width-300 huge-spacer-top">
+ <h4 className="spacer-bottom">
+ {translate('my_account.issue_widget.by_project')}
+ </h4>
+ <table className="data zebra">
+ <tbody>
+ {this.state.projects.map(p => (
+ <tr key={p.val}>
+ <td>
+ {p.name}
+ </td>
+ <td className="thin nowrap text-right">
+ <a href={getProjectUrl(p.val)}>
+ {formatMeasure(p.count, 'SHORT_INT')}
+ </a>
+ </td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
+ </section>
+ )}
+
+ {!this.state.loading && this.renderByDate()}
+ </section>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/apps/account/styles/account.css b/server/sonar-web/src/main/js/apps/account/styles/account.css
index 4105365b0cb..28eb45f4a5e 100644
--- a/server/sonar-web/src/main/js/apps/account/styles/account.css
+++ b/server/sonar-web/src/main/js/apps/account/styles/account.css
@@ -27,3 +27,21 @@
.account-page {
padding-top: 102px;
}
+
+.account-bar-chart .bar-chart-bar {
+ fill: #4b9fd5;
+}
+
+.account-bar-chart .bar-chart-tick {
+ fill: #777;
+ font-size: 12px;
+ text-anchor: middle;
+}
+
+.account-bar-chart .histogram-tick {
+ text-anchor: end;
+}
+
+.account-bar-chart .histogram-value {
+ text-anchor: start;
+}