Skip to content

Commit b623003

Browse files
authored
Merge pull request #1476 from hackclub/garyhtou/hcb-apply
[HCB] Updates to application form
2 parents c9ea1e0 + 80a41bc commit b623003

19 files changed

+695
-397
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { useRouter } from 'next/router'
2+
import { useRef, useState } from 'react'
3+
import { Alert, Button, Text } from 'theme-ui'
4+
import FormContainer from './form-container'
5+
import OrganizationInfoForm from './org-form'
6+
import PersonalInfoForm from './personal-form'
7+
import { onSubmit } from './submit'
8+
import TeenagerOrAdultForm from './teenager-or-adult-form'
9+
import MultiStepForm from './multi-step-form'
10+
11+
export default function ApplicationForm() {
12+
const router = useRouter()
13+
const formContainer = useRef()
14+
const [formError, setFormError] = useState(null)
15+
const [isSubmitting, setIsSubmitting] = useState(false)
16+
17+
const requiredFields = {
18+
// Key: form field name
19+
// Value: humanize field name used in error message
20+
eventTeenagerLed: 'are you a teenager?',
21+
eventName: 'project name',
22+
eventPostalCode: 'project zip/postal code',
23+
eventDescription: 'project description',
24+
eventIsPolitical: "project's political activity",
25+
eventPoliticalActivity: "project's political activity",
26+
eventHasWebsite: 'project website',
27+
eventAnnualBudget: 'project annual budget',
28+
firstName: 'first name',
29+
lastName: 'last name',
30+
userEmail: 'email',
31+
userPhone: 'phone number',
32+
userBirthday: 'birthday',
33+
userAddressLine1: 'address line 1',
34+
userAddressCity: 'city',
35+
userAddressProvince: 'state/province',
36+
userAddressPostalCode: 'ZIP/postal code',
37+
userAddressCountry: 'country',
38+
39+
referredBy: 'how did you hear about HCB?'
40+
}
41+
42+
const submitButton = (
43+
<Button
44+
variant="ctaLg"
45+
type="submit"
46+
disabled={isSubmitting}
47+
sx={{
48+
backgroundImage: theme => theme.util.gx('cyan', 'blue'),
49+
'&:disabled': {
50+
background: 'muted',
51+
cursor: 'not-allowed',
52+
transform: 'none !important'
53+
},
54+
width: 'fit-content'
55+
}}
56+
>
57+
{isSubmitting ? 'Submitting…' : 'Submit'}
58+
</Button>
59+
)
60+
61+
return (
62+
<FormContainer
63+
ref={formContainer}
64+
className={formError ? 'has-errors' : null}
65+
onSubmit={event =>
66+
onSubmit({
67+
event,
68+
router,
69+
form: formContainer,
70+
setFormError,
71+
setIsSubmitting,
72+
requiredFields
73+
})
74+
}
75+
>
76+
<MultiStepForm
77+
submitButton={submitButton}
78+
validationErrors={
79+
formError && (
80+
<Alert bg="primary" sx={{ mt: 4 }}>
81+
{formError}
82+
</Alert>
83+
)
84+
}
85+
>
86+
{/* Step 1 */}
87+
<MultiStepForm.Step title="Let's get started">
88+
<Text as="p" variant="caption" sx={{ marginBottom: '1rem' }}>
89+
Fill out this quick application to run your project on HCB. If you
90+
are a teenager, there is a very high likelihood we will accept your
91+
project. We just need to collect a few pieces of information first.
92+
</Text>
93+
<TeenagerOrAdultForm requiredFields={requiredFields} />
94+
</MultiStepForm.Step>
95+
{/* Step 2 */}
96+
<MultiStepForm.Step>
97+
<OrganizationInfoForm requiredFields={requiredFields} />
98+
</MultiStepForm.Step>
99+
{/* Step 3 */}
100+
<MultiStepForm.Step title="Personal details">
101+
<PersonalInfoForm requiredFields={requiredFields} />
102+
</MultiStepForm.Step>
103+
</MultiStepForm>
104+
</FormContainer>
105+
)
106+
}

components/fiscal-sponsorship/apply/callout.js

-29
This file was deleted.

components/fiscal-sponsorship/apply/checkbox.js

