Move /bank page to v3 (#167)
* initial migration
* port landing and feature components
* Features section
* Add event cards in Testimonials (no styles)
* add everything section
* everything but no styling
* fFix some styles in Everything.js
* Add Start section without the form yet
* Fix testimonials section grid
* Fix styles on Landing
* Styles on Everything section
* Fix testimonials cards
* Fix console error and testimonial cards
* Fix landing and style bugs
* Fix styles
* Add selection color
* Fix Grid on everything section
* Fetch transaction data from api
* Style stats
* Fix landing on mobile
* Fix some bugs and responsiveness
* Fix 🐛 so the form exists now
* Fix bugs & add timeline
* Fix Laptop in Grid
* Switch to Dark Laptop
* Fix Stat Text Size
* Form -> Airtable
* Change to hred to primary, align ModuleDetails card
* Remove dates
* Fix Event card to align items center
* Add mtop margin to transparency button
* Fix Run.js icon styles
* Make fiscal sponsor note more readable
* Add 2 tone colors btwn Start and Everything
* Flashing green dot
* Confusion
* Update components/bank/Timeline.js
Co-authored-by: Sam Poder <39828164+sampoder@users.noreply.github.com>
* Fix remaining style bugs
* small fix
* small fix
* Fix Form
* Fixes
- fix timeline horizontal
- green to hack club theme green
- more horizontal margin on everything section
- attempt to fix testimonial grid
* center 7 (percentage)
* Fix testimonial grid layout
* Fix landing
* Fix horizontal scroll on mobile
* Fix styles on event card
* Decrease font size and padding
* Actually fix it this time
* Fix styles
* Responsiveness and mobile styling
* Slight styles changes
* Fix Margins
* Neutralize
Co-authored-by: Sam Poder <39828164+sampoder@users.noreply.github.com>
222
components/bank/Everything.js
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
import {
|
||||
Box,
|
||||
Container,
|
||||
Heading,
|
||||
Text,
|
||||
Avatar,
|
||||
Badge,
|
||||
Link,
|
||||
Grid
|
||||
} from 'theme-ui'
|
||||
import Run from './Run'
|
||||
import { Fade } from 'react-reveal'
|
||||
import Icon from '../icon'
|
||||
|
||||
export default function Everything() {
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
pt: 6,
|
||||
pb: [3, 6],
|
||||
marginTop: 6,
|
||||
bg: 'darker'
|
||||
}}
|
||||
>
|
||||
<Container mb={[4, 5]} px={3} sx={{ textAlign: 'center' }}>
|
||||
<Heading variant="ultratitle" sx={{ color: 'smoke' }}>
|
||||
Everything you’ll need.
|
||||
</Heading>
|
||||
</Container>
|
||||
<Container px={[3, null, 5]}>
|
||||
<List>
|
||||
{Object.entries({
|
||||
'Legal entity with 501(c)(3) status': 'briefcase',
|
||||
'We do your taxes': 'checkmark',
|
||||
'Collect donations via card, check, or ACH': 'enter',
|
||||
'Share access with your whole team': 'member-add',
|
||||
'Bank account backed by Silicon Valley Bank': 'bank-account',
|
||||
'Negotiated nonprofit rates with Stripe': 'enter',
|
||||
'Instant invoice sending': 'transactions',
|
||||
'Real-time dashboard of finances': 'analytics',
|
||||
'Transaction data export': 'download',
|
||||
'Record shared notes on transactions': 'docs',
|
||||
'24-hour response support': 'clock',
|
||||
'Reimbursement process': 'enter'
|
||||
}).map(([item, icon = 'enter']) => (
|
||||
<ListItem key={icon} icon={icon}>
|
||||
{item}
|
||||
</ListItem>
|
||||
))}
|
||||
<ListItem
|
||||
start={
|
||||
<Avatar
|
||||
src="/team/mel.png"
|
||||
size={32}
|
||||
alt="Mel’s avatar"
|
||||
mr={2}
|
||||
/>
|
||||
}
|
||||
>
|
||||
Amazing support team
|
||||
</ListItem>
|
||||
{Object.entries({
|
||||
'Physical check sending & voiding': '',
|
||||
'Online ACH transfers': '',
|
||||
'Generate attendee legal waivers': '',
|
||||
'Instant Google Workspace & email addresses': '',
|
||||
'Virtual debit cards (with Apple Pay)': '',
|
||||
'Debit card transaction paper trail': '',
|
||||
'Self-serve, no-contract signup': '',
|
||||
'Transparency Mode (optional)': '',
|
||||
'Online, embeddable donation form': ''
|
||||
}).map(([item, date]) => (
|
||||
<ListItem
|
||||
key={item}
|
||||
icon={
|
||||
item.startsWith('Instant') || item.includes('signup')
|
||||
? 'bolt'
|
||||
: item.includes('card')
|
||||
? 'card'
|
||||
: item.includes('Transparency')
|
||||
? 'explore'
|
||||
: item.includes('form')
|
||||
? 'link'
|
||||
: item.includes('Physical')
|
||||
? 'email'
|
||||
: 'enter'
|
||||
}
|
||||
>
|
||||
{item}
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Container>
|
||||
<Run />
|
||||
<Container px={3} mt={4}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<Text sx={{ fontSize: 32, mr: 2 }}>You pay just</Text>
|
||||
<Percentage>7</Percentage>
|
||||
<Text sx={{ fontSize: 32, ml: 2 }}>
|
||||
of revenue. No upfront costs.
|
||||
</Text>
|
||||
</Box>
|
||||
<Container
|
||||
variant="copy"
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
fontSize: 18,
|
||||
lineHeight: '1.25',
|
||||
letterSpacing: '-.03ch',
|
||||
marginTop: 4,
|
||||
marginBottom: 5
|
||||
}}
|
||||
>
|
||||
<Container variant="narrow">
|
||||
<Text sx={{ color: 'muted', lineHeight: 1.375 }}>
|
||||
Hack Club Bank is a{' '}
|
||||
<Link
|
||||
color="primary"
|
||||
href="https://en.wikipedia.org/wiki/Fiscal_sponsorship"
|
||||
hoverline
|
||||
>
|
||||
fiscal sponsor
|
||||
</Link>{' '}
|
||||
for your project. Industry standard varies between 7-14%
|
||||
of revenue.
|
||||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
</Container>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function List({ children }) {
|
||||
return (
|
||||
<Container>
|
||||
<ol
|
||||
style={{
|
||||
paddingLeft: 0,
|
||||
listStyle: 'none'
|
||||
}}
|
||||
>
|
||||
<Grid gap={2} columns={[1, 1, '1fr 1fr']}>
|
||||
{children}
|
||||
</Grid>
|
||||
</ol>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
function ListItem({ icon = 'enter', start, ...props }) {
|
||||
return (
|
||||
<Fade left>
|
||||
<li
|
||||
style={{
|
||||
lineHeight: 1.25,
|
||||
breakInside: 'avoid',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
paddingBottom: 4,
|
||||
marginBottom: 8
|
||||
}}
|
||||
>
|
||||
{start || (
|
||||
<Icon
|
||||
glyph={icon}
|
||||
sx={{ color: 'muted', marginRight: 2 }}
|
||||
size={32}
|
||||
mr={2}
|
||||
/>
|
||||
)}
|
||||
<Text sx={{ fontSize: 24, color: 'smoke' }} {...props} />
|
||||
</li>
|
||||
</Fade>
|
||||
)
|
||||
}
|
||||
|
||||
function Percentage({ children }) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
bg: 'slate',
|
||||
color: 'green',
|
||||
width: [64, 128],
|
||||
height: [64, 128],
|
||||
borderRadius: 'circle',
|
||||
fontWeight: 'bold',
|
||||
justifyContent: 'center',
|
||||
boxShadow: '0 8px 32px rgba(255, 255, 255, 0.125)',
|
||||
fontSize: [48, 96],
|
||||
'&:after': {
|
||||
content: '"%"',
|
||||
mt: [3, 4],
|
||||
fontSize: [24, 40],
|
||||
fontWeight: 'normal',
|
||||
marginRight: -3,
|
||||
marginLeft: [null, 2],
|
||||
color: 'muted'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const recent = dt => {
|
||||
const past = new Date()
|
||||
past.setMonth(past.getMonth() - 2)
|
||||
return new Date(dt) > past
|
||||
}
|
||||
211
components/bank/Features.js
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
import { Box, Heading, Link, Text, Container, Grid } from 'theme-ui'
|
||||
import Icon from '../icon'
|
||||
|
||||
export default function Features() {
|
||||
return (
|
||||
<Box sx={{ py: 6 }}>
|
||||
<Container>
|
||||
<Text variant="heading" sx={{ fontSize: 50 }}>
|
||||
A full-stack toolkit for organizing anything.
|
||||
</Text>
|
||||
<br />
|
||||
<br />
|
||||
<Text sx={{ color: 'muted', maxWidth: '48', fontSize: 28 }}>
|
||||
Invoice sponsors, issue debit cards to your team, and view history.
|
||||
<br />
|
||||
Ongoing support so you can focus on organizing, not the paperwork.
|
||||
</Text>
|
||||
<br />
|
||||
<br />
|
||||
</Container>
|
||||
<Container>
|
||||
<Grid gap={4} columns={[1, null, 3]}>
|
||||
<Box>
|
||||
<Module
|
||||
icon="bank-account"
|
||||
name="Bank account"
|
||||
body="Backed by Silicon Valley Bank with a custom, beautiful dashboard."
|
||||
/>
|
||||
<ModuleDetails>
|
||||
<Document
|
||||
name="501(c)(3) nonprofit status"
|
||||
cost="Become part of Hack Club's legal entity, getting the benefits of our tax status."
|
||||
/>
|
||||
<Document
|
||||
name="Tax filings (990, end-of-year)"
|
||||
cost="We handle all filings with the IRS, so you can focus on your event, not hiring CPAs."
|
||||
/>
|
||||
</ModuleDetails>
|
||||
</Box>
|
||||
<Laptop
|
||||
href="https://bank.hackclub.com/hackpenn"
|
||||
title="See Hack Pennsylvania’s finances in public"
|
||||
sx={{
|
||||
gridColumn: [null, null, 'span 2'],
|
||||
gridRow: [null, null, 'span 2']
|
||||
}}
|
||||
/>
|
||||
<Module
|
||||
icon="card"
|
||||
name="Debit cards"
|
||||
body={
|
||||
<>
|
||||
Issue physical debit cards to all your teammates, backed by{' '}
|
||||
<Link
|
||||
href="https://stripe.com/issuing"
|
||||
color="smoke"
|
||||
hoverline
|
||||
target="_blank"
|
||||
>
|
||||
Stripe
|
||||
</Link>
|
||||
.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Module
|
||||
icon="analytics"
|
||||
name="Balance & history"
|
||||
body="Check real-time account balance + transaction history online anytime."
|
||||
/>
|
||||
<Module
|
||||
icon="payment"
|
||||
name="Built-in invoicing"
|
||||
body="Accept sponsor payments with low negotiated rates from Stripe."
|
||||
/>
|
||||
<Module
|
||||
icon="docs"
|
||||
name="Pre-written forms"
|
||||
body="Download liability + photo forms custom written by expert lawyers."
|
||||
/>
|
||||
<Module
|
||||
icon="explore"
|
||||
name="Transparency Mode"
|
||||
body="If you’d like, show your finances on public pages for full transparency."
|
||||
/>
|
||||
<Module
|
||||
icon="google"
|
||||
name="Google Workspace"
|
||||
body="Get instant, free accounts for your team (like joy@hackpenn.com)."
|
||||
/>
|
||||
<Module
|
||||
icon="support"
|
||||
name="Support anytime"
|
||||
body="We’ll never leave you hanging with best-effort 24hr response time."
|
||||
/>
|
||||
</Grid>
|
||||
</Container>
|
||||
<Container
|
||||
variant="copy"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
variant="lead"
|
||||
sx={{
|
||||
color: 'muted',
|
||||
fontSize: 3,
|
||||
marginTop: [4, 5]
|
||||
}}
|
||||
>
|
||||
Have more questions? <br /> Check out the{' '}
|
||||
<Link
|
||||
href="https://bank.hackclub.com/faq"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
hoverline
|
||||
>
|
||||
Hack Club Bank FAQ
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
</Container>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function Module({ icon, name, body }) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'start' }}>
|
||||
<Icon
|
||||
size={48}
|
||||
glyph={icon}
|
||||
sx={{ flexShrink: 0, marginRight: 3, color: 'primary' }}
|
||||
/>
|
||||
<Box>
|
||||
<Heading sx={{ color: 'snow', lineHeight: '1.5' }}>{name}</Heading>
|
||||
<Text
|
||||
sx={{ color: 'muted', lineHeight: '1.375', fontSize: 17 }}
|
||||
children={body}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function ModuleDetails({ children }) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
bg: '#252429',
|
||||
color: 'smoke',
|
||||
mt: 4,
|
||||
ml: 0,
|
||||
py: 3,
|
||||
px: 2,
|
||||
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.0625)',
|
||||
borderRadius: 'default'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function Document({ name, cost }) {
|
||||
return (
|
||||
<Box sx={{ display: 'flex' }}>
|
||||
<Icon
|
||||
size={28}
|
||||
mr={1}
|
||||
glyph="payment"
|
||||
sx={{ flexShrink: 0, color: 'green' }}
|
||||
/>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Text fontSize={2} children={name} />
|
||||
|
||||
{cost && (
|
||||
<Text
|
||||
fontSize={1}
|
||||
color="muted"
|
||||
style={{ lineHeight: '1.375' }}
|
||||
children={cost}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function Laptop({ href, title, sx }) {
|
||||
return (
|
||||
<Link href={href} title={title} sx={sx}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
minHeight: '16rem',
|
||||
backgroundSize: 'auto 115%',
|
||||
backgroundImage: "url('/bank/laptop-dark.png')",
|
||||
backgroundPosition: 'center top',
|
||||
backgroundRepeat: 'no-repeat'
|
||||
}}
|
||||
></Box>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
181
components/bank/Landing.js
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
import {
|
||||
Box,
|
||||
Button,
|
||||
Heading,
|
||||
Link,
|
||||
Text,
|
||||
Flex,
|
||||
Container,
|
||||
Badge
|
||||
} from 'theme-ui'
|
||||
import Fade from 'react-reveal/Fade'
|
||||
import ScrollHint from './ScrollHint'
|
||||
|
||||
export default function Landing() {
|
||||
return (
|
||||
<>
|
||||
<Slide>
|
||||
<Vignette />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
bottom: 5,
|
||||
mx: 'auto',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
zIndex: '100',
|
||||
paddingTop: '96px'
|
||||
}}
|
||||
>
|
||||
<Fade duration={625} bottom>
|
||||
<Container
|
||||
variant="container"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
variant="ultratitle"
|
||||
sx={{
|
||||
marginBottom: 4,
|
||||
// lineHeight: 0.875,
|
||||
textShadow: '0 0 16px rgba(0, 0, 0, 1)',
|
||||
letterSpacing: '-0.02em',
|
||||
'@media screen and (max-height: 600px)': {
|
||||
lineHeight: 0.875
|
||||
},
|
||||
'@media screen and (min-height: 610px)': {
|
||||
lineHeight: 1.125
|
||||
}
|
||||
}}
|
||||
>
|
||||
The bank for hackers to <Underline>make ideas real</Underline>
|
||||
.
|
||||
</Heading>
|
||||
<Container variant="copy">
|
||||
<Text
|
||||
variant="lead"
|
||||
sx={{
|
||||
textShadow: '0 3px 6px rgba(0, 0, 0, 0.5)',
|
||||
'@media screen and (max-height: 600px)': {
|
||||
lineHeight: 1
|
||||
}
|
||||
}}
|
||||
>
|
||||
The team behind{' '}
|
||||
<Link
|
||||
href="https://hackaz.io/?ref=bank"
|
||||
target="_blank"
|
||||
color="inherit"
|
||||
bold
|
||||
hoverline
|
||||
>
|
||||
Hack Arizona
|
||||
</Link>{' '}
|
||||
is one of 100+ teams who uses{' '}
|
||||
<strong>Hack Club Bank</strong> to run world-class
|
||||
hackathons.
|
||||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
</Fade>
|
||||
</Box>
|
||||
<br />
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginBottom: 3
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outlineLg"
|
||||
as="a"
|
||||
href="#apply"
|
||||
style={{ zIndex: '100' }}
|
||||
>
|
||||
Apply Now
|
||||
</Button>
|
||||
</Box>
|
||||
<ScrollHint />
|
||||
</Box>
|
||||
<Box sx={{ position: 'absolute', bottom: 3, right: 2 }}>
|
||||
<Badge
|
||||
variant="pill"
|
||||
sx={{
|
||||
zIndex: '1',
|
||||
bg: 'muted',
|
||||
color: 'steel',
|
||||
fontWeight: 'normal'
|
||||
}}
|
||||
>
|
||||
Tuscon, AZ
|
||||
</Badge>
|
||||
</Box>
|
||||
</Slide>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Underline({ children }) {
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
backgroundImage: 'url(/underline.svg)',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundSize: '100% 1rem',
|
||||
backgroundPosition: 'bottom center'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
function Slide({ children }) {
|
||||
return (
|
||||
<Box
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'end',
|
||||
width: '100vw',
|
||||
background: 'url("/bank/bg.jpg")',
|
||||
backgroundColor: '#000000',
|
||||
boxShadow: 'inset 0 0 4rem 1rem rgba(0, 0, 0, 0.5)',
|
||||
backgroundPosition: 'center',
|
||||
backgroundSize: 'cover',
|
||||
width: '100%',
|
||||
minHeight: '100vh',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function Vignette() {
|
||||
return (
|
||||
<Box
|
||||
style={{
|
||||
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%)',
|
||||
height: '100vh',
|
||||
left: '0',
|
||||
right: '0',
|
||||
position: 'absolute',
|
||||
zIndex: '0'
|
||||
}}
|
||||
></Box>
|
||||
)
|
||||
}
|
||||
80
components/bank/Run.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { Box, Heading, Text, Container } from 'theme-ui'
|
||||
import { Fade } from 'react-reveal'
|
||||
import Icon from '../icon'
|
||||
|
||||
export default function Run() {
|
||||
return (
|
||||
<>
|
||||
<Container
|
||||
variant="container"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: ['column', null, 'row'],
|
||||
alignItems: 'center',
|
||||
width: ['100%', '100%', '100%', '85%'],
|
||||
bg: '#252429',
|
||||
color: 'smoke',
|
||||
px: 4,
|
||||
|
||||
borderRadius: 'default',
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Container maxWidth={28} sx={{ mx: 0, py: 4 }}>
|
||||
<Text variant="heading" sx={{ fontSize: 48 }}>
|
||||
Bank doesn’t stop at closing ceremony.
|
||||
</Text>
|
||||
<br />
|
||||
<Text variant="lead" sx={{ color: 'muted', fontSize: 28 }}>
|
||||
Setting up a bank account is just the start. Hack Club Bank helps
|
||||
you handle ongoing obligations while you’re organizing.
|
||||
</Text>
|
||||
</Container>
|
||||
<List>
|
||||
<ListItem
|
||||
icon="docs"
|
||||
body="We handle ongoing tax filings including end-of-year taxes"
|
||||
/>
|
||||
<ListItem
|
||||
icon="payment-docs"
|
||||
body="Our accountants regularly reconcile your books"
|
||||
/>
|
||||
<ListItem
|
||||
icon="history"
|
||||
body="You always have access to historical financial data"
|
||||
/>
|
||||
</List>
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function List({ children }) {
|
||||
return (
|
||||
<Box>
|
||||
<ol style={{ listStyle: 'none', paddingLeft: 0 }}>{children}</ol>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function ListItem({ icon, body }) {
|
||||
return (
|
||||
<Fade bottom>
|
||||
<li
|
||||
style={{
|
||||
lineHeight: 1.25,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
pl: 0
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
glyph={icon}
|
||||
size={45}
|
||||
sx={{ color: 'primary', flexShrink: 'none', flexShrink: 0, mr: 2 }}
|
||||
/>
|
||||
<Text fontSize={[32, 48]} variant="lead" children={body} />
|
||||
</li>
|
||||
</Fade>
|
||||
)
|
||||
}
|
||||
24
components/bank/ScrollHint.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { Box } from 'theme-ui'
|
||||
|
||||
export default function ScrollHint() {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'block',
|
||||
position: 'relative',
|
||||
height: '32px',
|
||||
width: '32px',
|
||||
margin: '0 auto',
|
||||
borderBottom: '2px solid #fff',
|
||||
borderRight: '2px solid #fff',
|
||||
transform: 'rotate(45deg)',
|
||||
opacity: '.6',
|
||||
cursor: 'pointer',
|
||||
transition: 'transform .3s',
|
||||
|
||||
'&:hover': { transform: 'translateY(4px) rotate(45deg)' },
|
||||
'&:active': { transform: ' translateY(6px) rotate(45deg)' }
|
||||
}}
|
||||
></Box>
|
||||
)
|
||||
}
|
||||
90
components/bank/Signup.js
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import { Text, Heading, Box, Container, Input, Label, Button } from 'theme-ui'
|
||||
import { useState } from 'react'
|
||||
|
||||
function Base({ children, action, target, method }) {
|
||||
return (
|
||||
<Box
|
||||
as="form"
|
||||
sx={{ display: 'grid', gridTemplateColumns: '1fr' }}
|
||||
action={action}
|
||||
target={target}
|
||||
method={method}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function Field({
|
||||
placeholder,
|
||||
label,
|
||||
name,
|
||||
type,
|
||||
value,
|
||||
onChange,
|
||||
}) {
|
||||
return (
|
||||
<Box sx={{ my: 2 }}>
|
||||
<Label htmlFor={name} sx={{ color: 'muted', fontSize: 18 }}>
|
||||
{label}
|
||||
</Label>
|
||||
<Input
|
||||
id={name}
|
||||
placeholder={placeholder}
|
||||
name={name}
|
||||
type={type}
|
||||
sx={{ bg: 'dark' }}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
required
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Signup() {
|
||||
const [values, setValues] = useState({})
|
||||
return (
|
||||
<Base
|
||||
method="get"
|
||||
target="_blank"
|
||||
action="https://airtable.com/shrW33gWaPnSDBhYj"
|
||||
>
|
||||
<Field
|
||||
label="Project name"
|
||||
name="prefill_Event Name"
|
||||
placeholder="Windy City Hacks"
|
||||
value={values.name}
|
||||
onChange={e => setValues({...values, name: e.target.value})}
|
||||
/>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', gap: 2 }}>
|
||||
<Field
|
||||
label="First name"
|
||||
name="prefill_First Name"
|
||||
placeholder="Fiona"
|
||||
value={values.first_name}
|
||||
onChange={e => setValues({...values, first_name: e.target.value})}
|
||||
/>
|
||||
<Field
|
||||
label="Last name"
|
||||
name="prefill_Last Name"
|
||||
placeholder="Hackworth"
|
||||
value={values.last_name}
|
||||
onChange={e => setValues({...values, last_name: e.target.value})}
|
||||
/>
|
||||
</Box>
|
||||
<Field
|
||||
label="Email address"
|
||||
name="prefill_Email Address"
|
||||
placeholder="fiona@hackclub.com"
|
||||
type="email"
|
||||
value={values.email}
|
||||
onChange={e => setValues({...values, email: e.target.value})}
|
||||
/>
|
||||
<Button sx={{ bg: 'blue', mt: [2, 3], py: 3 }} type="submit">{`Finish ${
|
||||
10 - Object.values(values).filter(n => n !== '').length
|
||||
} fields to apply`}</Button>
|
||||
</Base>
|
||||
)
|
||||
}
|
||||
|
||||
103
components/bank/Start.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import { Box, Container, Link, Text, Heading, Card, Grid } from 'theme-ui'
|
||||
import { Fade } from 'react-reveal'
|
||||
import Signup from './Signup'
|
||||
import Timeline from './Timeline'
|
||||
import Stats from './Stats'
|
||||
|
||||
export default function Start() {
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
as="section"
|
||||
id="apply"
|
||||
sx={{
|
||||
// bg: 'darker',
|
||||
pt: 6,
|
||||
zIndex: -999
|
||||
}}
|
||||
>
|
||||
<Container
|
||||
px={3}
|
||||
mb={[4, 5]}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
textAlign: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
<Heading variant="ultratitle" color="white" mb={2}>
|
||||
Sign up for Hack Club Bank.
|
||||
</Heading>
|
||||
<Container variant="narrow" sx={{ color: 'muted' }}>
|
||||
<Text variant="lead">
|
||||
Open to all US-based registered Hack Clubs, hackathons, and your
|
||||
next amazing project.
|
||||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
<Timeline />
|
||||
<Grid mt={[4, 5]} mb={[3, 4]} px={3} columns={[1, 1, '1fr 1fr']}>
|
||||
<Fade bottom>
|
||||
<Card
|
||||
variant="primary"
|
||||
sx={{
|
||||
backgroundColor: 'darkless',
|
||||
color: 'snow',
|
||||
width: ['100%', null, 356],
|
||||
float: [null, null, 'right']
|
||||
}}
|
||||
>
|
||||
<Text variant="heading" sx={{ fontSize: 24, lineHeight: 2 }}>
|
||||
Your project
|
||||
</Text>
|
||||
<Signup />
|
||||
</Card>
|
||||
</Fade>
|
||||
<Container variant="narrow" sx={{ pr: [null, null, 2, 6], m: 0 }}>
|
||||
<Stats
|
||||
color="smoke"
|
||||
labelColor="muted"
|
||||
fontSize={[7, 8]}
|
||||
my={[3, 4]}
|
||||
px={0}
|
||||
width="auto"
|
||||
align="left"
|
||||
/>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 18,
|
||||
color: 'muted'
|
||||
}}
|
||||
>
|
||||
Starting in February 2020, we started running Hack Club HQ on Bank
|
||||
(& we don’t count our numbers in these stats).
|
||||
<Link
|
||||
href="https://bank.hackclub.com/hq"
|
||||
color="primary"
|
||||
hoverline
|
||||
>
|
||||
See our finances here.
|
||||
</Link>
|
||||
</Text>
|
||||
</Container>
|
||||
</Grid>
|
||||
<Container
|
||||
variant="copy"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center',
|
||||
paddingBottom: 6
|
||||
}}
|
||||
>
|
||||
<Text sx={{ fontSize: 18, color: 'muted', mx: [3, null, 6] }}>
|
||||
Hack Club does not directly provide banking services. Banking
|
||||
services provided by Silicon Valley Bank, an FDIC-certified
|
||||
institution.
|
||||
</Text>
|
||||
</Container>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
93
components/bank/Stats.js
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import { useState, useEffect } from 'react'
|
||||
import { Text, Box, Container } from 'theme-ui'
|
||||
import Stat from '../stat'
|
||||
import api from '../../lib/api'
|
||||
import { timeSince } from '../../lib/helpers'
|
||||
import { keyframes } from '@emotion/react'
|
||||
|
||||
const renderMoney = amount =>
|
||||
Math.floor(amount / 100)
|
||||
.toLocaleString('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD'
|
||||
})
|
||||
.replace('.00', '')
|
||||
|
||||
const flashing = keyframes({
|
||||
from: { opacity: 0 },
|
||||
'50%': { opacity: 1 },
|
||||
to: { opacity: 0 }
|
||||
})
|
||||
|
||||
function Dot() {
|
||||
return (
|
||||
<Text
|
||||
sx={{
|
||||
bg: 'green',
|
||||
color: 'white',
|
||||
borderRadius: 'circle',
|
||||
display: 'inline-block',
|
||||
lineHeight: 0,
|
||||
width: '.4em',
|
||||
height: '.4em',
|
||||
marginRight: '.4em',
|
||||
marginBottom: '.12em',
|
||||
animationName: `${flashing}`,
|
||||
animationDuration: '3s',
|
||||
animationTimingFunction: 'ease-in-out',
|
||||
animationIterationCount: 'infinite'
|
||||
// animation: `3s ${flashing} easin-in-out infinite`
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default props => {
|
||||
const [volume, setVolume] = useState(100 * 1000 * 1000) // 1MM default
|
||||
const [raised, setRaised] = useState(100 * 1000 * 500) // half million default
|
||||
const [lastUpdated, setLastUpdated] = useState(Date.now()) // now default
|
||||
|
||||
useEffect(() => {
|
||||
loadStats()
|
||||
})
|
||||
|
||||
const loadStats = () => {
|
||||
api.get('https://bank.hackclub.com/stats').then(stats => {
|
||||
setVolume(renderMoney(stats.transactions_volume))
|
||||
setRaised(renderMoney(stats.raised))
|
||||
setLastUpdated(stats.last_transaction_date * 1000)
|
||||
})
|
||||
}
|
||||
|
||||
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>
|
||||
<Stat {...props} value={raised} label="raised on Hack Club Bank" />
|
||||
<Stat
|
||||
{...props}
|
||||
fontSize={[3, 4, 5]}
|
||||
value={volume}
|
||||
label="total amount transacted"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
273
components/bank/Testimonials.js
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
import {
|
||||
Box,
|
||||
Avatar,
|
||||
Button,
|
||||
Image,
|
||||
Text,
|
||||
Heading,
|
||||
Container,
|
||||
Card,
|
||||
Grid,
|
||||
Link
|
||||
} from 'theme-ui'
|
||||
import { Slide } from 'react-reveal'
|
||||
import Stat from '../stat'
|
||||
import kebabCase from 'lodash/kebabCase'
|
||||
|
||||
const events = [
|
||||
{
|
||||
transparency: 'hackpenn',
|
||||
name: 'Hack Pennsylvania',
|
||||
location: 'State College, PA',
|
||||
organizer: 'Joy Liu',
|
||||
budget: 15,
|
||||
attendees: 115,
|
||||
testimonial:
|
||||
'For me, Hack Club Bank unlocked organizing hackathons. Even after as a club leader, raising money seemed insurmountable. Bank directly enabled organizing events in my community with event bank accounts & a supportive community. I couldn’t recommend it more highly.'
|
||||
},
|
||||
{
|
||||
name: 'Teenhacks LI',
|
||||
location: 'Long Island, NY',
|
||||
organizer: 'Wesley Pergament',
|
||||
budget: 35,
|
||||
attendees: 300,
|
||||
testimonial:
|
||||
'For our hackathon, Hack Club Bank has given us the tools to make sure our organization is professional with sponsors. Bank and their team have created an easily manageable resource to make sure any event is run successfully. We would highly recommend any organization be a part of the Hack Club ecosystem.'
|
||||
},
|
||||
{
|
||||
name: 'Los Altos Hacks',
|
||||
location: 'Sunnyvale, CA',
|
||||
organizer: 'Jamsheed Mistri',
|
||||
budget: 30,
|
||||
attendees: 350,
|
||||
testimonial:
|
||||
'Hack Club Bank has made it incredibly easy to handle our event’s funds and has provided countless tools to increase our productivity. With Bank, I can focus on making the event the best it can be.'
|
||||
},
|
||||
{
|
||||
name: 'SLO Hacks',
|
||||
location: 'San Luis Obispo, CA',
|
||||
organizer: 'Selynna Sun',
|
||||
budget: 50,
|
||||
attendees: 300,
|
||||
testimonial:
|
||||
'Hack Club Bank significantly improved the fiscal sponsorship process for SLO Hacks, through a beautifully-designed platform full of useful features, in addition to a responsive team addressed our questions as quickly as possible.'
|
||||
},
|
||||
{
|
||||
name: 'MAHacks',
|
||||
location: 'Boston, MA',
|
||||
organizer: 'Kat Huang',
|
||||
budget: 1.5,
|
||||
attendees: 70,
|
||||
testimonial:
|
||||
'Hack Club Bank removed the barriers to starting fundraising for MAHacks. In mere days, vs months of nonprofit paperwork, Bank enabled my team to invoice sponsors professionally and manage our finances on a clear, up-to-date dashboard. I highly recommend using Bank & joining the Hack Club community.'
|
||||
},
|
||||
{
|
||||
transparency: 'dv-hacks',
|
||||
name: 'DV Hacks',
|
||||
location: 'Santa Clara, CA',
|
||||
organizer: 'Khushi Wadhwa',
|
||||
budget: 12,
|
||||
attendees: 150,
|
||||
testimonial:
|
||||
'Hack Club Bank is an essential platform for any hackathon organizer! It made us look both professional and credible in front of our sponsors and it relieved us of legal/financial burdens. Hack Club Bank was there for us every step of the way and for a first-year hackathon, that support was priceless.'
|
||||
}
|
||||
]
|
||||
|
||||
export default function Testimonials() {
|
||||
return (
|
||||
<>
|
||||
<Box>
|
||||
<Container
|
||||
variant="copy"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<Heading variant="title">
|
||||
The best events across the country run on Bank.
|
||||
</Heading>
|
||||
<Text variant="lead" color="muted">
|
||||
Everywhere from Philadelphia to Phoenix to Portland,
|
||||
Hack Club Bank powers events of all sizes.
|
||||
</Text>
|
||||
</Container>
|
||||
<Container
|
||||
sx={{
|
||||
color: 'smoke',
|
||||
px: [null, null, 4],
|
||||
mx: 'auto',
|
||||
mt: 2,
|
||||
borderRadius: 0,
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
gap={4}
|
||||
sx={{
|
||||
// gridTemplateColumns: 'repeat(auto-fill, minmax(18em, 1fr))'
|
||||
gridTemplateColumns: ['100%', null, null, '1fr 1fr']
|
||||
}}
|
||||
>
|
||||
{events.map(event => {
|
||||
const id = kebabCase(event.name)
|
||||
return (
|
||||
<Event {...event} img={`/bank/events/${id}.jpg`} key={id} />
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Event({
|
||||
img,
|
||||
name,
|
||||
location,
|
||||
budget,
|
||||
attendees,
|
||||
organizer,
|
||||
testimonial,
|
||||
transparency
|
||||
}) {
|
||||
return (
|
||||
<Slide bottom>
|
||||
<Box
|
||||
sx={{
|
||||
backgroundColor: 'darkless',
|
||||
color: 'smoke',
|
||||
borderRadius: 'extra',
|
||||
mx: 'auto'
|
||||
}}
|
||||
>
|
||||
<Container sx={{ padding: 0, margin: 0 }}>
|
||||
<Image
|
||||
src={img}
|
||||
alt={location}
|
||||
sx={{
|
||||
maxHeight: '20rem',
|
||||
objectFit: 'cover',
|
||||
width: '100%',
|
||||
borderRadius: 'default',
|
||||
mb: -3
|
||||
}}
|
||||
/>
|
||||
<Box p={[3, null, 4]}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: ['column', 'row', 'row'],
|
||||
alignItems: ['baseline', 'center'],
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: -3
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
color="white"
|
||||
variant="headline"
|
||||
sx={{ fontSize: [48, null, 30], letterSpacing: -0.1 }}
|
||||
children={name}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-start',
|
||||
my: 0,
|
||||
ml: -2,
|
||||
pl: 0
|
||||
}}
|
||||
>
|
||||
<DetailStat value={attendees} label="attendees" />
|
||||
<DetailStat value={`$${budget}k`} label="budget" />
|
||||
</Box>
|
||||
</Box>
|
||||
<br />
|
||||
<Quote>"{testimonial}"</Quote>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: ['0px', 3]
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
mt: ['16px', '0px']
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={`/hackers/${organizer.split(' ')[0].toLowerCase()}.jpg`}
|
||||
size={48}
|
||||
mr={2}
|
||||
/>
|
||||
<Text
|
||||
color="white"
|
||||
sx={{
|
||||
fontSize: 19,
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
}}
|
||||
>
|
||||
<Text sx={{ fontWeight: 'bold', lineHeight: 1.125 }}>
|
||||
{organizer}
|
||||
</Text>
|
||||
<Text>Lead Organizer</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
{transparency && (
|
||||
<Link
|
||||
href={`https://bank.hackclub.com/${transparency}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
sx={{mt: ['16px', '0px']}}
|
||||
>
|
||||
<Button
|
||||
mt={[null, null, 4, 0]}
|
||||
ml={[0, 'auto']}
|
||||
sx={{ textTransform: 'none' }}
|
||||
variant="primary"
|
||||
>
|
||||
See Finances
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
</Slide>
|
||||
)
|
||||
}
|
||||
|
||||
function DetailStat({ value, label }) {
|
||||
return (
|
||||
<Box sx={{ px: 0, mb: [3, 0], ml: [-1, 0], mx: 3 }}>
|
||||
<Stat value={value} label={label} sm />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function Quote({ children }) {
|
||||
return (
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 2,
|
||||
color: 'muted',
|
||||
textIndent: '-.375em',
|
||||
lineHeight: 'caption',
|
||||
fontSize: 18
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
138
components/bank/Timeline.js
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
import { Box, Flex, Avatar, Container, Text, Badge } from 'theme-ui'
|
||||
import { Slide } from 'react-reveal'
|
||||
import Icon from '../icon'
|
||||
|
||||
function Timeline({ children }) {
|
||||
return (
|
||||
<Flex
|
||||
sx={{ flexDirection: ['column', null, 'row'], justifyContent: 'center' }}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
function TimelineStep({ children }) {
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
marginX: [4, null, null],
|
||||
paddingX: [null, null, 3, 4],
|
||||
paddingY: [4, null, 0],
|
||||
flexDirection: ['row', null, 'column'],
|
||||
alignItems: 'center',
|
||||
'&:before': {
|
||||
content: '""',
|
||||
background: '#3c4858',
|
||||
height: ['420px', null, '4px'],
|
||||
width: ['4px', null, '50%'],
|
||||
marginLeft: [26, null, 0],
|
||||
marginTop: [null, null, '34px'],
|
||||
position: 'absolute',
|
||||
zIndex: -1
|
||||
},
|
||||
'&:first-of-type:before': {
|
||||
top: [0, null, 'auto'],
|
||||
width: [0, null, 0],
|
||||
left: [0, null, 0]
|
||||
},
|
||||
'&:last-of-type:before': {
|
||||
bottom: [0, null, 'auto'],
|
||||
left: [null, null, 0],
|
||||
width: [null, null, 0]
|
||||
}
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
function Circle({ children }) {
|
||||
return (
|
||||
<Box
|
||||
style={{
|
||||
padding: 12,
|
||||
background: 'red',
|
||||
color: 'white',
|
||||
backgroundImage:
|
||||
'radial-gradient(ellipse farthest-corner at top left, #ff8c37, #ec3750)',
|
||||
borderRadius: '100%',
|
||||
display: 'inline-block',
|
||||
lineHeight: 0,
|
||||
position: 'relative',
|
||||
zIndex: 999
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function Step({ icon, name, duration }) {
|
||||
return (
|
||||
<TimelineStep pb={4}>
|
||||
<Slide left>
|
||||
<Circle mr={[3, null, 0]} mb={[null, null, 4]}>
|
||||
<Icon glyph={icon} size={48} />
|
||||
</Circle>
|
||||
<Container
|
||||
sx={{
|
||||
marginTop: 3,
|
||||
display: 'flex',
|
||||
justifyContent: ['left', null, 'center'],
|
||||
flexDirection: 'column',
|
||||
textAlign: ['left', null, 'center']
|
||||
}}
|
||||
>
|
||||
<Badge
|
||||
variant="pill"
|
||||
sx={{
|
||||
bg: 'muted',
|
||||
color: 'darker',
|
||||
fontWeight: 'normal',
|
||||
textTransform: 'uppercase',
|
||||
width: 64,
|
||||
fontSize: 18,
|
||||
px: 2,
|
||||
mx: [null, null, 'auto']
|
||||
}}
|
||||
children={duration}
|
||||
/>
|
||||
<Text
|
||||
sx={{ color: 'white', fontSize: 24, maxWidth: [200, null, 300] }}
|
||||
children={name}
|
||||
/>
|
||||
</Container>
|
||||
</Slide>
|
||||
</TimelineStep>
|
||||
)
|
||||
}
|
||||
|
||||
export default function RealTimeline() {
|
||||
return (
|
||||
<Timeline px={3}>
|
||||
<Step
|
||||
icon="send"
|
||||
name="Sign up, explore, order debit cards"
|
||||
duration="Day 1"
|
||||
/>
|
||||
<Step
|
||||
icon="welcome"
|
||||
name="Intro meeting with Hack Club Bank"
|
||||
duration="Day 3"
|
||||
/>
|
||||
<Step
|
||||
icon="post"
|
||||
name="Sign the contract & unlock full access"
|
||||
duration="Day 4"
|
||||
/>
|
||||
<Step
|
||||
icon="card"
|
||||
name="Receive debit cards in the mail"
|
||||
duration="Day 10"
|
||||
mb={0}
|
||||
/>
|
||||
</Timeline>
|
||||
)
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ const Stat = ({
|
|||
reversed = false,
|
||||
half = false,
|
||||
lg = false,
|
||||
sm = false,
|
||||
...props
|
||||
}) => (
|
||||
<Flex
|
||||
|
|
@ -33,7 +34,7 @@ const Stat = ({
|
|||
as="span"
|
||||
sx={{
|
||||
color,
|
||||
fontSize: lg ? [5, 6, 7] : [4, 5, 6],
|
||||
fontSize: lg ? [5, 6, 7] : sm ? [3, 4] : [4, 5, 6],
|
||||
fontWeight: 'heading',
|
||||
letterSpacing: 'title',
|
||||
my: 0
|
||||
|
|
@ -44,7 +45,7 @@ const Stat = ({
|
|||
<Text
|
||||
as="sup"
|
||||
sx={{
|
||||
fontSize: lg ? [2, 3] : [1, 2],
|
||||
fontSize: lg ? [2, 3] : sm ? [1, 1] : [1, 2],
|
||||
color: color === 'text' ? 'secondary' : color,
|
||||
ml: [null, unit === '%' ? 1 : null],
|
||||
mr: [null, 1],
|
||||
|
|
|
|||
78
lib/api.js
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import storage from './storage'
|
||||
|
||||
export const url = 'https://api.hackclub.com/'
|
||||
const methods = ['GET', 'PUT', 'POST', 'PATCH', 'DELETE']
|
||||
|
||||
const generateMethod =
|
||||
method =>
|
||||
(path, options = {}, fetchOptions = {}) => {
|
||||
let filteredOptions = {}
|
||||
const authToken = storage.get('authToken')
|
||||
if (authToken) {
|
||||
options.authToken = authToken
|
||||
}
|
||||
|
||||
for (let [key, value] of Object.entries(options)) {
|
||||
switch (key) {
|
||||
case 'authToken':
|
||||
filteredOptions.headers = filteredOptions.headers || {}
|
||||
filteredOptions.headers['Authorization'] = `Bearer ${value}`
|
||||
break
|
||||
case 'data':
|
||||
if (value instanceof FormData) {
|
||||
filteredOptions.body = value
|
||||
} else {
|
||||
filteredOptions.body = JSON.stringify(value)
|
||||
filteredOptions.headers = filteredOptions.headers || {}
|
||||
filteredOptions.headers['Content-Type'] = 'application/json'
|
||||
}
|
||||
break
|
||||
default:
|
||||
filteredOptions[key] = value
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (fetchOptions.noAuth) {
|
||||
if (filteredOptions.headers && filteredOptions.headers['Authorization']) {
|
||||
delete filteredOptions.headers['Authorization']
|
||||
}
|
||||
}
|
||||
|
||||
const foreignUrl = path.startsWith('http')
|
||||
const urlPath = foreignUrl ? path : url + path
|
||||
|
||||
return fetch(urlPath, { method, ...filteredOptions })
|
||||
.then(res => {
|
||||
if (res.ok) {
|
||||
const contentType = res.headers.get('content-type')
|
||||
if (contentType && contentType.indexOf('application/json') !== -1) {
|
||||
return res.json()
|
||||
} else {
|
||||
return res.text()
|
||||
}
|
||||
} else {
|
||||
if (res.status === 422) {
|
||||
return res.json().then(json => {
|
||||
// eslint-disable-next-line
|
||||
throw { ...res, errors: json.errors }
|
||||
})
|
||||
} else {
|
||||
throw res
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
throw err
|
||||
})
|
||||
}
|
||||
|
||||
let api = {}
|
||||
|
||||
methods.forEach(method => {
|
||||
api[method.toLowerCase()] = generateMethod(method)
|
||||
})
|
||||
|
||||
api.currentUser = () => api.get(`v1/users/current`)
|
||||
|
||||
export default api
|
||||
135
lib/helpers.js
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
export const dt = d => new Date(d).toLocaleDateString()
|
||||
|
||||
const year = new Date().getFullYear()
|
||||
export const tinyDt = d => dt(d).replace(`/${year}`, '').replace(`${year}-`, '')
|
||||
|
||||
// based on https://github.com/withspectrum/spectrum/blob/alpha/src/helpers/utils.js#L146
|
||||
export const timeSince = (
|
||||
previous,
|
||||
absoluteDuration = false,
|
||||
longForm = false,
|
||||
current = new Date()
|
||||
) => {
|
||||
const msPerMinute = 60 * 1000
|
||||
const msPerHour = msPerMinute * 60
|
||||
const msPerDay = msPerHour * 24
|
||||
const msPerWeek = msPerDay * 7
|
||||
const msPerMonth = msPerDay * 30 * 2
|
||||
const msPerYear = msPerDay * 365
|
||||
|
||||
const elapsed = new Date(current) - new Date(previous)
|
||||
|
||||
let humanizedTime
|
||||
if (elapsed < msPerMinute) {
|
||||
humanizedTime = '< 1m'
|
||||
} else if (elapsed < msPerHour) {
|
||||
const now = Math.round(elapsed / msPerMinute)
|
||||
humanizedTime = longForm ? `${now} minute${now > 1 ? 's' : ''}` : `${now}m`
|
||||
} else if (elapsed < msPerDay) {
|
||||
const now = Math.round(elapsed / msPerHour)
|
||||
humanizedTime = longForm ? `${now} hour${now > 1 ? 's' : ''}` : `${now}h`
|
||||
} else if (elapsed < msPerWeek) {
|
||||
const now = Math.round(elapsed / msPerDay)
|
||||
humanizedTime = longForm ? `${now} day${now > 1 ? 's' : ''}` : `${now}d`
|
||||
} else if (elapsed < msPerMonth) {
|
||||
const now = Math.round(elapsed / msPerWeek)
|
||||
humanizedTime = longForm ? `${now} week${now > 1 ? 's' : ''}` : `${now}w`
|
||||
} else if (elapsed < msPerYear) {
|
||||
const now = Math.round(elapsed / msPerMonth)
|
||||
humanizedTime = longForm ? `${now} month${now > 1 ? 's' : ''}` : `${now}mo`
|
||||
} else {
|
||||
const now = Math.round(elapsed / msPerYear)
|
||||
humanizedTime = longForm ? `${now} year${now > 1 ? 's' : ''}` : `${now}y`
|
||||
}
|
||||
|
||||
if (absoluteDuration) {
|
||||
return humanizedTime
|
||||
} else {
|
||||
return elapsed > 0 ? `${humanizedTime} ago` : `in ${humanizedTime}`
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(@lachlanjc): I know this is bad, I’m trying to get it out the door okay???
|
||||
export const timeTo = (time, current = new Date(), longForm = true) => {
|
||||
const msPerMinute = 60 * 1000
|
||||
const msPerHour = msPerMinute * 60
|
||||
const msPerDay = msPerHour * 64 // getting close to a day
|
||||
const msPerYear = msPerDay * 365
|
||||
|
||||
const elapsed = new Date(time) - new Date(current)
|
||||
|
||||
let humanizedTime
|
||||
if (elapsed < msPerMinute) {
|
||||
humanizedTime = '< 1m'
|
||||
} else if (elapsed < msPerHour) {
|
||||
const now = Math.round(elapsed / msPerMinute)
|
||||
humanizedTime = longForm ? `${now} more minutes` : `${now}m`
|
||||
} else if (elapsed < msPerDay) {
|
||||
const now = Math.round(elapsed / msPerHour)
|
||||
humanizedTime = longForm ? `${now} more hours` : `${now}h`
|
||||
} else if (elapsed < msPerYear) {
|
||||
const now = Math.round(elapsed / msPerDay)
|
||||
humanizedTime = longForm ? `${now} days` : `${now}d`
|
||||
} else {
|
||||
const now = Math.round(elapsed / msPerYear)
|
||||
humanizedTime = longForm ? `${now} years` : `${now}y`
|
||||
}
|
||||
|
||||
return humanizedTime
|
||||
}
|
||||
|
||||
function formatChunk(type, date) {
|
||||
const days = [
|
||||
'Sunday',
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday'
|
||||
]
|
||||
const months = [
|
||||
'January',
|
||||
'Febuary',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'
|
||||
]
|
||||
switch (type) {
|
||||
case 'dddd':
|
||||
return days[date.getDay()]
|
||||
case 'ddd':
|
||||
return formatChunk('dddd', date).slice(0, 3)
|
||||
case 'dd':
|
||||
return ('00' + formatChunk('d', date)).slice(-2)
|
||||
case 'd':
|
||||
return date.getDate()
|
||||
case 'mmmm':
|
||||
return months[date.getMonth()]
|
||||
case 'mmm':
|
||||
return formatChunk('mmmm', date).slice(0, 3)
|
||||
case 'mm':
|
||||
return ('00' + formatChunk('m', date)).slice(-2)
|
||||
case 'm':
|
||||
return (date.getMonth() + 1).toString()
|
||||
case 'yyyy':
|
||||
return date.getFullYear().toString()
|
||||
case 'yy':
|
||||
return formatChunk('yyyy', date).slice(-2)
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
export const formatDate = (format, date, divider = ' ') => {
|
||||
return format
|
||||
.split(divider)
|
||||
.map(chunk => formatChunk(chunk, new Date(date)))
|
||||
.join(divider)
|
||||
}
|
||||
33
lib/storage.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
const stubbedStorage = {}
|
||||
'get set remove keys'
|
||||
.split(' ')
|
||||
.forEach(method => (stubbedStorage[method] = () => null))
|
||||
|
||||
let localStorage
|
||||
try {
|
||||
localStorage = window.localStorage
|
||||
} catch (e) {
|
||||
if (e instanceof ReferenceError) {
|
||||
localStorage = stubbedStorage
|
||||
}
|
||||
}
|
||||
|
||||
const storage = {
|
||||
get: key => {
|
||||
try {
|
||||
// (max@maxwofford.com) Values that were set before values were stringified might fail to parse, so we return the raw storage item if we can't parse it
|
||||
return JSON.parse(localStorage.getItem(key))
|
||||
} catch (e) {
|
||||
if (e.name === 'SyntaxError') {
|
||||
return localStorage.getItem(key)
|
||||
} else {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
},
|
||||
set: (key, value) => localStorage.setItem(key, JSON.stringify(value)),
|
||||
remove: key => localStorage.removeItem(key),
|
||||
keys: () => Object.keys(localStorage)
|
||||
}
|
||||
|
||||
export default storage
|
||||
|
|
@ -122,10 +122,6 @@ module.exports = withMDX({
|
|||
source: '/summer/_next/:path*',
|
||||
destination: 'https://summer.hackclub.com/_next/:path*'
|
||||
},
|
||||
{
|
||||
source: '/bank/',
|
||||
destination: 'https://v2.hackclub.dev/bank/'
|
||||
},
|
||||
{
|
||||
source: '/sponsorship/',
|
||||
destination: 'https://workshops.hackclub.com/sponsorship/'
|
||||
|
|
|
|||
|
|
@ -28,5 +28,6 @@
|
|||
"react-use-websocket": "2.7.1",
|
||||
"resnow": "^1.0.0",
|
||||
"theme-ui": "^0.10"
|
||||
}
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
|
|
|||
56
pages/bank.js
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import {
|
||||
Card,
|
||||
Box,
|
||||
Button,
|
||||
Container,
|
||||
Flex,
|
||||
Heading,
|
||||
Image,
|
||||
Text,
|
||||
Grid,
|
||||
theme
|
||||
} from 'theme-ui'
|
||||
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'
|
||||
|
||||
const styles = `
|
||||
::selection {
|
||||
background-color: #e42d42;
|
||||
color: #ffffff;
|
||||
text-shadow: none;
|
||||
}
|
||||
`
|
||||
|
||||
export default function Bank() {
|
||||
return (
|
||||
<>
|
||||
<Box as="main" key="main">
|
||||
<Nav dark />
|
||||
<ForceTheme theme="dark" />
|
||||
<Meta
|
||||
as={Head}
|
||||
title="Bank"
|
||||
description="Hack Club Bank provides a 501(c)(3) status-backed bank account optimized for high school hackathons including invoicing, debit cards, check sending, pre-written legal forms, automated tax filing, and transparent finances. Get fiscal sponsorship designed to help you run great events."
|
||||
/>
|
||||
<style children={styles} />
|
||||
<Box>
|
||||
<Landing />
|
||||
<Features />
|
||||
<Testimonials />
|
||||
<Everything />
|
||||
<Start />
|
||||
</Box>
|
||||
</Box>
|
||||
<Footer dark key="footer" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
BIN
public/bank/bg.jpg
Executable file
|
After Width: | Height: | Size: 382 KiB |
BIN
public/bank/events/dv-hacks.jpg
Executable file
|
After Width: | Height: | Size: 404 KiB |
BIN
public/bank/events/hack-pennsylvania.jpg
Executable file
|
After Width: | Height: | Size: 577 KiB |
BIN
public/bank/events/los-altos-hacks.jpg
Executable file
|
After Width: | Height: | Size: 456 KiB |
BIN
public/bank/events/ma-hacks.jpg
Executable file
|
After Width: | Height: | Size: 366 KiB |
BIN
public/bank/events/slo-hacks.jpg
Executable file
|
After Width: | Height: | Size: 689 KiB |
BIN
public/bank/events/teenhacks-li.jpg
Executable file
|
After Width: | Height: | Size: 366 KiB |
BIN
public/bank/laptop-dark.png
Executable file
|
After Width: | Height: | Size: 536 KiB |
BIN
public/bank/laptop-light.png
Executable file
|
After Width: | Height: | Size: 538 KiB |
BIN
public/bank/logo-512.png
Executable file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/bank/logo.png
Executable file
|
After Width: | Height: | Size: 4.5 KiB |
16
public/bank/logo.svg
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0)">
|
||||
<circle cx="16" cy="16" r="15.25" fill="#E42D45" stroke="#F9D5D9" stroke-width="1.5"/>
|
||||
<path d="M16.1942 8.096C16.1042 8.056 16.0412 8.032 16.0002 8.018V6C16.3582 6 16.7352 6.149 16.9972 6.264C17.2942 6.394 17.6732 6.59 18.0742 6.819C18.8632 7.27 19.8722 7.921 20.9522 8.683C23.1012 10.201 23.5 10.5 25.7072 12.293C26.0982 12.683 26.0982 13.317 25.7072 13.707C25.3172 14.098 24.6832 14.098 24.2932 13.707C22 12 21.8992 11.799 19.7982 10.317C18.7532 9.579 17.8242 8.98 17.0822 8.556C16.7172 8.347 16.4332 8.199 16.1942 8.096Z" fill="white"/>
|
||||
<path d="M15.8062 8.096C15.8962 8.056 15.9592 8.032 16.0002 8.018V6C15.6422 6 15.2652 6.149 15.0032 6.264C14.7062 6.394 14.3272 6.59 13.9262 6.819C13.1372 7.27 12.1282 7.921 11.0482 8.683C8.89924 10.201 8.50049 10.5 6.29324 12.293C5.90224 12.683 5.90224 13.317 6.29324 13.707C6.68324 14.098 7.31724 14.098 7.70724 13.707C10.0005 12 10.1012 11.799 12.2022 10.317C13.2472 9.579 14.1762 8.98 14.9182 8.556C15.2832 8.347 15.5672 8.199 15.8062 8.096Z" fill="white"/>
|
||||
<path d="M7 24C7 23.448 7.448 23 8 23H24C24.552 23 25 23.448 25 24C25 24.552 24.552 25 24 25H8C7.448 25 7 24.552 7 24Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 22C15.448 22 15 21.552 15 21V14C15 13.448 15.448 13 16 13C16.552 13 17 13.448 17 14V21C17 21.552 16.552 22 16 22Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M21 22C20.448 22 20 21.552 20 21V14C20 13.448 20.448 13 21 13C21.552 13 22 13.448 22 14V21C22 21.552 21.552 22 21 22Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 22C10.448 22 10 21.552 10 21V14C10 13.448 10.448 13 11 13C11.552 13 12 13.448 12 14V21C12 21.552 11.552 22 11 22Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="32" height="32" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
31
public/bank/possibility.svg
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
<svg width="128" height="64" viewBox="0 0 128 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.27886 27.6738C4.18716 27.628 3.89436 28.9425 3.89136 28.9525C3.65996 29.7189 3.39136 30.4736 3.15516 31.2386C2.71306 32.67 2.32956 34.1184 1.99266 35.5783C1.87356 36.0946 1.45776 37.0344 1.64396 37.5932C1.75756 37.934 2.76676 38.4034 3.00016 38.5231C4.20426 39.1406 5.46996 39.6301 6.68116 40.228C7.44646 40.6058 9.66476 42.145 9.66476 42.0492" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M2.76631 38.2891C2.55291 37.8624 3.21621 37.3088 3.45461 37.0108C4.65531 35.5099 6.06591 34.2281 7.68311 33.1756C13.1648 29.6082 19.9058 27.3738 26.4653 27.3738" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M26.2285 15.8117C28.2995 15.8117 30.9855 14.855 32.9965 14.3819C33.6625 14.2252 34.3295 14.0486 34.9975 13.9053C35.1785 13.8667 36.2145 13.6784 36.0465 13.7623" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M31.2803 15.5259C31.8293 18.8225 32.9483 22.1952 32.9483 25.5347" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M40.0488 12.3325C41.3168 14.8679 41.2358 18.4479 42.0508 21.1974C42.3028 22.0468 42.9088 25.0383 42.9088 24.915" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M42.1816 18.7633C43.501 18.5 45.0166 18.1454 46.1586 17.9008L48.001 17.5" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M47.627 11.9512C48 14.5 48.398 17.1159 48.628 19.577C48.706 20.4013 48.744 21.2307 48.819 22.0553C48.838 22.2691 49.0099 23.6282 49.0099 23.6282" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M54.0163 16.3196C54.0163 17.6231 53.8983 18.9991 54.0163 20.2967C54.0633 20.8148 54.1603 22.1175 54.1603 22.1175" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M53.249 12.2466V12.1508V11.9591V11.8633V11.7675V11.6716" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M63.569 9.55737C62.627 9.55737 61.993 9.64737 61.109 10.0094C60.527 10.2474 60.08 10.5491 59.552 10.9133C55.072 14.0029 62.386 14.5499 64.373 15.6337C64.914 15.9288 65.519 16.3333 65.628 16.9896C65.889 18.5562 63.398 20.012 62.213 20.555C61.787 20.7503 60.798 21.0892 60.506 20.5048" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M79.2374 9.65771C75.4874 9.65771 73.1394 15.887 75.3204 18.8474C76.7734 20.8191 78.8384 19.6007 80.6434 19.6007" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M86.5252 9.10524C86.6502 9.23694 85.6172 10.9597 85.3882 11.5869C84.5272 13.9569 84.1942 18.9148 87.9402 19.2405C91.7462 19.5715 91.4962 12.3574 90.3522 10.4272C89.6762 9.28614 86.3102 8.87824 86.5252 9.10524Z" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M96.8927 9.31396C96.5567 9.81756 96.5607 11.1555 96.5217 11.5869C96.3147 13.8618 96.2647 16.9305 97.6817 18.8695C99.5997 21.494 102.33 20.0555 103.526 17.6634C103.958 16.7993 104.262 15.7981 104.361 14.8339C104.563 12.8677 103.711 11.9548 103.387 10.3344" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M108.762 9.69287C108.762 12.509 108.634 15.3838 108.762 18.1986C108.792 18.8704 108.803 20.8759 109.751 21.0339C111.627 21.3467 113.546 19.913 115.355 19.913" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M119.171 9.61719C118.939 10.3157 119.191 11.3439 119.234 12.0744C119.335 13.8263 119.327 15.6377 119.234 17.3933C119.211 17.8178 118.794 19.6907 119.296 20.1927" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M119.297 8.87088C120.629 7.98288 123.034 9.10868 124.118 10.1772C126.644 12.667 126.701 16.4657 124.087 18.9487C123.112 19.8748 121.366 21.5323 119.857 20.8461" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M29.1592 38.2793C29.8752 41.1454 31.1862 43.8818 32.0002 46.7305C32.1362 47.2067 32.8742 51.4929 32.8742 50.1547" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M29.6689 37.8422C29.8699 37.4396 30.7439 37.2604 30.9799 37.1865C32.6199 36.6742 34.3709 36.5308 36.0799 36.5308C36.7299 36.5308 38.4169 36.5693 38.5569 37.6236C38.8999 40.1981 36.1559 41.8354 34.2589 42.942C34.1909 42.9818 31.882 44.2807 32.146 44.5448C32.522 44.9212 33.8509 44.2195 34.1859 44.1077C35.6379 43.6237 39.22 42.2586 40.16 44.3263C41.78 47.8905 35.8429 50.5221 33.2389 50.9561" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M47.4458 34.1265C46.4078 34.1265 45.1058 34.7496 44.3138 35.0008C43.7178 35.1896 42.1588 35.6036 41.9818 36.3122C41.8428 36.8702 42.2857 37.6659 42.4187 38.1336C42.7417 39.2632 42.9658 40.412 43.2208 41.5578C43.6228 43.3661 44.2868 47.0234 45.9168 48.1876C46.5048 48.608 47.5228 47.7048 47.9558 47.4591C49.4378 46.6196 51.0238 45.8562 52.7648 45.8562" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M43.6572 41.4119C45.3102 41.1757 46.4712 40.4791 48.2472 39.8091" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M59.4678 33.2523C59.4678 36.8428 61.7118 39.4608 65.0048 38.8621C66.4788 38.5941 68.3758 37.1847 68.7928 35.6565C68.9618 35.0379 69.1258 34.0697 68.7928 33.4708C68.5368 33.0097 67.8458 32.504 67.8458 33.3251" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M64.7852 39.5176C64.7852 41.7075 64.7662 40.0493 65.6592 45.3461C65.6942 45.5507 65.8782 46.1364 65.8782 45.9289" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M74.402 31.7952C72.4997 34.5 72.5 35.2963 72.5004 37C72.5009 39 72.4595 40.6967 73.163 42.1406C73.866 43.5845 75.442 44.7516 77.098 44.1076C80.645 42.7282 80.967 37.9536 80.303 34.855C80.083 33.8275 79.82 32.6063 78.846 32.0136C77.443 31.1595 75.878 31.3497 74.402 31.7952Z" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M84.1654 31C84 31.5 83.9438 34.7816 84.1648 35.9477C84.6358 38.4416 87.0329 42.9072 90.1389 42.2861C91.1099 42.092 92.1148 40.9722 92.6888 40.2462C93.4598 39.2695 93.4169 39.0817 93.7809 37.5505C94.0439 36.4465 94.0328 35.1772 93.9998 34.0535C93.9548 32.5301 93.2778 31.3706 92.6158 30.0464" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M99.2925 38.1646C99.0001 35 99.501 31.5 101 29" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M99.3916 42.2863C99.3916 42.2378 99.3916 42.1892 99.3916 42.1406" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M104 37.5C103.682 33.6884 104.85 31.3665 107 28.5" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M103.398 41.7034C103.578 41.5233 103.544 41.489 103.544 41.7034" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M67.3672 54.7853C68.0732 53.8449 69.7332 53.0178 70.4442 52.5972C73.9052 50.5523 77.8122 49.0719 81.7272 48.2209C82.5132 48.05 85.0332 47.326 85.6252 48.0158C86.1512 48.6297 84.5462 49.8913 84.2572 50.1355C82.6162 51.5204 80.9492 52.8754 79.4702 54.4434C79.2872 54.638 78.4872 55.6851 78.7872 56.0844C79.3622 56.8511 80.7192 56.1357 81.6592 55.9478C83.5072 55.578 85.3042 54.9869 87.1292 54.5118C96.5232 52.0654 105.783 49.2032 115.164 46.7166" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.7 KiB |
BIN
public/hackers/athul.jpg
Executable file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
public/hackers/connie.jpg
Executable file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/hackers/jamsheed.jpg
Executable file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
public/hackers/joy.jpg
Executable file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/hackers/kat.jpg
Executable file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/hackers/khushi.jpg
Executable file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/hackers/lachlan.jpg
Executable file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/hackers/megan.png
Executable file
|
After Width: | Height: | Size: 62 KiB |
BIN
public/hackers/michael.jpg
Executable file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
public/hackers/michael.png
Executable file
|
After Width: | Height: | Size: 492 KiB |
BIN
public/hackers/rashid.jpg
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
public/hackers/selynna.jpg
Executable file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/hackers/victor.png
Executable file
|
After Width: | Height: | Size: 17 KiB |
BIN
public/hackers/wesley.jpg
Executable file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
public/team/mel.png
Executable file → Normal file
|
Before Width: | Height: | Size: 846 KiB After Width: | Height: | Size: 2.4 MiB |
1
public/underline.svg
Executable file
|
|
@ -0,0 +1 @@
|
|||
<svg preserveAspectRatio="none" width="119" height="6" viewBox="0 0 119 6" xmlns="http://www.w3.org/2000/svg"><path d="M117.434 3.853C59.027 5.933 84.784-2.46 1.566 3.436" stroke="#e42d42" stroke-width="2" fill="none" stroke-linecap="round"/></svg>
|
||||
|
After Width: | Height: | Size: 249 B |
45
yarn.lock
|
|
@ -1346,6 +1346,11 @@ debug@^4.1.0:
|
|||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
deepmerge@^2.1.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
|
||||
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
|
||||
|
||||
deepmerge@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
||||
|
|
@ -1630,6 +1635,19 @@ form-data@~2.3.2:
|
|||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
formik@^2.2.9:
|
||||
version "2.2.9"
|
||||
resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0"
|
||||
integrity sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==
|
||||
dependencies:
|
||||
deepmerge "^2.1.1"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
lodash "^4.17.21"
|
||||
lodash-es "^4.17.21"
|
||||
react-fast-compare "^2.0.1"
|
||||
tiny-warning "^1.0.2"
|
||||
tslib "^1.10.0"
|
||||
|
||||
fsevents@~2.3.1:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
|
|
@ -1849,7 +1867,7 @@ hmac-drbg@^1.0.1:
|
|||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
hoist-non-react-statics@^3.3.1:
|
||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
|
|
@ -2225,6 +2243,11 @@ locate-path@^5.0.0:
|
|||
dependencies:
|
||||
p-locate "^4.1.0"
|
||||
|
||||
lodash-es@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
|
||||
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
|
||||
|
||||
lodash.sortby@^4.7.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||
|
|
@ -2837,6 +2860,11 @@ react-dom@^17.0.2:
|
|||
object-assign "^4.1.1"
|
||||
scheduler "^0.20.2"
|
||||
|
||||
react-fast-compare@^2.0.1:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
|
||||
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
|
||||
|
||||
react-is@17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||
|
|
@ -3336,6 +3364,11 @@ timers-browserify@2.0.12, timers-browserify@^2.0.4:
|
|||
dependencies:
|
||||
setimmediate "^1.0.4"
|
||||
|
||||
tiny-warning@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||
|
||||
to-arraybuffer@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
|
||||
|
|
@ -3393,6 +3426,11 @@ ts-pnp@^1.1.6:
|
|||
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
|
||||
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
|
||||
|
||||
tslib@^1.10.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tty-browserify@0.0.0:
|
||||
version "0.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
|
||||
|
|
@ -3430,6 +3468,11 @@ unbox-primitive@^1.0.0:
|
|||
has-symbols "^1.0.2"
|
||||
which-boxed-primitive "^1.0.2"
|
||||
|
||||
unfetch@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
|
||||
integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==
|
||||
|
||||
unherit@^1.0.4:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22"
|
||||
|
|
|
|||