]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5770 add a analysis status to the component's header
authorStas Vilchik <vilchiks@gmail.com>
Fri, 25 Sep 2015 14:46:14 +0000 (16:46 +0200)
committerStas Vilchik <vilchiks@gmail.com>
Fri, 25 Sep 2015 14:51:32 +0000 (16:51 +0200)
server/sonar-web/src/main/js/api/ce.js
server/sonar-web/src/main/js/apps/nav/component/component-nav-meta.jsx
server/sonar-web/src/main/js/apps/nav/component/component-nav.jsx
server/sonar-web/src/main/js/components/shared/pending-icon.jsx [new file with mode: 0644]
server/sonar-web/src/main/less/init/icons.less
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index e31b8a4cbf3abd2402e0045b4908da54e6b870a7..2c019eff3b502bce4680bf022015a4779faacb09 100644 (file)
@@ -19,3 +19,8 @@ export function cancelTask (id) {
   let url = baseUrl + '/api/ce/cancel';
   return $.post(url, { id }).then(getTask.bind(null, id));
 }
+
+export function getTasksForComponent(componentId) {
+  let url = baseUrl + '/api/ce/project';
+  return $.get(url, { componentId });
+}
index 8ebb8e91a424fb2f7bb4d1a394e90c017e1588f7..eb64c48a64cf6b0c2161275da270bb5a17108bef 100644 (file)
@@ -1,12 +1,41 @@
 import React from 'react';
