Skip to content
This repository has been archived by the owner on Oct 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #462 from 0xProject/test/serverless
Browse files Browse the repository at this point in the history
Add new Form integration and upgrade Node Version
  • Loading branch information
DennisSimon authored Jul 13, 2022
2 parents efd3156 + b9e7702 commit 921bc20
Show file tree
Hide file tree
Showing 11 changed files with 1,469 additions and 415 deletions.
27 changes: 27 additions & 0 deletions api/_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export function validateContactForm(entries: { [s: string]: string }): { [s: string]: string } {
const newErrors: { [s: string]: string } = {};
const requiredFields = [
'email',
'firstName',
'lastName',
'companyName',
'linkToProductOrWebsite',
'usageDescription',
'referral',
] as const;

for (const field of requiredFields) {
if (entries[field] === '') {
newErrors[field] = 'Field is required';
}
}

if (!newErrors.email && !/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(entries.email)) {
newErrors.email = 'No valid email address';
}

if (entries.chainOfInterest === 'Other' && entries.chainOfInterestOther === '') {
newErrors.chainOfInterestOther = 'Field is required';
}
return newErrors;
}
68 changes: 68 additions & 0 deletions api/contact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { VercelRequest, VercelResponse } from '@vercel/node';
import fetch from 'node-fetch';

import { validateContactForm } from './_utils';

// tslint:disable-next-line:no-default-export
export default async function handlerAsync(req: VercelRequest, res: VercelResponse): Promise<VercelResponse> {
const { body } = req;

if (Object.keys(body || {}).length === 0) {
return res.status(401).send('Body missing');
}

const errors = validateContactForm(req.body);

if (Object.keys(errors).length > 0) {
return res.status(422).send('Invalid payload');
}

const {
firstName,
lastName,
email,
linkToProductOrWebsite,
companyName,
typeOfBusiness,
role,
currentTradingVolume,
isApplicationLive,
productOfInterest,
chainOfInterest,
// usageDescription,
timelineForIntegration,
chainOfInterestOther,
isApiKeyRequired,
referral,
} = body;

const payload = {
oid: process.env.SALESFORCE_OID,
first_name: firstName,
last_name: lastName,
email,
website: linkToProductOrWebsite,
company: companyName,
'00N8c00000drpGS': typeOfBusiness,
'00N8c00000drpLI': role,
'00N8c00000drpLS': currentTradingVolume,
'00N8c00000drpLm': `${isApplicationLive}`,
'00N8c00000drpLw': productOfInterest,
'00N8c00000drr36': chainOfInterest,
'00N8c00000ds8KX': timelineForIntegration,
// '00N8c00000drpgB': usageDescription,
...(chainOfInterest === 'Other' ? { '00N8c00000drr3B': chainOfInterestOther } : {}),
'00N8c00000drpgL': `${isApiKeyRequired}`,
'00N8c00000drvT8': referral,
};

await fetch('https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams(payload as { [s: string]: string }),
});

