import * as _ from 'lodash'; import * as React from 'react'; import styled from 'styled-components'; import { colors } from 'ts/style/colors'; import { DialogContent, DialogOverlay } from '@reach/dialog'; import '@reach/dialog/styles.css'; import { Button } from 'ts/components/button'; import { Icon } from 'ts/components/icon'; import { Input, InputWidth } from 'ts/components/modals/input'; import { Heading, Paragraph } from 'ts/components/text'; import { GlobalStyle } from 'ts/constants/globalStyle'; export enum ModalContactType { General = 'GENERAL', MarketMaker = 'MARKET_MAKER', } interface Props { theme?: GlobalStyle; isOpen?: boolean; onDismiss?: () => void; modalContactType: ModalContactType; } interface FormProps { isSuccessful?: boolean; isSubmitting?: boolean; } interface ErrorResponseProps { param: string; location: string; msg: string; } interface ErrorResponse { errors: ErrorResponseProps[]; } interface ErrorProps { [key: string]: string; } export class ModalContact extends React.Component { public static defaultProps = { modalContactType: ModalContactType.General, }; public state = { isSubmitting: false, isSuccessful: false, errors: {}, }; // shared fields public nameRef: React.RefObject = React.createRef(); public emailRef: React.RefObject = React.createRef(); public companyProjectRef: React.RefObject = React.createRef(); public commentsRef: React.RefObject = React.createRef(); // general lead fields public linkRef: React.RefObject = React.createRef(); // market maker lead fields public countryRef: React.RefObject = React.createRef(); public fundSizeRef: React.RefObject = React.createRef(); public constructor(props: Props) { super(props); } public render(): React.ReactNode { const { isOpen, onDismiss } = this.props; const { isSuccessful, errors } = this.state; return ( <>
Contact the 0x Core Team {this._renderFormContent(errors)}
Thanks for contacting us. We'll get back to you soon. If you need quick support in the meantime, reach out to the 0x team on Discord.
); } public _renderFormContent(errors: ErrorProps): React.ReactNode { switch (this.props.modalContactType) { case ModalContactType.MarketMaker: return this._renderMarketMakerFormContent(errors); case ModalContactType.General: default: return this._renderGeneralFormContent(errors); } } private _renderMarketMakerFormContent(errors: ErrorProps): React.ReactNode { return ( <> If you’re considering market making on 0x, we’re happy to answer your questions. Fill out the form so we can connect you with the right person to help you get started. ); } private _renderGeneralFormContent(errors: ErrorProps): React.ReactNode { return ( <> If you're considering building on 0x, we're happy to answer your questions. Fill out the form so we can connect you with the right person to help you get started. ); } private async _onSubmitAsync(e: Event): Promise { e.preventDefault(); let jsonBody; if (this.props.modalContactType === ModalContactType.MarketMaker) { jsonBody = { name: this.nameRef.current.value, email: this.emailRef.current.value, country: this.countryRef.current.value, fundSize: this.fundSizeRef.current.value, projectOrCompany: this.companyProjectRef.current.value, comments: this.commentsRef.current.value, }; } else { jsonBody = { name: this.nameRef.current.value, email: this.emailRef.current.value, projectOrCompany: this.companyProjectRef.current.value, link: this.linkRef.current.value, comments: this.commentsRef.current.value, }; } this.setState({ ...this.state, errors: [], isSubmitting: true }); const endpoint = this.props.modalContactType === ModalContactType.MarketMaker ? '/market_maker_leads' : '/leads'; try { // Disabling no-unbound method b/c no reason for _.isEmpty to be bound // tslint:disable:no-unbound-method const response = await fetch(`https://website-api.0xproject.com${endpoint}`, { method: 'post', mode: 'cors', credentials: 'same-origin', headers: { 'content-type': 'application/json; charset=utf-8', }, body: JSON.stringify(_.omitBy(jsonBody, _.isEmpty)), }); if (!response.ok) { const errorResponse: ErrorResponse = await response.json(); const errors = this._parseErrors(errorResponse.errors); this.setState({ ...this.state, isSubmitting: false, errors }); throw new Error('Request failed'); } this.setState({ ...this.state, isSuccessful: true }); } catch (e) { // Empty block } } private _parseErrors(errors: ErrorResponseProps[]): ErrorProps { const initialValue: {} = {}; return _.reduce( errors, (hash: ErrorProps, error: ErrorResponseProps) => { const { param, msg } = error; const key = param; hash[key] = msg; return hash; }, initialValue, ); } } // Handle errors: {"errors":[{"location":"body","param":"name","msg":"Invalid value"},{"location":"body","param":"email","msg":"Invalid value"}]} const InputRow = styled.div` width: 100%; flex: 0 0 auto; @media (min-width: 768px) { display: flex; justify-content: space-between; margin-bottom: 30px; } `; const ButtonRow = styled(InputRow)` @media (max-width: 768px) { display: flex; flex-direction: column; button:nth-child(1) { order: 2; } button:nth-child(2) { order: 1; margin-bottom: 10px; } } `; const StyledDialogContent = styled(DialogContent)` position: relative; max-width: 800px; background-color: #f6f6f6 !important; padding: 60px 60px !important; @media (max-width: 768px) { width: calc(100vw - 40px) !important; margin: 40px auto !important; padding: 30px 30px !important; } `; const Form = styled.form < FormProps > ` position: relative; transition: opacity 0.30s ease-in-out, visibility 0.30s ease-in-out; opacity: ${props => props.isSuccessful && `0`}; visibility: ${props => props.isSuccessful && `hidden`}; `; const Confirmation = styled.div < FormProps > ` position: absolute; top: 50%; text-align: center; width: 100%; left: 0; transition: opacity 0.30s ease-in-out, visibility 0.30s ease-in-out; transition-delay: 0.40s; padding: 60px 60px; transform: translateY(-50%); opacity: ${props => (props.isSuccessful ? `1` : `0`)}; visibility: ${props => (props.isSuccessful ? 'visible' : `hidden`)}; p { max-width: 492px; margin-left: auto; margin-right: auto; } `;