This commit is contained in:
Sam Poder 2022-02-01 17:46:50 +08:00
commit cf9f5bffd2
11 changed files with 515 additions and 67 deletions

2
.gitignore vendored
View file

@ -1,5 +1,5 @@
.now
.env
.env.*
.next
node_modules
.DS_Store

312
components/bank/Form.js Normal file
View file

@ -0,0 +1,312 @@
import {
Box,
Label,
Input,
Button,
Select,
Text,
Container,
Textarea,
Divider,
Link,
Flex
} from 'theme-ui'
import { useRouter } from 'next/router'
import countries from '../../lib/countries'
export default function BankApplyForm() {
const { query } = useRouter()
const handleSubmit = e => {
e.preventDefault()
}
return (
<Box>
<Flex sx={{ flexDirection: 'column', pl: 3 }}>
<Text
sx={{
fontSize: [36, null, 48],
fontWeight: 'bold',
color: 'primary'
}}
>
Apply for Hack Club Bank
</Text>
<Text sx={{ fontSize: 18, mb: 2}}>
Hack Club Bank is open to all US-based Hack Clubs, hackathons, and
student-led nonprofits. There are three steps to getting on Hack Club Bank:
<ol>
<li>Fill out this form</li>
<li>
Have a 30 minute introductory call with a member of the Hack Club
Bank team
</li>
<li>
Sign the Hack Club Bank fiscal sponsorship agreement (sent right
after the call)
</li>
</ol>
If you have any questions, give us a shout at{' '}
<Link as="a" href="mailto:bank@hackclub.com">
bank@hackclub.com
</Link>{' '}
or in the <strong>#bank</strong> channel on the{' '}
<Link href="/slack">Hack Club Slack</Link>!
</Text>
</Flex>
<Base method="POST" action="/api/bank-apply">
<Text variant="headline" sx={{ color: 'primary' }}>
Your Project
</Text>
<Divider sx={{ borderColor: 'slate', mt: -2 }} />
<Field
label="Project name"
name="eventName"
placeholder="Windy City Hacks"
helperText="What's the name of your event or project?"
value={query.eventName}
required
/>
<Field
label="Project website"
name="eventWebsite"
placeholder="https://hackclub.com"
type="url"
helperText="If you don't have one yet, you can leave this blank."
value={query.eventWebsite}
/>
<Field
label="Project Location"
name="eventLocation"
placeholder="San Francisco, CA"
type="text"
helperText="If applicable, please format as: City, State."
value={query.eventLocation}
required
/>
<Label
htmlFor="eventCountry"
sx={{ color: 'smoke', fontSize: 18, pb: 2, my: 2 }}
>
Country
<Select
name="eventCountry"
defaultValue="Choose a country"
sx={{ bg: 'dark', my: 1 }}
>
<option value="" selected disabled>
Choose a country
</option>
{countries.countries.map(country => (
<option value={country} key={country}>
{country}
</option>
))}
</Select>
<HelperText>
We're testing out limited support for international organizations,
and want to know in advance if you're operating outside the US.
</HelperText>
</Label>
<Label htmlFor="eventDescription" sx={{ color: 'smoke', fontSize: 18, my: 2 }}>
Tell us about your project!
<Textarea name="eventDescription" sx={{ bg: 'dark', my: 1 }} required />
<HelperText>
1-2 sentences summarizing what you'd like to use Hack Club Bank for.
This is just to help us know what to expect during the call!
</HelperText>
</Label>
<Text variant="headline" sx={{ color: 'primary' }}>
Your Profile
</Text>
<Divider sx={{ borderColor: 'slate', mt: -2 }} />
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 2 }}>
<Field
label="First name"
name="firstName"
placeholder="Fiona"
value={query.firstName}
required
/>
<Field
label="Last name"
name="lastName"
placeholder="Hackworth"
value={query.lastName}
required
/>
</Box>
<Field
label="Email address"
name="userEmail"
placeholder="fiona@hackclub.com"
type="email"
value={query.userEmail}
required
/>
<Field
label="Phone"
name="userPhone"
placeholder="1-855-625-HACK"
type="tel"
helperText="We'll only use this if we need to get in touch with you urgently."
value={query.userPhone}
required
/>
<Field
label="Birthday"
name="userBirthday"
type="date"
width="fit-content"
sx={{height: '44px'}}
required
/>
<Label htmlFor="returningUser" sx={{ color: 'smoke', fontSize: 18, my: 2 }}>
Have you used Hack Club Bank before?
<Select
name="returningUser"
sx={{ bg: 'dark', mt: 1 }}
value={query.returningUser}
>
<option value="No, first time!">No, first time!</option>
<option value="Yes, I have used Hack Club Bank before">
Yes, I have used Hack Club Bank before
</option>
</Select>
</Label>
<Box sx={{ mb: 2}}>
<Text variant="subheadline" sx={{ mt: 3, mb: 1, display: 'block', fontSize: 3 }}>
Mailing Address
</Text>
<HelperText>
This is so we can send you some swag and goodies if you ever request
them!
</HelperText>
</Box>
<Field
label="Address (line 1)"
name="addressLine1"
placeholder="8605 Santa Monica Blvd"
type="text"
required
/>
<Field
label="Address (line 2)"
name="addressLine2"
placeholder="Suite 86294"
type="text"
/>
<Box
sx={{
display: 'grid',
gridTemplateColumns: '1fr 1fr',
columnGap: 2
}}
>
<Field
label="City"
name="addressCity"
placeholder="West Hollywood"
type="text"
required
/>
<Field
label="State"
name="addressState"
placeholder="CA"
type="text"
required
/>
<Field
label="Zip Code"
name="addressZip"
placeholder="90069"
type="text"
required
/>
<Field
label="Country"
name="addressCountry"
placeholder="USA"
type="text"
required
/>
</Box>
<Button
sx={{ bg: 'red', mt: [2, 3], py: 2, fontSize: 24 }}
type="submit"
onSubmit={handleSubmit}
>
Apply
</Button>
</Base>
</Box>
)
}
function Field({
placeholder,
label,
name,
type,
helperText,
width,
value,
onChange,
required,
sx
}) {
return (
<Box sx={{ my: 2 }}>
<Label htmlFor={name} sx={{ color: 'smoke', fontSize: 18 }}>
{label}
<Input
id={name}
placeholder={placeholder}
name={name}
type={type}
sx={{
bg: 'dark',
width: `${width ? width : '100%'}`,
my: helperText ? 1 : 0,
mt: 1,
...sx
}}
value={value}
onChange={onChange}
required={required}
/>
{helperText && <HelperText>{helperText}</HelperText>}
</Label>
</Box>
)
}
function HelperText({ children }) {
return (
<Text variant="caption" sx={{ color: 'muted', fontSize: 16 }}>
{children}
</Text>
)
}
function Base({ children, action, method }) {
return (
<Container
as="form"
sx={{ display: 'grid', gridTemplateColumns: '1fr' }}
action={action}
method={method}
variant="copy"
>
{children}
</Container>
)
}

