]> source.dussan.org Git - sonarqube.git/commitdiff
SONARCLOUD-66 Move and improve contact form
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Fri, 15 Jun 2018 16:14:31 +0000 (18:14 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 21 Jun 2018 18:21:29 +0000 (20:21 +0200)
server/sonar-web/src/main/js/app/components/GlobalFooterSonarCloud.tsx
server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/GlobalFooterSonarCloud-test.tsx.snap
server/sonar-web/src/main/js/app/styles/components/alerts.css
server/sonar-web/src/main/js/app/theme.js
server/sonar-web/src/main/js/apps/about/routes.ts
server/sonar-web/src/main/js/apps/about/sonarcloud/Contact.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/about/sonarcloud/Footer.tsx
server/sonar-web/src/main/js/apps/about/sonarcloud/style.css

index d1ffc010768e93bf802c2132929f89d83445e960..05568542aebb2d5a92e8c6e34f5b17c1cbb4ff53 100644 (file)
@@ -52,7 +52,7 @@ export default function GlobalFooterSonarCloud() {
           <a href="https://community.sonarsource.com/c/help/sc">{translate('footer.help')}</a>
         </li>
         <li className="page-footer-menu-item">
-          <Link to="/contact">{translate('footer.contact_us')}</Link>
+          <Link to="/about/contact">{translate('footer.contact_us')}</Link>
         </li>
         <li className="page-footer-menu-item">
           <a href="https://sonarcloud.statuspage.io/">{translate('footer.status')}</a>
index 89bb14e6625ed198b73c7570a0ea13ec960f3dc4..42385bd429301e3f35ba633e66f2b929d58e38c9 100644 (file)
@@ -75,7 +75,7 @@ exports[`should render correctly 1`] = `
       <Link
         onlyActiveOnIndex={false}
         style={Object {}}
-        to="/contact"
+        to="/about/contact"
       >
         footer.contact_us
       </Link>
index 6cbd7fb0896a09a259539d4962c016b147ff1dff..0695abad28ff9282683a5e522e78ea10df95798b 100644 (file)
   color: #3c763d;
 }
 
+.alert-big {
+  font-size: var(--mediumFontSize);
+  padding: 10px 16px;
+}
+
 .page-notifs .alert {
   padding: 8px 10px;
 }
index 75f6dcdc4d13d501ee0b5780faeea44783411d64..b9e4c9670cceb878ae791de3718db12a282deca7 100644 (file)
@@ -111,11 +111,12 @@ module.exports = {
   // sonarcloud
   sonarcloudOrange: '#f60',
   sonarcloudOrangeDark: '#e65c00',
-  sonarcloudFontFamily:
-    "Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif",
   sonarcloudBlack200: '#f9f9fb',
   sonarcloudBlack300: '#cfd3d7',
   sonarcloudBlack700: '#434447',
   sonarcloudBlack800: '#2d3032',
-  sonarcloudBlack900: '#070706'
+  sonarcloudBlack900: '#070706',
+  sonarcloudBorderGray: 'rgba(207, 211, 215, 0.5)',
+  sonarcloudFontFamily:
+    "Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif"
 };
index a042681c66cc45f7909138939dbf5017a1bae3aa..315b604da61f0f1f8c2f1b4fa8e0b284c847f8fd 100644 (file)
@@ -29,6 +29,10 @@ const routes = [
     },
     childRoutes: isSonarCloud
       ? [
+          {
+            path: 'contact',
+            component: lazyLoad(() => import('./sonarcloud/Contact'))
+          },
           {
             path: 'sq',
             childRoutes: [
diff --git a/server/sonar-web/src/main/js/apps/about/sonarcloud/Contact.tsx b/server/sonar-web/src/main/js/apps/about/sonarcloud/Contact.tsx
new file mode 100644 (file)
index 0000000..dd0e78d
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info 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 * as React from 'react';
+import { Link } from 'react-router';
+import SonarCloudPage from './SonarCloudPage';
+import Select from '../../../components/controls/Select';
+import { isLoggedIn, Organization } from '../../../app/types';
+import './style.css';
+
+const CATEGORIES = [
+  { label: 'Commercial', value: 'commercial' },
+  { label: 'Product', value: 'product' },
+  { label: 'Operations / Service / Infrastructure', value: 'operations' }
+];
+
+interface State {
+  category: string;
+  organization: string;
+  subject: string;
+}
+
+export default class Contact extends React.PureComponent<{}, State> {
+  state: State = { category: '', organization: '', subject: '' };
+
+  getOrganizations = (organizations?: Organization[]) => {
+    return (organizations || []).map(org => ({
+      label: org.name,
+      value: org.key
+    }));
+  };
+
+  handleCategoryChange = ({ value }: { value: string }) => {
+    this.setState({ category: value });
+  };
+
+  handleOrganizationChange = ({ value }: { value: string }) => {
+    this.setState({ organization: value });
+  };
+
+  handleSubjectChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+    this.setState({ subject: event.currentTarget.value });
+  };
+
+  render() {
+    return (
+      <SonarCloudPage>
+        {({ currentUser, userOrganizations }) => (
+          <div className="page page-limited sc-page sc-contact-page">
+            <h1 className="sc-page-title">Contact us</h1>
+            <p className="alert alert-warning alert-big display-inline-block">
+              If you are looking for help with SonarCloud, our{' '}
+              <a
+                href="https://community.sonarsource.com/c/help/sc"
+                rel="noopener noreferrer"
+                target="_blank">
+                <strong>Support forum</strong>
+              </a>{' '}
+              is the best place to get help.
+            </p>
+            <br />
+            <p className="alert alert-warning alert-big display-inline-block">
+              Please contact us only if you couldn&apos;t solve your problem with the forum help.
+            </p>
+            {!isLoggedIn(currentUser) && (
+              <p>
+                You can{' '}
+                <Link to={{ pathname: '/sessions/new', query: { return_to: '/about/contact' } }}>
+                  log in to SonarCloud
+                </Link>{' '}
+                to automatically fill this form information and get better support.
+              </p>
+            )}
+            <form action="https://formspree.io/contact@sonarcloud.io" method="POST">
+              <div className="form-group">
+                <label htmlFor="contact-name">Name</label>
+                <input
+                  autoFocus={true}
+                  defaultValue={isLoggedIn(currentUser) ? currentUser.name : ''}
+                  id="contact-name"
+                  name="name"
+                  required={true}
+                  type="text"
+                />
+              </div>
+              <div className="form-group">
+                <label htmlFor="contact-email">Email</label>
+                <input
+                  defaultValue={isLoggedIn(currentUser) ? currentUser.email : ''}
+                  id="contact-email"
+                  name="_replyto"
+                  required={true}
+                  type="email"
+                />
+              </div>
+              <div className="form-group category-select">
+                <label htmlFor="contact-category">Category</label>
+                <Select
+                  id="contact-category"
+                  name="category"
+                  onChange={this.handleCategoryChange}
+                  options={CATEGORIES}
+                  placeholder="Choose a category"
+                  required={true}
+                  searchable={false}
+                  value={this.state.category}
+                />
+                <input
+                  className="category-select-helper"
+                  required={true}
+                  tabIndex={-1}
+                  value={this.state.category}
+                />
+              </div>
+              {isLoggedIn(currentUser) && (
+                <div className="form-group category-select">
+                  <label htmlFor="contact-organization">Organization concerned by the issue</label>
+                  <Select
+                    id="contact-organization"
+                    name="organization"
+                    onChange={this.handleOrganizationChange}
+                    options={this.getOrganizations(userOrganizations)}
+                    placeholder="Choose an organization"
+                    searchable={false}
+                    value={this.state.organization}
+                  />
+                </div>
+              )}
+              <div className="form-group">
+                <label htmlFor="contact-subject">Subject</label>
+                <input
+                  id="contact-subject"
+                  maxLength={70}
+                  onChange={this.handleSubjectChange}
+                  required={true}
+                  type="text"
+                  value={this.state.subject}
+                />
+                <input
+                  name="_subject"
+                  type="hidden"
+                  value={`[${this.state.category}] ${this.state.subject}`}
+                />
+              </div>
+              <div className="form-group">
+                <label htmlFor="contact-question">How can we help?</label>
+                <textarea
+                  className="form-control"
+                  id="contact-question"
+                  name="question"
+                  placeholder="Please describe precisely what is your issue..."
+                  required={true}
+                  rows={8}
+                />
+              </div>
+              <div className="form-group">
+                {
+                  // The following hidden input field must absolutely be kept
+                  // This is a "honeypot" field to avoid spam by fooling scrapers
+                }
+                <input name="_gotcha" type="text" />
+                <button type="submit">Send Request</button>
+              </div>
+              {isLoggedIn(currentUser) && (
+                <input name="login" type="hidden" value={currentUser.login} />
+              )}
+            </form>
+          </div>
+        )}
+      </SonarCloudPage>
+    );
+  }
+}
index f7634e2479f4331244c16b66175feed9c21094c7..4ee9a593fe81dab7fff2351572130dd79851ffca 100644 (file)
@@ -43,7 +43,7 @@ export default function Footer() {
                   className="sc-footer-link"
                   rel="noopener noreferrer"
                   target="_blank"
-                  to="/contact">
+                  to="/about/contact">
                   Contact Us
                 </Link>
               </li>
index f05b2e23fcb89eb7b72920ba5474ba4934a8cd54..40319514e6e2e68de481c4b4816880a64f1f0d15 100644 (file)
@@ -517,7 +517,7 @@ a.sc-top-nav-link:focus {
   width: 600px;
   margin: 20px auto;
   padding: 30px;
-  border: 1px solid rgba(207, 211, 215, 0.5);
+  border: 1px solid var(--sonarcloudBorderGray);
   border-radius: 5px;
   box-shadow: 0 3px 3px 0 rgba(7, 7, 6, 0.05);
 }
@@ -559,7 +559,7 @@ a.sc-top-nav-link:focus {
 }
 
 .sc-branch-feature + .sc-branch-feature {
-  border-top: 1px solid rgba(207, 211, 215, 0.5);
+  border-top: 1px solid var(--sonarcloudBorderGray);
 }
 
 .sc-branch-feature-left {
@@ -585,7 +585,7 @@ a.sc-top-nav-link:focus {
 
 .sc-vsts-start {
   display: inline-flex;
-  border: 1px solid rgba(207, 211, 215, 0.5);
+  border: 1px solid var(--sonarcloudBorderGray);
   border-radius: 5px;
   box-shadow: 0 3px 3px 0 rgba(7, 7, 6, 0.05);
 }
@@ -599,7 +599,7 @@ a.sc-top-nav-link:focus {
 }
 
 .sc-vsts-start-box + .sc-vsts-start-box {
-  border-left: 1px solid rgba(207, 211, 215, 0.5);
+  border-left: 1px solid var(--sonarcloudBorderGray);
 }
 
 .sc-vsts-start-title {
@@ -607,3 +607,126 @@ a.sc-top-nav-link:focus {
   font-size: 21px;
   font-weight: 300;
 }
+
+.sc-contact-page label {
+  display: block;
+  max-width: 100%;
+  margin-bottom: 5px;
+  font-size: 14px;
+  font-weight: 700;
+}
+
+.sc-contact-page input,
+.sc-contact-page textarea {
+  display: block;
+  width: 100%;
+  max-width: 100%;
+  height: 30px;
+  padding: 6px 12px;
+  font-size: 14px;
+  font-family: inherit;
+  line-height: 1.42857143;
+  color: #555;
+  background-color: white;
+  border: 1px solid var(--gray80);
+  border-radius: 2px;
+  transition: border-color ease 0.3s;
+}
+
+.sc-contact-page input:focus,
+.sc-contact-page textarea:focus {
+  border-color: #4b9fd5;
+  outline: none !important;
+  box-shadow: none;
+}
+
+.sc-contact-page input,
+.sc-contact-page .Select {
+  width: 300px;
+}
+
+.sc-contact-page .Select-control {
+  height: 30px;
+}
+
+.sc-contact-page .Select-value,
+.sc-contact-page .Select-placeholder {
+  margin-top: 4px;
+}
+
+.sc-contact-page .Select-placeholder {
+  font-style: italic;
+  color: var(--disableGrayText);
+}
+
+.sc-contact-page .Select-input {
+  box-shadow: none;
+}
+
+.sc-contact-page .category-select {
+  position: relative;
+}
+
+.sc-contact-page .category-select-helper {
+  opacity: 0;
+  z-index: -1;
+  position: absolute;
+  bottom: 0px;
+}
+
+.sc-contact-page input[name='_gotcha'] {
+  display: none !important;
+}
+
+.sc-contact-page textarea {
+  height: auto;
+}
+
+.sc-contact-page button {
+  padding: 6px 12px;
+  background: none;
+  color: var(--sonarcloudOrange);
+  border: 1px solid var(--sonarcloudOrange);
+  border-radius: 5px;
+  font-size: 12px;
+  font-weight: 400;
+  cursor: pointer;
+}
+
+.sc-contact-page button:hover {
+  color: white;
+  background-color: var(--sonarcloudOrange);
+}
+
+.sc-contact-page button:focus {
+  color: white;
+  background-color: var(--sonarcloudOrange);
+  outline: none;
+}
+
+.sc-contact-page button:active {
+  color: white;
+  background-color: var(--sonarcloudOrange);
+}
+
+.sc-contact-page .form-group {
+  margin-bottom: 15px;
+}
+
+.sc-contact-page .sc-page-title {
+  text-align: left;
+  margin-top: 0;
+}
+
+.sc-contact-page p {
+  margin-bottom: 20px;
+}
+
+.sc-contact-page a {
+  color: var(--sonarcloudOrange) !important;
+  border: none;
+}
+
+.sc-contact-page a:hover {
+  color: #cc5200 !important;
+}