-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export default function Checkbox({ name, defaultChecked = false, size = 38 }) {
2828
name={name}
2929
aria-checked={checked}
3030
role="checkbox"
31-
tabindex="0"
3231
onClick={() => toggle()}
3332
onKeyDown={e => e.key === 'Enter' && toggle()}
3433
/>

components/fiscal-sponsorship/apply/field.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,24 @@ export default function Field({
1111
children
1212
}) {
1313
const router = useRouter()
14-
const isRequired = requiredFields.includes(name)
14+
const isRequired = Object.keys(requiredFields).includes(name)
1515

1616
/* Fill in the field input element with the value from sessionStorage.
1717
Note: the custom checkbox component does this in its own useEffect hook. */
1818
useEffect(() => {
1919
const value =
2020
router.query[name] || sessionStorage.getItem('bank-signup-' + name)
21-
if (value) {
22-
const input = document.getElementById(name)
23-
if (input) input.value = value
21+
if (!value) return
22+
23+
let input = document.getElementById(name)
24+
if (input) {
25+
input.value = value
26+
return
2427
}
28+
29+
// Maybe it's radio buttons
30+
input = document.querySelector(`input[name='${name}']`)
31+
if (input) input.checked = true
2532
}, [router.query, name])
2633

2734
return (

components/fiscal-sponsorship/apply/form-container.js

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { forwardRef } from 'react'
22
import { Box, Container } from 'theme-ui'
3+
import { TeenagerLedProvider } from '../../../components/fiscal-sponsorship/apply/teenager-led-context'
4+
import { MultiStepProvider } from './multi-step-context'
35

46
const formContainer = forwardRef(({ children, ...props }, ref) => {
57
return (
@@ -9,11 +11,15 @@ const formContainer = forwardRef(({ children, ...props }, ref) => {
911
sx={{
1012
bg: 'snow',
1113
px: [3, 5],
12-
py: 5,
13-
minHeight: '100dvb',
14+
py: [1, 5],
15+
pb: 5,
16+
minHeight: [null, null, '100dvb'],
1417
'&.has-errors div[aria-required="true"] input:placeholder-shown': {
1518
borderColor: 'primary'
1619
},
20+
'&.has-errors div[aria-required="true"] input[type="date"]': {
21+
borderColor: 'primary'
22+
},
1723
'&.has-errors div[aria-required="true"] textarea:placeholder-shown': {
1824
borderColor: 'primary'
1925
}
@@ -24,14 +30,10 @@ const formContainer = forwardRef(({ children, ...props }, ref) => {
2430
variant="copy"
2531
sx={{
2632
ml: 0,
27-
display: 'flex',
28-
flexDirection: 'column',
29-
columnGap: 4,
30-
rowGap: 3,
3133
px: 0
3234
}}
3335
>
34-
{children}
36+
<TeenagerLedProvider>{children}</TeenagerLedProvider>
3537
</Container>
3638
</Box>
3739
)

components/fiscal-sponsorship/apply/hcb-info.js

+22-33
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
1-
import { Box, Link, Heading } from 'theme-ui'
1+
import { Box, Link, Heading, Grid } from 'theme-ui'
22
import Icon from '../../icon'
3+
import { useMultiStepContext } from './multi-step-context'
4+
import { useEffect } from 'react'
35

46
export default function HCBInfo() {
7+
const { step } = useMultiStepContext()
8+
const firstStep = step === 0
9+
510
return (
611
<Box
712
sx={{
13+
display: firstStep ? 'block' : ['none', 'block'],
814
gridArea: 'info',
915
alignItems: 'start',
1016
mark: { color: '#ec555c', bg: 'inherit' },
11-
ul: { pl: [3, 0], color: 'muted', mb: 4 },
17+
ul: { pl: 3, color: 'muted', mb: 4 },
1218
p: { color: 'muted', mb: 0 }
1319
}}
1420
>
1521
<Heading variant="subheadline">
16-
HCB is a{' '}
22+
HCB is not a bank, we're a{' '}
1723
<Link
1824
href="https://en.wikipedia.org/wiki/Fiscal_sponsorship"
1925
target="_blank"
@@ -27,39 +33,22 @@ export default function HCBInfo() {
2733
<Icon glyph="external" size={24} aria-hidden />
2834
</Link>
2935
</Heading>
36+
{/* <Grid columns={2} bg="white" sx={{ color: 'muted', border: 'slate 1px' }}>
37+
<Box>Gives your project nonprofit status.</Box>
38+
</Grid> */}
39+
3040
<ul>
31-
<li>Nonprofit status.</li>
32-
<li>Tax-deductible donations.</li>
33-
</ul>
34-
<Heading variant="subheadline">
35-
HCB provides a financial platform.
36-
</Heading>
37-
<ul>
38-
<li>A donations page and invoicing system.</li>
39-
<li>Transfer money electronically.</li>
40-
<li>Order cards for you and your team to make purchases.</li>
41-
</ul>
42-
<Heading variant="subheadline">HCB is not a bank.</Heading>
43-
<ul>
44-
<li>
45-
We partner with{' '}
46-
<Link href="https://column.com" target="_blank">
47-
Column N.A.
48-
</Link>{' '}
49-
to offer restricted funds to fiscally-sponsored projects.
50-
</li>
51-
<li>
52-
You can't deposit or withdraw cash. But you can receive any kind of
53-
electronic payment!
54-
</li>
41+
<li>HCB gives your project nonprofit status.</li>
42+
<li>Allows you to raise tax-deductible donations.</li>
43+
<li>Provides a financial platform.</li>
44+
<li>Allows you to order cards to make purchases.</li>
5545
</ul>
56-
<Heading variant="subheadline">HCB is not for for-profits.</Heading>
5746
<p>
58-
If you’re looking to set up a for-profit entity, consider{' '}
59-
<Link href="https://stripe.com/atlas" target="_blank">
60-
Stripe Atlas
61-
</Link>
62-
.
47+
HCB partners with{' '}
48+
<Link href="https://column.com" target="_blank">
49+
Column N.A.
50+
</Link>{' '}
51+
to provide restricted funds to fiscally-sponsored projects.
6352
</p>
6453
</Box>
6554
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { createContext, useContext, useState } from 'react'
2+
3+
const MultiStepContext = createContext()
4+
const useMultiStepContext = () => useContext(MultiStepContext)
5+
6+
const MultiStepProvider = ({ children }) => {
7+
const [step, setStep] = useState(0)
8+
9+
const useStepper = steps => {
10+
const modifyStep = number => {
11+
const newStep = step + number
12+
13+
// Guard against invalid step numbers
14+
if (newStep < 0 || newStep > steps.length - 1) {
15+
console.error(
16+
`[MultiStepProvider] Invalid new step number: ${newStep}. Current step number: ${step}`
17+
)
18+
return
19+
}
20+
21+
setStep(newStep)
22+
}
23+
24+
return {
25+
nextStep: () => modifyStep(1),
26+
previousStep: () => modifyStep(-1)
27+
}
28+
}
29+
30+
return (
31+
<MultiStepContext.Provider value={{ step, useStepper }}>
32+
{children}
33+
</MultiStepContext.Provider>
34+
)
35+
}
36+
37+
export { MultiStepProvider, useMultiStepContext }

0 commit comments

Comments
 (0)