Skip to content

Commit b2b0baf

Browse files
authored
2023 census form (#289)
* WIP 2023 census form. Only professional questions, no submission * Add netlify function for connecting to Airtable * Clean up professional form * Demographics page * Form submission * Submitting state * Fix glitch on empty Discords list * Persistent submission ID
1 parent 289d39c commit b2b0baf

File tree

12 files changed

+8432
-161
lines changed

12 files changed

+8432
-161
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,8 @@ yarn-error.log*
3232
# vercel
3333
.vercel
3434

35+
.netlify
36+
.env
37+
3538
# typescript
3639
*.tsbuildinfo

netlify/functions/airtable.mts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { Context } from "@netlify/functions";
2+
3+
const handler = async (request: Request, context: Context) => {
4+
if (request.method !== "POST") {
5+
return new Response("Method Not Allowed", { status: 405 });
6+
}
7+
8+
const {
9+
meta,
10+
data: { id, ...data },
11+
} = await request.json();
12+
const { base, table } = meta;
13+
14+
try {
15+
// // Make the request using fetch
16+
const response = await fetch(
17+
`https://api.airtable.com/v0/${base}/${table}`,
18+
{
19+
method: "PATCH",
20+
headers: {
21+
Authorization: `Bearer ${process.env.AIRTABLE_RECORD}`,
22+
"Content-Type": "application/json",
23+
},
24+
body: JSON.stringify({
25+
performUpsert: {
26+
fieldsToMergeOn: ["id"],
27+
},
28+
records: [
29+
{
30+
fields: {
31+
id,
32+
...data,
33+
},
34+
},
35+
],
36+
}),
37+
},
38+
);
39+
const output = await response.json();
40+
console.log(output);
41+
return new Response("ok");
42+
} catch (e) {
43+
return new Response(JSON.stringify(e), { status: 400 });
44+
}
45+
};
46+
47+
export default handler;

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "reactiflux.com",
3-
"version": "π",
3+
"version": "0.3.14",
44
"private": true,
55
"license": "MIT",
66
"scripts": {
@@ -21,10 +21,12 @@
2121
}
2222
},
2323
"dependencies": {
24+
"@netlify/functions": "^2.4.1",
2425
"@types/node": "20.6.0",
2526
"@types/react": "17.0.37",
2627
"@types/styled-components": "^5.1.15",
2728
"@vcarl/remark-headings": "^0.0.1",
29+
"airtable": "^0.12.2",
2830
"date-fns": "^2.27.0",
2931
"eslint": "7",
3032
"eslint-config-next": "12.0.4",
@@ -33,6 +35,7 @@
3335
"mdast-util-to-string": "^3.1.0",
3436
"minireset.css": "^0.0.6",
3537
"moment": "^2.27.0",
38+
"netlify-cli": "^17.10.2",
3639
"next": "^12.3.4",
3740
"next-sitemap": "^4.2.2",
3841
"polished": "^3.6.5",
@@ -42,6 +45,7 @@
4245
"react-dom": "17.0.2",
4346
"react-headroom": "^2.1.2",
4447
"react-helmet": "^5.2.1",
48+
"react-hook-form": "^7.49.2",
4549
"rehype-slug": "^5.0.0",
4650
"rehype-stringify": "^9.0.2",
4751
"remark-gfm": "^3.0.1",

src/assets/un-geo-regions.png

81.3 KB
Loading
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import React from "react";
2+
import { useForm } from "react-hook-form";
3+
import styled from "styled-components";
4+
5+
import { Button } from "@components";
6+
import { TextareaInner } from "@components/Form/Input";
7+
8+
import {
9+
CensusItem,
10+
Explanation,
11+
Question,
12+
} from "@components/Census/2023/Styles";
13+
import { Region } from "@components/Census/2023/Region";
14+
15+
const TextareaEl = styled(TextareaInner)`
16+
font-size: 0.8em;
17+
`;
18+
19+
export const Demographics = ({ onSubmit }) => {
20+
const form = useForm();
21+
const { handleSubmit, register, watch, formState } = form;
22+
const { isSubmitting } = formState;
23+
const religion = watch("religion");
24+
const gender = watch("gender");
25+
const orientation = watch("sexual-orientation");
26+
27+
const inclusionExplanation = (
28+
<Explanation summary="(why we ask)">
29+
<p>
30+
This is a survey of the members of our community, and we don’t make
31+
assumptions about who is here. We ask questions like this so that we can
32+
have a clearer sense of whether we’re achieving our goals.
33+
</p>
34+
<p>
35+
Reactiflux is an inclusive community that strives to be welcoming to
36+
all, regardless of level of experience, gender identity and expression,
37+
sexual orientation, disability, neurodivergence, personal appearance,
38+
body, race, ethnicity, age, religion, nationality, or other similar
39+
characteristic.
40+
</p>
41+
</Explanation>
42+
);
43+
44+
return (
45+
<form onSubmit={handleSubmit(onSubmit)}>
46+
<CensusItem>
47+
<Question>Do you feel well represented within Reactiflux?</Question>
48+
{["Yes", "Somewhat", "No"].map((r) => (
49+
<div key={r}>
50+
<label>
51+
<input {...register("represented")} type="radio" value={r} /> {r}
52+
</label>
53+
</div>
54+
))}
55+
</CensusItem>
56+
57+
<CensusItem>
58+
<Question>
59+
Have you felt marginalized or harassed by another member of
60+
Reactiflux?
61+
</Question>
62+
{["Yes", "Somewhat", "No"].map((r) => (
63+
<div key={r}>
64+
<label>
65+
<input {...register("marginalized")} type="radio" value={r} /> {r}
66+
</label>
67+
</div>
68+
))}
69+
<label>
70+
<TextareaEl
71+
{...register("marginalized-free-response")}
72+
type="text"
73+
placeholder="(free response)"
74+
/>
75+
</label>
76+
</CensusItem>
77+
78+
<CensusItem>
79+
<Question>How old are you?</Question>
80+
{["Under 18", "18–22", "23–27", "28–34", "35–44", "45–59", "60+"].map(
81+
(r) => (
82+
<div key={r}>
83+
<label>
84+
<input {...register("age")} type="radio" value={r} /> {r}
85+
</label>
86+
</div>
87+
),
88+
)}
89+
</CensusItem>
90+
91+
<Region form={form} />
92+
93+
<CensusItem>
94+
<Question>Do you identify as a member of a religious group?</Question>
95+
{[
96+
"Agnostic",
97+
"Buddhism",
98+
"Catholocism",
99+
"Christianity",
100+
"Hinduism",
101+
"Islam",
102+
"Judaism",
103+
"Protestantism",
104+
"Sikhism",
105+
"No religion",
106+
].map((r) => (
107+
<div key={r}>
108+
<label>
109+
<input {...register("religion")} type="radio" value={r} /> {r}
110+
</label>
111+
</div>
112+
))}
113+
<div>
114+
<label>
115+
<input {...register("religion")} type="radio" value="Other" /> Other
116+
(please include)
117+
</label>
118+
{religion && religion.includes("Other") && (
119+
<>
120+
{" "}
121+
<label>
122+
<input {...register("other-religion")} type="text" />
123+
</label>
124+
</>
125+
)}
126+
</div>
127+
{inclusionExplanation}
128+
<Explanation summary="(how did we decide what to include?)">
129+
We reviewed several national census surveys and publicly available
130+
global demographic datasets, including any religions that were
131+
included by several sources and represent a portion of the global
132+
population greater than 0.2%. The religions are presented in
133+
alphabetical order.
134+
</Explanation>
135+
</CensusItem>
136+
137+
<CensusItem>
138+
<Question>What is your gender identity?</Question>
139+
{[
140+
"Male",
141+
"Female",
142+
"Non-binary or genderfluid",
143+
"Agender",
144+
"Prefer not to say",
145+
].map((r) => (
146+
<div key={r}>
147+
<label>
148+
<input {...register("gender")} type="radio" value={r} /> {r}
149+
</label>
150+
</div>
151+
))}
152+
<div>
153+
<label>
154+
<input {...register("gender")} type="radio" value="Other" /> Other
155+
(please include)
156+
</label>
157+
{gender && gender.includes("Other") && (
158+
<>
159+
{" "}
160+
<label>
161+
<input {...register("other-gender")} type="text" />
162+
</label>
163+
</>
164+
)}
165+
</div>
166+
{inclusionExplanation}
167+
</CensusItem>
168+
169+
<CensusItem>
170+
<Question>Do you identify as transgender?</Question>
171+
{["Yes", "No", "Prefer not to say"].map((r) => (
172+
<div key={r}>
173+
<label>
174+
<input {...register("trans")} type="radio" value={r} /> {r}
175+
</label>
176+
</div>
177+
))}
178+
{inclusionExplanation}
179+
</CensusItem>
180+
181+
<CensusItem>
182+
<Question>Are you a person living with a disability?</Question>
183+
{["Yes", "No", "Prefer not to say"].map((r) => (
184+
<div key={r}>
185+
<label>
186+
<input {...register("disability")} type="radio" value={r} /> {r}
187+
</label>
188+
</div>
189+
))}
190+
{inclusionExplanation}
191+
</CensusItem>
192+
193+
<CensusItem>
194+
<Question>What is your sexual orientation?</Question>
195+
{[
196+
"Asexual",
197+
"Bisexual",
198+
"Heterosexual",
199+
"Homosexual",
200+
"Pansexual",
201+
"Queer",
202+
"Prefer not to say",
203+
].map((r) => (
204+
<div key={r}>
205+
<label>
206+
<input
207+
{...register("sexual-orientation")}
208+
type="radio"
209+
value={r}
210+
/>{" "}
211+
{r}
212+
</label>
213+
</div>
214+
))}
215+
<div>
216+
<label>
217+
<input
218+
{...register("sexual-orientation")}
219+
type="radio"
220+
value="Other"
221+
/>{" "}
222+
Other (please include)
223+
</label>
224+
{orientation && orientation.includes("Other") && (
225+
<>
226+
{" "}
227+
<label>
228+
<input {...register("other-sexual-orientation")} type="text" />
229+
</label>
230+
</>
231+
)}
232+
</div>
233+
{inclusionExplanation}
234+
</CensusItem>
235+
236+
<CensusItem>
237+
<Question>
238+
Anything else you’d like to share with the Reactiflux staff?
239+
</Question>
240+
<label>
241+
<TextareaEl
242+
{...register("free-response")}
243+
placeholder="(free response)"
244+
/>
245+
</label>
246+
</CensusItem>
247+
248+
<Button
249+
disabled={isSubmitting}
250+
as="input"
251+
type="submit"
252+
value={isSubmitting ? "…" : "Submit"}
253+
/>
254+
</form>
255+
);
256+
};

0 commit comments

Comments
 (0)