// 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"
};
--- /dev/null
+/*
+ * 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'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>
+ );
+ }
+}
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);
}
}
.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 {
.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);
}
}
.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 {
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;
+}