]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7230 improve rendering of notifications page
authorStas Vilchik <vilchiks@gmail.com>
Wed, 27 Jan 2016 14:44:24 +0000 (15:44 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Thu, 28 Jan 2016 15:14:24 +0000 (16:14 +0100)
server/sonar-web/src/main/js/apps/account/components/GlobalNotifications.js
server/sonar-web/src/main/js/apps/account/components/Notifications.js
server/sonar-web/src/main/js/apps/account/components/ProjectNotification.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/components/ProjectNotifications.js
server/sonar-web/src/main/less/components/columns.less
server/sonar-web/src/main/less/init/misc.less

index 5f5de5de9b4170e13e9fb736b0820e98f381b392..180f22468f8ec86916ce66036fc64b83db0aa5a8 100644 (file)
@@ -24,12 +24,10 @@ import { translate } from '../../../helpers/l10n';
 
 export default function GlobalNotifications ({ notifications, channels }) {
   return (
-      <div>
-        <header className="page-header">
-          <h2 className="page-title">
-            {translate('my_profile.overall_notifications.title')}
-          </h2>
-        </header>
+      <section>
+        <h2 className="spacer-bottom">
+          {translate('my_profile.overall_notifications.title')}
+        </h2>
 
         <table className="form">
           <thead>
@@ -48,6 +46,6 @@ export default function GlobalNotifications ({ notifications, channels }) {
               checkboxId={(d, c) => `global_notifs_${d}_${c}`}
               checkboxName={(d, c) => `global_notifs[${d}.${c}]`}/>
         </table>
-      </div>
+      </section>
   );
 }
index 75784ae9aa1caf924e5ccc09545f75f8c7fe5fae..853d64d3c3555ce6e82ab983ac79f4b4664aeafc 100644 (file)
@@ -32,7 +32,7 @@ export default function Notifications ({ globalNotifications, projectNotificatio
           {translate('notification.dispatcher.information')}
         </p>
         <form id="notif_form" method="post" action={`${window.baseUrl}/account/update_notifications`}>
-          <div className="columns">
+          <div className="columns columns-overflow-visible">
             <div className="column-half">
               <GlobalNotifications
                   notifications={globalNotifications}
diff --git a/server/sonar-web/src/main/js/apps/account/components/ProjectNotification.js b/server/sonar-web/src/main/js/apps/account/components/ProjectNotification.js
new file mode 100644 (file)
index 0000000..1147830
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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 classNames from 'classnames';
+import React, { Component } from 'react';
+
+import NotificationsList from './NotificationsList';
+import { translate } from '../../../helpers/l10n';
+
+export default class ProjectNotification extends Component {
+  state = {
+    toDelete: false
+  }
+
+  handleRemoveProject (e) {
+    e.preventDefault();
+    if (this.state.toDelete) {
+      const { data, onRemoveProject } = this.props;
+      onRemoveProject(data.project);
+    } else {
+      this.setState({ toDelete: true });
+    }
+  }
+
+  render () {
+    const { data, channels } = this.props;
+    const buttonClassName = classNames('big-spacer-left', 'button-red', {
+      'active': this.state.toDelete
+    });
+
+    return (
+        <table key={data.project.internalId} className="form big-spacer-bottom">
+          <thead>
+            <tr>
+              <th>
+                <h3 className="display-inline-block">{data.project.name}</h3>
+                <button
+                    onClick={this.handleRemoveProject.bind(this)}
+                    className={buttonClassName}>
+                  {this.state.toDelete ? 'Sure?' : translate('delete')}
+                </button>
+              </th>
+              {channels.map(channel => (
+                  <th key={channel} className="text-center">
+                    <h4>{translate('notification.channel', channel)}</h4>
+                  </th>
+              ))}
+            </tr>
+          </thead>
+          <NotificationsList
+              notifications={data.notifications}
+              checkboxId={(d, c) => `project_notifs_${data.project.internalId}_${d}_${c}`}
+              checkboxName={(d, c) => `project_notifs[${data.project.internalId}][${d}][${c}]`}/>
+        </table>
+    );
+  }
+}
index 5683e50e1d87e431c055d2cc0dd5f52f373d8d0f..acdd691e3489d03f56c1a06dc4772414734be871 100644 (file)
@@ -20,7 +20,7 @@
 import React from 'react';
 import Select from 'react-select';
 
-import NotificationsList from './NotificationsList';
+import ProjectNotification from './ProjectNotification';
 import { translate } from '../../../helpers/l10n';
 import { getProjectsWithInternalId } from '../../../api/components';
 
@@ -46,28 +46,11 @@ export default function ProjectNotifications ({ notifications, channels, onAddPr
     onAddProject(project);
   };
 
-  const handleRemoveProject = (project) => (
-      (e) => {
-        e.preventDefault;
-        onRemoveProject(project);
-      }
-  );
-
   return (
-      <div>
-        <header className="page-header">
-          <h2 className="page-title">
-            {translate('my_profile.per_project_notifications.title')}
-          </h2>
-          <div className="pull-right">
-            <Select.Async
-                name="new_project"
-                style={{ width: '150px' }}
-                loadOptions={loadOptions}
-                onChange={handleAddProject}
-                placeholder="Add Project"/>
-          </div>
-        </header>
+      <section>
+        <h2 className="spacer-bottom">
+          {translate('my_profile.per_project_notifications.title')}
+        </h2>
 
         {!notifications.length && (
             <div className="note">
@@ -76,27 +59,24 @@ export default function ProjectNotifications ({ notifications, channels, onAddPr
         )}
 
         {notifications.map(p => (
-            <table key={p.project.internalId} className="form spacer-bottom">
-              <thead>
-                <tr>
-                  <th>
-                    <a onClick={handleRemoveProject(p.project)}
-                       className="spacer-right icon-delete js-delete-project" href="#"></a>
-                    <h3 className="display-inline-block">{p.project.name}</h3>
-                  </th>
-                  {channels.map(channel => (
-                      <th key={channel} className="text-center">
-                        <h4>{translate('notification.channel', channel)}</h4>
-                      </th>
-                  ))}
-                </tr>
-              </thead>
-              <NotificationsList
-                  notifications={p.notifications}
-                  checkboxId={(d, c) => `project_notifs_${p.project.internalId}_${d}_${c}`}
-                  checkboxName={(d, c) => `project_notifs[${p.project.internalId}][${d}][${c}]`}/>
-            </table>
+            <ProjectNotification
+                key={p.project.internalId}
+                data={p}
+                channels={channels}
+                onRemoveProject={onRemoveProject}/>
         ))}
-      </div>
+
+        <div className="huge-spacer-top panel bg-muted">
+          <span className="text-middle spacer-right">
+            Set notifications for:
+          </span>
+          <Select.Async
+              name="new_project"
+              style={{ width: '150px' }}
+              loadOptions={loadOptions}
+              onChange={handleAddProject}
+              placeholder="Search Project"/>
+        </div>
+      </section>
   );
 }
index dba2ee2a94ed9217cd6a6713a9c958f872f20b9e..03d5046114586df38379c27154b142ed81b1a0f8 100644 (file)
   overflow: hidden;
 }
 
+.columns-overflow-visible {
+  overflow: visible !important;
+}
+
 .column-half {
   float: left;
   width: 50%;
index d109674532f954eb696e3c3771860914324c8733..2595e67a970f8ee838ae3b4d4fa7b94d387c6ed2 100644 (file)
@@ -131,12 +131,13 @@ td.big-spacer-top    { padding-top: 16px; }
 
 // Background Color
 
-.bg-variant(@color) {
+.bg-variant(@color, @fontColor: #fff) {
   background-color: @color;
-  color: #fff;
+  color: @fontColor;
 }
 
 .bg-danger  { .bg-variant(@red); }
 .bg-warning { .bg-variant(@orange); }
 .bg-info    { .bg-variant(@blue); }
 .bg-success { .bg-variant(@green); }
+.bg-muted   { .bg-variant(@barBackgroundColor, inherit); }