Skip to content

Commit 20fa56f

Browse files
authored
Merge pull request #125 from HackAtUCI/feature/client-apply
Implement client-side application form submission
2 parents bc528db + 196d33f commit 20fa56f

File tree

4 files changed

+82
-9
lines changed

4 files changed

+82
-9
lines changed

apps/site/src/app/apply/sections/Components/DropdownSelect.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const OtherPopup = ({ value, name }: OtherProps) => {
2626
</label>
2727
<input
2828
type="text"
29-
name={`${name}-other-input`}
29+
name={`other_${name}`}
3030
id={`${name}-other-input`}
3131
className="border-b-2 p-1 h-6 border-black w-6/12"
3232
required

apps/site/src/app/apply/sections/Components/RadioSelect.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ interface RadioInputs {
1010
containerClass: string;
1111
}
1212

13-
interface IsChecked {
13+
interface OtherInputProps {
1414
isChecked: boolean;
15-
id: string;
15+
name: string;
1616
}
1717

18-
const OtherInput = forwardRef<HTMLInputElement, IsChecked>(
19-
({ isChecked, id }, ref) => (
18+
const OtherInput = forwardRef<HTMLInputElement, OtherInputProps>(
19+
({ isChecked, name }, ref) => (
2020
<input
2121
ref={ref}
2222
type="text"
23-
name={`${id}-other`}
23+
name={name}
2424
className={
2525
isChecked
2626
? "border-b-2 p-1 h-6 border-black w-6/12"
@@ -76,7 +76,7 @@ export default function RadioSelect({
7676
</label>
7777
<OtherInput
7878
isChecked={isOtherChecked}
79-
id={IdentifierId}
79+
name={`other_${name}`}
8080
ref={otherRef}
8181
/>
8282
</div>

apps/site/src/app/apply/sections/Form/Form.tsx

+68-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
"use client";
2+
3+
import { FormEvent, useState } from "react";
4+
import axios from "axios";
5+
16
import Button from "@/lib/components/Button/Button";
27

38
import BasicInformation from "./BasicInformation";
@@ -8,20 +13,82 @@ import ResumeInformation from "./ResumeInformation";
813

914
import styles from "./Form.module.scss";
1015

16+
const APPLY_PATH = "/api/user/apply";
17+
const FIELDS_WITH_OTHER = ["pronouns", "ethnicity", "school", "major"];
18+
1119
export default function Form() {
20+
const [submitting, setSubmitting] = useState(false);
21+
const [sessionExpired, setSessionExpired] = useState(false);
22+
23+
const handleSubmit = async (
24+
event: FormEvent<HTMLFormElement>,
25+
): Promise<void> => {
26+
// Disable native post submission
27+
event.preventDefault();
28+
setSubmitting(true);
29+
setSessionExpired(false);
30+
31+
const formData = new FormData(event.currentTarget);
32+
33+
// Use other values when selected
34+
for (const field of FIELDS_WITH_OTHER) {
35+
const otherField = `other_${field}`;
36+
if (formData.get(field) === "other") {
37+
formData.set(field, formData.get(otherField)!);
38+
formData.delete(otherField);
39+
}
40+
}
41+
42+
const formEntries = Object.fromEntries(formData.entries());
43+
console.debug(formEntries);
44+
45+
try {
46+
const res = await axios.post(APPLY_PATH, formData);
47+
if (res.status === 201) {
48+
console.log("Application submitted");
49+
// TODO: show submitted message
50+
}
51+
} catch (err) {
52+
console.error(err);
53+
if (axios.isAxiosError(err)) {
54+
if (err.response?.status === 401) {
55+
setSessionExpired(true);
56+
}
57+
}
58+
}
59+
60+
setSubmitting(false);
61+
};
62+
63+
const sessionExpiredMessage = (
64+
<p className="text-red-500 w-11/12">
65+
Your session has expired. Please{" "}
66+
<a
67+
href="/login"
68+
target="_blank"
69+
className="text-blue-600 underline"
70+
>
71+
log in from a new tab
72+
</a>{" "}
73+
to restore your session and then try submitting again.
74+
</p>
75+
);
76+
1277
return (
1378
<form
1479
method="post"
1580
className={`${styles.form} text-[#000000] w-8/12 flex flex-col items-center py-12 gap-10 z-1 max-[800px]:w-9/12 max-[400px]:w-11/12`}
1681
action="/api/user/apply"
1782
encType="multipart/form-data"
83+
onSubmit={handleSubmit}
1884
>
1985
<BasicInformation />
2086
<SchoolInformation />
2187
<ProfileInformation />
2288
<ResumeInformation />
2389
<AgeInformation />
24-
<Button text="Submit Application" />
90+
<Button text="Submit Application" disabled={submitting} />
91+
{sessionExpired && sessionExpiredMessage}
2592
</form>
2693
);
2794
}

apps/site/src/lib/components/Button/Button.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ interface ButtonProps {
88
href?: string;
99
isLightVersion?: boolean;
1010
usePrefetch?: boolean;
11+
disabled?: boolean;
1112
}
1213

1314
const Button: React.FC<ButtonProps> = ({
1415
text,
1516
href,
1617
className,
1718
isLightVersion,
19+
disabled,
1820
}) => {
1921
if (href) {
2022
return (
@@ -32,7 +34,11 @@ const Button: React.FC<ButtonProps> = ({
3234
);
3335
}
3436
return (
35-
<button type="submit" className={styles.button + " font-body"}>
37+
<button
38+
type="submit"
39+
className={styles.button + " font-body"}
40+
disabled={disabled}
41+
>
3642
{text}
3743
</button>
3844
);

0 commit comments

Comments
 (0)