aboutsummaryrefslogtreecommitdiffstats
path: root/packages/website/ts/components/modals/modal_contact.tsx
diff options
context:
space:
mode:
authorSteve Klebanoff <steve.klebanoff@gmail.com>2019-01-03 07:17:26 +0800
committerSteve Klebanoff <steve.klebanoff@gmail.com>2019-01-03 07:17:26 +0800
commit65af195054e1f6de41d36a2d30d8342bef9752c0 (patch)
tree8559d71d6dd4269139afa5334b317f6efe90acd3 /packages/website/ts/components/modals/modal_contact.tsx
parent9f47f90c6e80ba9a61bcb6065fed1e2c6be8c5b7 (diff)
parent1ddf1087dd327b2ef35165518ee91eb457b84174 (diff)
downloaddexon-sol-tools-65af195054e1f6de41d36a2d30d8342bef9752c0.tar
dexon-sol-tools-65af195054e1f6de41d36a2d30d8342bef9752c0.tar.gz
dexon-sol-tools-65af195054e1f6de41d36a2d30d8342bef9752c0.tar.bz2
dexon-sol-tools-65af195054e1f6de41d36a2d30d8342bef9752c0.tar.lz
dexon-sol-tools-65af195054e1f6de41d36a2d30d8342bef9752c0.tar.xz
dexon-sol-tools-65af195054e1f6de41d36a2d30d8342bef9752c0.tar.zst
dexon-sol-tools-65af195054e1f6de41d36a2d30d8342bef9752c0.zip
Merge branch 'development' into feature/monorepo/release-notes
Diffstat (limited to 'packages/website/ts/components/modals/modal_contact.tsx')
-rw-r--r--packages/website/ts/components/modals/modal_contact.tsx278
1 files changed, 278 insertions, 0 deletions
diff --git a/packages/website/ts/components/modals/modal_contact.tsx b/packages/website/ts/components/modals/modal_contact.tsx
new file mode 100644
index 000000000..d9c276584
--- /dev/null
+++ b/packages/website/ts/components/modals/modal_contact.tsx
@@ -0,0 +1,278 @@
+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';
+
+interface Props {
+ theme?: GlobalStyle;
+ isOpen?: boolean;
+ onDismiss?: () => void;
+}
+
+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<Props> {
+ public state = {
+ isSubmitting: false,
+ isSuccessful: false,
+ errors: {},
+ };
+ public nameRef: React.RefObject<HTMLInputElement> = React.createRef();
+ public emailRef: React.RefObject<HTMLInputElement> = React.createRef();
+ public companyProjectRef: React.RefObject<HTMLInputElement> = React.createRef();
+ public linkRef: React.RefObject<HTMLInputElement> = React.createRef();
+ public commentsRef: React.RefObject<HTMLInputElement> = React.createRef();
+ public constructor(props: Props) {
+ super(props);
+ }
+ public render(): React.ReactNode {
+ const { isOpen, onDismiss } = this.props;
+ const { isSuccessful, errors } = this.state;
+
+ return (
+ <>
+ <DialogOverlay
+ style={{ background: 'rgba(0, 0, 0, 0.75)', zIndex: 30 }}
+ isOpen={isOpen}
+ onDismiss={onDismiss}
+ >
+ <StyledDialogContent>
+ <Form onSubmit={this._onSubmitAsync.bind(this)} isSuccessful={isSuccessful}>
+ <Heading color={colors.textDarkPrimary} size={34} asElement="h2">
+ Contact the 0x Core Team
+ </Heading>
+ <Paragraph isMuted={true} color={colors.textDarkPrimary}>
+ 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.
+ </Paragraph>
+ <InputRow>
+ <Input
+ name="name"
+ label="Your name"
+ type="text"
+ width={InputWidth.Half}
+ ref={this.nameRef}
+ required={true}
+ errors={errors}
+ />
+ <Input
+ name="email"
+ label="Your email"
+ type="email"
+ ref={this.emailRef}
+ required={true}
+ errors={errors}
+ width={InputWidth.Half}
+ />
+ </InputRow>
+ <InputRow>
+ <Input
+ name="companyOrProject"
+ label="Name of your project / company"
+ type="text"
+ ref={this.companyProjectRef}
+ required={true}
+ errors={errors}
+ />
+ </InputRow>
+ <InputRow>
+ <Input
+ name="link"
+ label="Do you have any documentation or a website?"
+ type="text"
+ ref={this.linkRef}
+ errors={errors}
+ />
+ </InputRow>
+ <InputRow>
+ <Input
+ name="comments"
+ label="Anything else?"
+ type="textarea"
+ ref={this.commentsRef}
+ errors={errors}
+ />
+ </InputRow>
+ <ButtonRow>
+ <Button
+ color="#5C5C5C"
+ isNoBorder={true}
+ isTransparent={true}
+ type="button"
+ onClick={this.props.onDismiss}
+ >
+ Back
+ </Button>
+ <Button>Submit</Button>
+ </ButtonRow>
+ </Form>
+ <Confirmation isSuccessful={isSuccessful}>
+ <Icon name="rocketship" size="large" margin={[0, 0, 'default', 0]} />
+ <Heading color={colors.textDarkPrimary} size={34} asElement="h2">
+ Thanks for contacting us.
+ </Heading>
+ <Paragraph isMuted={true} color={colors.textDarkPrimary}>
+ We'll get back to you soon. If you need quick support in the meantime, reach out to the
+ 0x team on Discord.
+ </Paragraph>
+ <Button onClick={this.props.onDismiss}>Done</Button>
+ </Confirmation>
+ </StyledDialogContent>
+ </DialogOverlay>
+ </>
+ );
+ }
+ private async _onSubmitAsync(e: Event): Promise<void> {
+ e.preventDefault();
+
+ const name = this.nameRef.current.value;
+ const email = this.emailRef.current.value;
+ const projectOrCompany = this.companyProjectRef.current.value;
+ const link = this.linkRef.current.value;
+ const comments = this.commentsRef.current.value;
+
+ this.setState({ ...this.state, errors: [], isSubmitting: true });
+
+ 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/leads', {
+ method: 'post',
+ mode: 'cors',
+ credentials: 'same-origin',
+ headers: {
+ 'content-type': 'application/json; charset=utf-8',
+ },
+ body: JSON.stringify(_.omitBy({ name, email, projectOrCompany, link, comments }, _.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;
+ }
+`;