diff options
-rw-r--r-- | packages/website/ts/@next/components/modals/input.tsx | 29 | ||||
-rw-r--r-- | packages/website/ts/@next/components/modals/modal_contact.tsx | 47 |
2 files changed, 69 insertions, 7 deletions
diff --git a/packages/website/ts/@next/components/modals/input.tsx b/packages/website/ts/@next/components/modals/input.tsx index 61663d906..d4d9206a2 100644 --- a/packages/website/ts/@next/components/modals/input.tsx +++ b/packages/website/ts/@next/components/modals/input.tsx @@ -11,27 +11,37 @@ interface InputProps { width?: InputWidth; label: string; type?: string; + errors?: ErrorProps; + isErrors?: boolean; } interface LabelProps { string: boolean; } +interface ErrorProps { + [key: string]: string; +} + export const Input = React.forwardRef((props: InputProps, ref?: React.Ref<HTMLInputElement>) => { - const { name, label, type } = props; + const { name, label, type, errors } = props; const id = `input-${name}`; const componentType = type === 'textarea' ? 'textarea' : 'input'; + const isErrors = errors.hasOwnProperty(name) && errors[name] !== null; + const errorMessage = isErrors ? errors[name] : null; return ( <InputWrapper {...props}> <Label htmlFor={id}>{label}</Label> - <StyledInput as={componentType} ref={ref} id={id} placeholder={label} {...props} /> + <StyledInput as={componentType} ref={ref} id={id} placeholder={label} isErrors={isErrors} {...props} /> + {isErrors && <Error>{errorMessage}</Error>} </InputWrapper> ); }); Input.defaultProps = { width: InputWidth.Full, + errors: {}, }; const StyledInput = styled.input` @@ -45,6 +55,9 @@ const StyledInput = styled.input` width: 100%; min-height: ${props => props.type === 'textarea' && `120px`}; + background-color: ${(props: InputProps) => props.isErrors && `#FDEDED`}; + border-color: ${(props: InputProps) => props.isErrors && `#FD0000`}; + &::placeholder { color: #C3C3C3; } @@ -68,3 +81,15 @@ const Label = styled.label` margin-bottom: 10px; display: inline-block; `; + +const Error = styled.span` + color: #FD0000; + font-size: .833333333rem; + line-height: 1em; + display: inline-block; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + transform: translateY(24px); +`; diff --git a/packages/website/ts/@next/components/modals/modal_contact.tsx b/packages/website/ts/@next/components/modals/modal_contact.tsx index f107b9174..b595c236d 100644 --- a/packages/website/ts/@next/components/modals/modal_contact.tsx +++ b/packages/website/ts/@next/components/modals/modal_contact.tsx @@ -24,6 +24,20 @@ interface FormProps { 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, @@ -40,7 +54,7 @@ export class ModalContact extends React.Component<Props> { } public render(): React.ReactNode { const {isOpen, onDismiss} = this.props; - const {isSuccessful} = this.state; + const {isSuccessful, errors} = this.state; return ( <> @@ -61,6 +75,7 @@ export class ModalContact extends React.Component<Props> { width={InputWidth.Half} ref={this.nameRef} required={true} + errors={errors} /> <Input name="email" @@ -68,6 +83,7 @@ export class ModalContact extends React.Component<Props> { type="email" ref={this.emailRef} required={true} + errors={errors} width={InputWidth.Half} /> </InputRow> @@ -78,6 +94,7 @@ export class ModalContact extends React.Component<Props> { type="text" ref={this.companyProjectRef} required={true} + errors={errors} /> </InputRow> <InputRow> @@ -86,6 +103,7 @@ export class ModalContact extends React.Component<Props> { label="Do you have any documentation or a website?" type="text" ref={this.linkRef} + errors={errors} /> </InputRow> <InputRow> @@ -94,6 +112,7 @@ export class ModalContact extends React.Component<Props> { label="Anything else?" type="textarea" ref={this.commentsRef} + errors={errors} /> </InputRow> <ButtonRow> @@ -129,24 +148,42 @@ export class ModalContact extends React.Component<Props> { const link = this.linkRef.current.value; const comments = this.commentsRef.current.value; - this.setState({ ...this.state, isSubmitting: true }); + this.setState({ ...this.state, errors: [], isSubmitting: true }); try { - await fetch('https://website-api.0x.org/leads', { + const response = await fetch('https://website-api.0x.org/leads', { method: 'post', mode: 'cors', credentials: 'same-origin', headers: { 'content-type': 'application/json; charset=utf-8', }, - body: JSON.stringify({ name, email, projectOrCompany, link, comments }), + 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) { - this.setState({ ...this.state, errors: [] }); + // Empty block } } + private _parseErrors(errors: ErrorResponseProps[]): ErrorProps { + return _ + .reduce(errors, (hash: ErrorProps, error: ErrorResponseProps) => { + const { param, msg } = error; + const key = param; + hash[key] = msg; + + return hash; + }, {}); + } } // Handle errors: {"errors":[{"location":"body","param":"name","msg":"Invalid value"},{"location":"body","param":"email","msg":"Invalid value"}]} |