View file

@ -42,39 +42,38 @@ export default function Signup() {
return (
<Base
method="get"
target="_blank"
action="https://hack.af/bank-apply"
action="/bank/apply"
>
<Field
label="Project name"
name="prefill_Event Name"
name="eventName"
placeholder="Windy City Hacks"
value={values.name}
onChange={e => setValues({ ...values, name: e.target.value })}
value={values.eventName}
onChange={e => setValues({ ...values, eventName: e.target.value })}
/>
<Box sx={{ display: 'flex', flexDirection: 'row', gap: 2 }}>
<Field
label="First name"
name="prefill_First Name"
name="firstName"
placeholder="Fiona"
value={values.first_name}
onChange={e => setValues({ ...values, first_name: e.target.value })}
value={values.firstName}
onChange={e => setValues({ ...values, firstName: e.target.value })}
/>
<Field
label="Last name"
name="prefill_Last Name"
name="lastName"
placeholder="Hackworth"
value={values.last_name}
onChange={e => setValues({ ...values, last_name: e.target.value })}
value={values.lastName}
onChange={e => setValues({ ...values, lastName: e.target.value })}
/>
</Box>
<Field
label="Email address"
name="prefill_Email Address"
name="userEmail"
placeholder="fiona@hackclub.com"
type="email"
value={values.email}
onChange={e => setValues({ ...values, email: e.target.value })}
value={values.userEmail}
onChange={e => setValues({ ...values, userEmail: e.target.value })}
/>
<Button sx={{ bg: 'blue', mt: [2, 3], py: 3 }} type="submit">{`Finish ${
11 - Object.values(values).filter(n => n !== '').length

View file

@ -1,8 +1,18 @@
import { Box, Container, Link, Text, Heading, Card, Grid } from 'theme-ui'
import {
Box,
Container,
Link,
Text,
Heading,
Card,
Flex,
Grid,
Button
} from 'theme-ui'
import { Fade } from 'react-reveal'
import Signup from './Signup'
import Timeline from './Timeline'
import Stats from './Stats'
import Signup from './Signup'
export default function Start() {
return (
@ -30,8 +40,8 @@ export default function Start() {
</Heading>
<Container variant="narrow" sx={{ color: 'muted' }}>
<Text variant="lead">
Open to all registered Hack Clubs, hackathons, and your
next amazing project.
Open to all registered Hack Clubs, hackathons, and your next
amazing project.
</Text>
</Container>
</Container>

View file

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react'
import { Text } from 'theme-ui'
import { Text, Box } from 'theme-ui'
import Stat from '../stat'
import api from '../../lib/api'
import { timeSince } from '../../lib/helpers'
@ -59,26 +59,19 @@ const Stats = props => {
}
return (
<div>
{/* styled-components has a rendering bug that applies classes
to incorrect components in this particular tree, but I didn't
have time to upgrade styled-components or fix root cause.
This <div> soup seemed to remove the symptoms in the UI for now.
- @thesephist */}
<div>
<Text
variant="lead"
fontSize={[2, 3]}
color={props.labelColor}
mt={[2, 4]}
mb={[2, 3]}
>
<span></span>
<Dot />
As of {timeSince(lastUpdated, false, true)}...
</Text>
</div>
<div>
<Box>
<Text
variant="lead"
fontSize={[2, 3]}
color={props.labelColor}
mt={[2, 4]}
mb={[2, 3]}
>
<Dot />
As of {timeSince(lastUpdated, false, true)}...
</Text>
<Box as="div">
<Stat {...props} value={raised} label="raised on Hack Club Bank" />
<Stat
{...props}
@ -86,9 +79,9 @@ const Stats = props => {
value={volume}
label="total amount transacted"
/>
</div>
</div>
</Box>
</Box>
)
};
}
export default Stats;
export default Stats

View file

@ -1,4 +1,4 @@
import { Box, Flex, Container, Text, Badge } from 'theme-ui'
import { Box, Flex, Container, Text, Badge, Link } from 'theme-ui'
import { Slide } from 'react-reveal'
import Icon from '../icon'
@ -25,7 +25,7 @@ function TimelineStep({ children }) {
content: '""',
background: '#3c4858',
height: ['420px', null, '4px'],
width: ['4px', null, '50%'],
width: ['4px', null, '48%'],
marginLeft: [26, null, 0],
marginTop: [null, null, '34px'],
position: 'absolute',
@ -69,12 +69,18 @@ function Circle({ children }) {
)
}
function Step({ icon, name, duration }) {
function Step({ icon, name, duration, href }) {
return (
<TimelineStep pb={4}>
<Slide left>
<Circle mr={[3, null, 0]} mb={[null, null, 4]}>
<Icon glyph={icon} size={48} />
<Circle>
{href ? (
<Link href={href} sx={{ cursor: 'pointer' }}>
<Icon glyph={icon} size={48} color="white" />
</Link>
) : (
<Icon glyph={icon} size={48} />
)}
</Circle>
<Container
sx={{
@ -101,7 +107,7 @@ function Step({ icon, name, duration }) {
{duration}
</Badge>
<Text
sx={{ color: 'white', fontSize: 24, maxWidth: [250, null, 500] }}
sx={{ color: 'white', fontSize: 24, maxWidth: [300, null, 550] }}
>
{name}
</Text>
@ -116,12 +122,13 @@ export default function RealTimeline() {
<Timeline px={3}>
<Step
icon="send"
name="Sign up, explore, order debit cards"
name="Submit an application telling us about your project"
duration="Step 1"
href="/bank/apply"
/>
<Step
icon="welcome"
name="Intro meeting with the Bank team"
name="Hop on an intro call with the Bank team"
duration="Step 2"
/>
<Step
@ -131,7 +138,7 @@ export default function RealTimeline() {
/>
<Step
icon="card"
name="Receive debit cards in the mail"
name="Invite your team &amp; start fundraising"
duration="Step 4"
mb={0}
/>

View file

@ -39,7 +39,11 @@ const nextConfig = {
permanent: true
},
{ source: '/slack_invite/', destination: '/slack/', permanent: true },
{ source: '/jobs/bank-tech-lead/', destination: '/jobs/lead-hacker/', permanent: true },
{
source: '/jobs/bank-tech-lead/',
destination: '/jobs/lead-hacker/',
permanent: true
},
{ source: '/workshops/slack/', destination: '/slack/', permanent: true },
{ source: '/community/', destination: '/slack/', permanent: true },
{ source: '/hack_camp/', destination: '/camp/', permanent: true },
@ -137,6 +141,10 @@ const nextConfig = {
source: '/sunsetting-som/',
destination: '/content/sunsetting-som/'
},
{
source: '/bank/apply/success',
destination: '/bank/success'
},
{
source: '/banner/',
destination: 'https://workshops.hackclub.com/banner/'
@ -158,9 +166,9 @@ const nextConfig = {
destination: 'https://workshops.hackclub.com/vip-newsletters/$1'
},
{
source: '/transparency/may-2020/',
destination: '/content/transparency/may-2020/'
},
source: '/transparency/may-2020/',
destination: '/content/transparency/may-2020/'
},
{
source: '/map/',
destination: 'https://map.hackclub.dev/'

56
pages/api/bank-apply.js Normal file
View file

@ -0,0 +1,56 @@
import AirtablePlus from 'airtable-plus'
const applicationsTable = new AirtablePlus({
baseID: 'apppALh5FEOKkhjLR',
apiKey: process.env.AIRTABLE_API_KEY,
tableName: 'Events'
})
export default async function handler(req, res) {
if (req.method === 'POST') {
const data = req.body
const application = await applicationsTable.create({
'First Name': data.firstName,
'Last Name': data.lastName,
'Email Address': data.userEmail,
'Phone Number': data.userPhone,
'Date of Birth': data.userBirthday,
'Event Name': data.eventName,
'Event Website': data.eventWebsite,
'Tell us about your event': data.eventDescription,
'Mailing Address': data.mailingAddress,
'Address Line 1': data.addressLine1,
'Address Line 2': data.addressLine2,
City: data.addressCity,
State: data.addressState,
'Zip Code': data.addressZip,
'Address Country': data.addressCountry,
Country: data.eventCountry,
'Event Location': data.eventLocation,
'Have you used Hack Club Bank for any previous events?':
data.returningUser
})
const url = process.env.BANK_NOTIFS_WEBHOOK
const body = JSON.stringify({
application
})
fetch(url, {
body,
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(r => {
res.writeHead(302, { Location: '/bank/apply/success' }).end()
console.log(r.statusText)
})
.catch(error => {
console.log(error)
res.json({ status: 'Something went wrong', error })
})
} else {
res.status(405).json({ status: 'error', error: 'Must send POST request' })
}
}

23
pages/bank/apply.js Normal file
View file

@ -0,0 +1,23 @@
import BankApplyForm from '../../components/bank/Form'
import { Box, Container, Card } from 'theme-ui'
import ForceTheme from '../../components/force-theme'
export default function Apply() {
return (
<Box
sx={{
backgroundImage:
'linear-gradient(to bottom,rgba(0, 0, 0, 0),rgba(0, 0, 0, 0.25) 25%,rgba(0, 0, 0, 0.625) 50%, rgba(0, 0, 0, 0.75) 100%), url("/bank/bg.jpg")',
py: 4,
backgroundAttachment: 'fixed'
}}
>
<ForceTheme theme="dark" />
<Container variant="copy">
<Card variant="primary">
<BankApplyForm />
</Card>
</Container>
</Box>
)
}

View file

@ -1,16 +1,15 @@
import { Box } from 'theme-ui'
import ForceTheme from '../components/force-theme'
import ForceTheme from '../../components/force-theme'
import Meta from '@hackclub/meta'
import Head from 'next/head'
import Nav from '../components/nav'
import Footer from '../components/footer'
import Landing from '../components/bank/Landing'
import Features from '../components/bank/Features'
import Testimonials from '../components/bank/Testimonials'
import Everything from '../components/bank/Everything'
import Start from '../components/bank/Start'
import Nonprofits from '../components/bank/Nonprofits'
import { useRouter } from 'next/router'
import Nav from '../../components/nav'
import Footer from '../../components/footer'
import Landing from '../../components/bank/Landing'
import Features from '../../components/bank/Features'
import Testimonials from '../../components/bank/Testimonials'
import Everything from '../../components/bank/Everything'
import Start from '../../components/bank/Start'
import Nonprofits from '../../components/bank/Nonprofits'
const styles = `
::selection {

41
pages/bank/success.js Normal file
View file

@ -0,0 +1,41 @@
import Head from 'next/head'
import { Container, Text, Flex, Link, Image } from 'theme-ui'
import ForceTheme from '../../components/force-theme'
export default function ApplicationSuccess() {
return (
<Container variant="copy">
<Head>
<meta httpEquiv="refresh" content="7;url=https://hackclub.com/bank" />
</Head>
<ForceTheme theme="dark" />
<Flex
sx={{
flexDirection: 'column',
justifyContent: 'center',
textAlign: 'center',
pt: 6
}}
>
<Image
src="https://cloud-5mgrt4f4s-hack-club-bot.vercel.app/0frame.svg"
alt="Dinosaur partying"
sx={{ width: '40%', mx: 'auto', mb: 4 }}
/>
<Text variant="title" sx={{ textAlign: 'center' }}>
Thanks for applying!
</Text>
<Text variant="lead">
Well reach out to schedule your introductory call within 24 hours on
weekdays. If you have any questions about your application, please
dont hesitate to reach out at{' '}
<Link as="a" href="mailto:bank@hackclub.com">
bank@hackclub.com
</Link>{' '}
or on the <Link href="/slack">Hack Club Slack</Link> in the{' '}
<strong>#bank</strong> channel.
</Text>
</Flex>
</Container>
)
}