+import PendingIcon from '../../../components/shared/pending-icon';
 
 export default React.createClass({
   render() {
-    const version = this.props.version ? `Version ${this.props.version}` : null;
-    const snapshotDate = this.props.snapshotDate ? moment(this.props.snapshotDate).format('LLL') : null;
+    let metaList = [];
+
+    if (this.props.isInProgress) {
+      metaList.push(
+          <li key="isInProgress" data-toggle="tooltip" title={window.t('component_navigation.status.in_progress')}>
+            <i className="spinner" style={{ marginTop: '-1px' }}/> <span className="text-info">{window.t('background_task.status.IN_PROGRESS')}</span>
+          </li>
+      );
+    } else if (this.props.isPending) {
+      metaList.push(
+          <li key="isPending" data-toggle="tooltip" title={window.t('component_navigation.status.pending')}>
+            <PendingIcon/> <span>{window.t('background_task.status.PENDING')}</span>
+          </li>
+      );
+    } else if (this.props.isFailed) {
+      metaList.push(
+          <li key="isFailed" data-toggle="tooltip" title={window.t('component_navigation.status.failed')}>
+            <i className="icon-test-status-error"/> <span className="text-danger">{window.t('background_task.status.FAILED')}</span>
+          </li>
+      );
+    }
+
+    if (this.props.snapshotDate) {
+      metaList.push(<li key="snapshotDate">{moment(this.props.snapshotDate).format('LLL')}</li>);
+    }
+
+    if (this.props.version) {
+      metaList.push(<li key="version">Version {this.props.version}</li>);
+    }
+
     return (
         <div className="navbar-right navbar-context-meta">
-          {version} {snapshotDate}
+          <ul className="list-inline">{metaList}</ul>
         </div>
     );
   }
index 29d93a87b64ba68a630d14d26147d36c527a9364..ce2b4da804dfd073228c703fd4a221f7711ec646 100644 (file)
@@ -1,5 +1,8 @@
 import $ from 'jquery';
+import _ from 'underscore';
 import React from 'react';
+import {STATUSES} from '../../background-tasks/constants';
+import {getTasksForComponent} from '../../../api/ce';
 import ComponentNavFavorite from './component-nav-favorite';
 import ComponentNavBreadcrumbs from './component-nav-breadcrumbs';
 import ComponentNavMeta from './component-nav-meta';
@@ -11,13 +14,13 @@ export default React.createClass({
   },
 
   componentDidMount() {
-    this.loadDetails();
+    this.loadDetails().then(this.loadStatus);
   },
 
   loadDetails() {
     const url = `${window.baseUrl}/api/navigation/component`;
     const data = { componentKey: this.props.componentKey };
-    $.get(url, data).done(r => {
+    return $.get(url, data).done(r => {
       this.setState({
         component: r,
         conf: r.configuration || {}
@@ -25,6 +28,20 @@ export default React.createClass({
     });
   },
 
+  loadStatus(component) {
+    getTasksForComponent(component.uuid).done(r => {
+      this.setState({
+        isPending: !!_.findWhere(r.queue, { status: STATUSES.PENDING }),
+        isInProgress: !!_.findWhere(r.queue, { status: STATUSES.IN_PROGRESS }),
+        isFailed: r.current && r.current.status === STATUSES.FAILED
+      }, this.initTooltips);
+    });
+  },
+
+  initTooltips() {
+    $('[data-toggle="tooltip"]', React.findDOMNode(this)).tooltip({ container: 'body', placement: 'bottom' });
+  },
+
   render() {
     return (
         <div className="container">
@@ -37,6 +54,7 @@ export default React.createClass({
               breadcrumbs={this.state.component.breadcrumbs}/>
 
           <ComponentNavMeta
+              {...this.state}
               version={this.state.component.version}
               snapshotDate={this.state.component.snapshotDate}/>
 
diff --git a/server/sonar-web/src/main/js/components/shared/pending-icon.jsx b/server/sonar-web/src/main/js/components/shared/pending-icon.jsx
new file mode 100644 (file)
index 0000000..6749e97
--- /dev/null
@@ -0,0 +1,13 @@
+import React from 'react';
+
+export default React.createClass({
+  render() {
+    return (
+        <svg width="16" height="16" className="icon-pending">
+          <g transform="matrix(0.0364583,0,0,0.0364583,1,-0.166667)">
+            <path d="M224,136L224,248C224,250.333 223.25,252.25 221.75,253.75C220.25,255.25 218.333,256 216,256L136,256C133.667,256 131.75,255.25 130.25,253.75C128.75,252.25 128,250.333 128,248L128,232C128,229.667 128.75,227.75 130.25,226.25C131.75,224.75 133.667,224 136,224L192,224L192,136C192,133.667 192.75,131.75 194.25,130.25C195.75,128.75 197.667,128 200,128L216,128C218.333,128 220.25,128.75 221.75,130.25C223.25,131.75 224,133.667 224,136ZM328,224C328,199.333 321.917,176.583 309.75,155.75C297.583,134.917 281.083,118.417 260.25,106.25C239.417,94.083 216.667,88 192,88C167.333,88 144.583,94.083 123.75,106.25C102.917,118.417 86.417,134.917 74.25,155.75C62.083,176.583 56,199.333 56,224C56,248.667 62.083,271.417 74.25,292.25C86.417,313.083 102.917,329.583 123.75,341.75C144.583,353.917 167.333,360 192,360C216.667,360 239.417,353.917 260.25,341.75C281.083,329.583 297.583,313.083 309.75,292.25C321.917,271.417 328,248.667 328,224ZM384,224C384,258.833 375.417,290.958 358.25,320.375C341.083,349.792 317.792,373.083 288.375,390.25C258.958,407.417 226.833,416 192,416C157.167,416 125.042,407.417 95.625,390.25C66.208,373.083 42.917,349.792 25.75,320.375C8.583,290.958 0,258.833 0,224C0,189.167 8.583,157.042 25.75,127.625C42.917,98.208 66.208,74.917 95.625,57.75C125.042,40.583 157.167,32 192,32C226.833,32 258.958,40.583 288.375,57.75C317.792,74.917 341.083,98.208 358.25,127.625C375.417,157.042 384,189.167 384,224Z"/>
+          </g>
+        </svg>
+    );
+  }
+});
index b6f03b49d9152f15def41791e147321b518ff7c2..4fb95d59cc8dcc90fbf95983c047ff6ed90b3980 100644 (file)
@@ -320,7 +320,7 @@ a[class^="icon-"], a[class*=" icon-"] {
 }
 
 .icon-star-favorite {
-  animation: spin .6s forwards;
+  animation: spin-star .6s forwards;
 }
 
 .icon-star-favorite path {
@@ -329,7 +329,7 @@ a[class^="icon-"], a[class*=" icon-"] {
   fill-opacity: 1;
 }
 
-@keyframes spin {
+@keyframes spin-star {
   0% {
     transform: rotate(0deg);
   }
@@ -560,6 +560,26 @@ a[class^="icon-"], a[class*=" icon-"] {
   background-repeat: no-repeat;
 }
 
+.icon-pending {
+  position: relative;
+  top: -1px;
+}
+.icon-pending path {
+  fill: #777;
+  animation: animation-pending 2s linear infinite;
+}
+@keyframes animation-pending {
+  0% {
+    fill: #777;
+  }
+  50% {
+    fill: #aaa;
+  }
+  100% {
+    fill: #777;
+  }
+}
+
 
 /*
  * Spinner
index 10a5feaa0316fc3d0a562a8ab95c043221a533fe..45bb035fc6c4fb9bd8773155254324735c410046 100644 (file)
@@ -3035,3 +3035,22 @@ update_center.status.COMPATIBLE=Compatible
 update_center.status.INCOMPATIBLE=Incompatible
 update_center.status.REQUIRES_SYSTEM_UPGRADE=Requires system update
 update_center.status.DEPS_REQUIRE_SYSTEM_UPGRADE=Some of dependencies requires system update
+
+
+
+#------------------------------------------------------------------------------
+#
+# BACKGROUND TASKS
+#
+#------------------------------------------------------------------------------
+component_navigation.status.failed=The last analysis has failed.
+component_navigation.status.pending=There is a pending analysis.
+component_navigation.status.in_progress=The analysis is in progress.
+
+background_task.status.PENDING=Pending
+background_task.status.IN_PROGRESS=In Progress
+background_task.status.SUCCESS=Success
+background_task.status.FAILED=Failed
+background_task.status.CANCELED=Canceled
+
+