return res.send('Created successfully');
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"moment-precise-range-plugin": "^1.3.0",
"moment-timezone": "^0.5.33",
"nice-color-palettes": "^3.0.0",
"node-fetch": "^2.6.2",
"numeral": "^2.0.6",
"patch-package": "^6.4.7",
"polished": "^1.9.2",
Expand Down Expand Up @@ -217,6 +218,7 @@
"unist-util-select": "^2.0.2",
"unist-util-visit": "^2.0.0",
"unist-util-visit-parents": "^3.0.0",
"vercel": "^27.0.1",
"webpack": "^4.39.2",
"webpack-cli": "3.3.7",
"webpack-dev-server": "^3.8.0",
Expand Down
6 changes: 6 additions & 0 deletions ts/components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ const linkRows: LinkRows[] = [
{ url: 'https://docs.0x.org/', text: 'Documentation', shouldOpenInNewTab: true },
{ url: constants.URL_GITHUB_ORG, text: 'GitHub', shouldOpenInNewTab: true },
{ url: 'https://docs.0x.org/protocol/docs', text: 'Protocol Spec', shouldOpenInNewTab: true },
{ url: '/#contact', text: 'Contact Us' },
{
url: 'https://ethereum.stackexchange.com/questions/tagged/0x',
text: 'Stack Exchange',
shouldOpenInNewTab: true,
},
],
},
{
Expand Down
10 changes: 2 additions & 8 deletions ts/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { FlexWrap } from 'ts/components/newLayout';

import { ThemeValuesInterface } from 'ts/style/theme';
import { zIndex } from 'ts/style/z_index';
import { constants } from 'ts/utils/constants';

import { WebsitePaths } from 'ts/types';

Expand Down Expand Up @@ -101,13 +100,8 @@ export const HeaderBase: React.FC<HeaderProps> = React.memo((props) => {
</NavLinks>

<MediaQuery minWidth={990}>
<TradeButton
bgColor={theme.headerButtonBg}
color="#ffffff"
href={constants.MATCHA_PRODUCTION_URL}
target="_blank"
>
Trade on Matcha
<TradeButton bgColor={theme.headerButtonBg} color="#ffffff" href={'#contact'}>
Get in Touch
</TradeButton>
</MediaQuery>

Expand Down
97 changes: 96 additions & 1 deletion ts/components/modals/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,86 @@ export const CheckBoxInput = (props: CheckBoxProps) => {
const { isSelected, label, onClick } = props;
return (
<Container onClick={onClick} className="flex items-center">
<Container marginRight="10px">
<Container marginRight="10px" minWidth={40}>
<CheckMark isChecked={isSelected} color={colors.brandLight} />
</Container>
<Label style={{ marginBottom: '0' }}>{label}</Label>
</Container>
);
};

interface WrappedReactNode {
item: React.ReactNode[];
value: string;
}

interface GenericDropdownProps {
items: string[] | readonly string[] | WrappedReactNode[];
name: string;
errors?: ErrorProps;
label: string;
defaultValue?: string;
onItemSelected?: (item: string) => any;
width?: InputWidth;
}

export const GenericDropdown = ({
items,
defaultValue = typeof items[0] === 'string' ? items[0] : items[0].value,
// tslint:disable-next-line:no-empty
onItemSelected = () => {},
width = InputWidth.Full,
errors,
name,
label,
}: GenericDropdownProps) => {
const id = `input-${name}`;
const [currentValue, setCurrentValue] = React.useState(defaultValue);
const isErrors = errors && errors.hasOwnProperty(name) && errors[name] !== null;
const errorMessage = isErrors ? errors[name] : null;

const handleChange = React.useCallback(
(event) => {
setCurrentValue(event.target.value);
onItemSelected(event.target.value);
},
[onItemSelected],
);

return (
<InputWrapper width={width}>
<Label htmlFor={id}>{label}</Label>
<StyledDropdownContainer id={id} isErrors={isErrors}>
<StyledDropdown
value={currentValue}
onChange={handleChange}
isErrors={isErrors}
style={{ color: currentValue === '' ? 'gray' : undefined }}
>
<option value="" disabled={true} selected={true} color="gray">
Select
</option>
{items.map((item: string | WrappedReactNode) => {
if (typeof item === 'string') {
return (
<option value={item} key={`item-${item}`}>
{item}
</option>
);
}
return (
<option value={item.value} key={`item-${item.value}`}>
{item.item}
</option>
);
})}
</StyledDropdown>
</StyledDropdownContainer>
{isErrors && <Error>{errorMessage}</Error>}
</InputWrapper>
);
};

export const Input = React.forwardRef((props: InputProps, ref?: React.Ref<HTMLInputElement>) => {
const { name, label, type, errors, defaultValue, onChange, width, className, placeholder, value } = props;
const id = `input-${name}`;
Expand Down Expand Up @@ -119,6 +191,29 @@ const StyledInput = styled.input`
}
`;

const StyledDropdown = styled.select`
height: auto !important;
outline: none;
width: 100%;
border: none;
background-color: #fff;
background-color: ${(props: InputProps) => props.isErrors && `#FDEDED`};
font-size: 1.111111111rem !important;
`;

const StyledDropdownContainer = styled.div`
background-color: #fff;
background-color: ${(props: InputProps) => props.isErrors && `#FDEDED`};
border-color: ${(props: InputProps) => props.isErrors && `#FD0000`};
border: 1px solid #d5d5d5;
padding: 16px 15px 14px;
color: #000;
height: auto !important;
font-size: 1.111111111rem !important;
outline: none;
width: 100%;
`;

const InputWrapper = styled.div<InputProps>`
position: relative;
flex-grow: ${(props) => props.width === InputWidth.Full && 1};
Expand Down
Loading

1 comment on commit 921bc20

@vercel
Copy link

@vercel vercel bot commented on 921bc20 Jul 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.