diff --git a/components/home/join-form.js b/components/home/join-form.js
index 6410821c..747d09a9 100644
--- a/components/home/join-form.js
+++ b/components/home/join-form.js
@@ -1,24 +1,9 @@
-import { useState, useEffect } from 'react'
-import { Card, Label, Input, Button, Checkbox, Textarea } from 'theme-ui'
-import fetch from 'isomorphic-unfetch'
+import { Card, Label, Input, Checkbox, Textarea } from 'theme-ui'
+import useForm from '../../lib/use-form'
+import Submit from '../submit'
const JoinForm = () => {
- const [name, setName] = useState('')
- const [email, setEmail] = useState('')
- const [teen, setTeen] = useState(false)
- const [reason, setReason] = useState('')
-
- const [status, setStatus] = useState('')
-
- useEffect(() => {
- setTimeout(() => {
- setName('')
- setEmail('')
- setTeen(false)
- setReason('')
- setStatus('')
- }, 1500)
- }, [status])
+ const { status, formProps, useField } = useForm('/api/join')
return (
{
}
}}
>
-
diff --git a/components/submit.js b/components/submit.js
new file mode 100644
index 00000000..06e2b5d8
--- /dev/null
+++ b/components/submit.js
@@ -0,0 +1,44 @@
+import { Button } from 'theme-ui'
+import theme from '../lib/theme'
+
+const bg = {
+ default: {
+ bg: 'blue',
+ backgroundImage: theme.util.gradient('cyan', 'blue')
+ },
+ success: {
+ bg: 'green',
+ backgroundImage: theme.util.gradient('green', 'cyan')
+ },
+ error: {
+ bg: 'orange',
+ backgroundImage: theme.util.gradient('orange', 'red'),
+ boxShadow: `0 0 0 1px ${theme.colors.white}, 0 0 0 4px ${theme.colors.primary}`
+ }
+}
+
+const Submit = ({
+ status,
+ labels = { default: 'Submit', error: 'Error!', success: 'Submitted!' },
+ width = '100%',
+ sx,
+ ...props
+}) => (
+
+)
+
+export default Submit
diff --git a/lib/theme.js b/lib/theme.js
index 2fbdc1e3..c32cd92f 100644
--- a/lib/theme.js
+++ b/lib/theme.js
@@ -31,6 +31,7 @@ theme.util.gradientText = (from, to) => ({
})
theme.buttons.primary = merge(theme.buttons.primary, {
+ justifyContent: 'center',
fontFamily: 'inherit',
borderRadius: 'circle',
boxShadow: 'card',
@@ -81,4 +82,22 @@ theme.cards.translucentDark = {
}
}
+theme.forms.input = merge(theme.forms.input, { boxShadow: 'none !important' })
+theme.forms.textarea = { variant: 'forms.input' }
+theme.forms.label = merge(theme.forms.label, {
+ display: 'flex',
+ flexDirection: 'column',
+ textAlign: 'left',
+ fontSize: 2,
+ mb: 3
+})
+theme.forms.labelCheckbox = {
+ variant: 'forms.label',
+ flexDirection: 'row !important',
+ alignItems: 'center',
+ svg: { color: 'muted' }
+}
+
+theme.text.lead = {}
+
export default theme
diff --git a/lib/use-form.js b/lib/use-form.js
new file mode 100644
index 00000000..fc281aab
--- /dev/null
+++ b/lib/use-form.js
@@ -0,0 +1,69 @@
+import { useState, useEffect } from 'react'
+import fetch from 'isomorphic-unfetch'
+
+const useForm = (
+ submitURL = '/',
+ callback,
+ options = { clearOnSubmit: 2000, method: 'post' }
+) => {
+ const [status, setStatus] = useState('default')
+ const [data, setData] = useState({})
+ const [touched, setTouched] = useState({})
+
+ const onFieldChange = (e, name, type) => {
+ e.persist()
+ const value = e.target[type === 'checkbox' ? 'checked' : 'value']
+ setData((data) => ({ ...data, [name]: value }))
+ }
+
+ useEffect(() => {
+ setTouched(Object.keys(data))
+ }, [data])
+
+ const useField = (name, type = 'text', ...props) => {
+ const checkbox = type === 'checkbox'
+ const empty = checkbox ? false : ''
+ const onChange = (e) => onFieldChange(e, name, type)
+ const value = data[name]
+ return {
+ name,
+ type: name === 'email' ? 'email' : type,
+ [checkbox ? 'checked' : 'value']: value || empty,
+ onChange,
+ ...props
+ }
+ }
+
+ const { method = 'post' } = options
+ const action =
+ submitURL?.startsWith('/') && process.env.NODE_ENV !== 'development'
+ ? `https://v3.hackclub.com${submitURL}`
+ : submitURL
+
+ const onSubmit = (e) => {
+ e.preventDefault()
+ fetch(action, {
+ method,
+ body: JSON.stringify(data)
+ })
+ .then((r) => r.json())
+ .then((r) => {
+ setStatus('success')
+ if (callback) callback(r)
+ setTimeout(() => setStatus('default'), 2000)
+ if (options.clearOnSubmit) {
+ setTimeout(() => setData({}), options.clearOnSubmit)
+ }
+ })
+ .catch((e) => {
+ console.error(e)
+ setStatus('error')
+ })
+ }
+
+ const formProps = { method, action, onSubmit }
+
+ return { status, data, touched, useField, formProps }
+}
+
+export default useForm
diff --git a/pages/index.js b/pages/index.js
index c3b491b1..e580204c 100644
--- a/pages/index.js
+++ b/pages/index.js
@@ -99,6 +99,7 @@ const Window = ({ title, children, ...props }) => (
)
+export default () => (
<>