Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add preliminary Apply page and application form #63

Merged
merged 23 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions apps/site/src/app/apply/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Form from "./sections/Form/Form";
import Title from "./sections/Title/Title";

export const revalidate = 60;

export default function Apply() {
return (
<div className="flex flex-col items-center gap-10 my-32">
<Title />
<Form />
</div>
)
}
16 changes: 16 additions & 0 deletions apps/site/src/app/apply/sections/Components/PasswordEye.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Eye } from "lucide-react";
import { EyeOff } from "lucide-react";
tyleryy marked this conversation as resolved.
Show resolved Hide resolved

interface PasswordProps {
tyleryy marked this conversation as resolved.
Show resolved Hide resolved
visible: boolean,
handler: any
tyleryy marked this conversation as resolved.
Show resolved Hide resolved
}


export default function PasswordEye(props: PasswordProps) {
tyleryy marked this conversation as resolved.
Show resolved Hide resolved
if(props.visible) {
return <EyeOff className="cursor-pointer" onClick={props.handler}/>
} else {
return <Eye className="cursor-pointer" onClick={props.handler}/>
}
}
73 changes: 73 additions & 0 deletions apps/site/src/app/apply/sections/Components/SingleSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"use client"

import { useState } from 'react';

interface SelectProps {
name: string;
labelText: string;
IdentifierID: string;
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
values: Array<any>;
samderanova marked this conversation as resolved.
Show resolved Hide resolved
}

interface IsChecked {
isChecked: boolean;
id: string;
}

const OtherInput = (props: IsChecked) => {
if(props.isChecked) {
return (
<input type="text" name={`${props.id}-other`} className="border-b-2 p-1 h-6 border-black w-6/12" required/>
)
} else {
return (
<input type="text" name={`${props.id}-other`} className="border-b-2 p-1 h-6 border-black w-6/12 bg-transparent" disabled={true}/>
)
}
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
}



export default function SingleSelect(props: SelectProps) {
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
const [isOtherChecked, setIsOtherChecked] = useState(false);

// I'm not entirely sure whether we would like to erase the input on
// other when it's checked off, so I'll leave that implementation
// out for now
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
return(
<>
<p className="m-0 text-lg mb-4">{props.labelText} <span className="text-[#FF2222]">*</span></p>
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
<div className="w-10/12 flex flex-col gap-2">
{props.values.map((item, i) => {
if(item.value == "other") {
return(
<div key={`${props.IdentifierID}-${i}`} className="flex gap-2">
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
<input id={`${props.IdentifierID}-${i}`}
type="radio"
key={`option-${i}`}
name={props.name}
value={item.value}
onChange={(e) => setIsOtherChecked(e.target.checked)}
required/>
<label className="text-lg" htmlFor={`${props.IdentifierID}-${i}`}>{item.text}</label>
<OtherInput isChecked={isOtherChecked} id={props.IdentifierID} />
</div>
)
}
return(
<div key={`${props.IdentifierID}-${i}`} className="flex gap-2">
<input id={`${props.IdentifierID}-${i}`}
type="radio"
key={`option-${i}`}
name={props.name}
value={item.value}
onChange={() => setIsOtherChecked(false)}
required/>
<label className="text-lg" htmlFor={`${props.IdentifierID}-${i}`}>{item.text}</label>
</div>
)
})}
</div>
</>
)
}
45 changes: 45 additions & 0 deletions apps/site/src/app/apply/sections/Components/dropdownSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"use client"
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved

import { useState } from 'react';

interface SelectProps {
labelStyle: string;
inputStyle: string;
name: string;
labelText: string;
values: Array<any>;
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
}
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved

interface OtherProps {
value: string
name: string
}

const OtherPopup = (props: OtherProps) => {
if(props.value == 'other') {
return(
<div className='mt-2 flex gap-2'>
<label htmlFor={`${props.name}-other-input`} className='text-lg'>Other: <span className="text-[#FF2222]">*</span></label>
<input type="text" name={`${props.name}-other-input`} className="border-b-2 p-1 h-6 border-black w-6/12" required/>
</div>
)
}
}

export default function DropdownSelect(props: SelectProps) {
const [value, setValue] = useState('');

return(
<>
<label className={`${props.labelStyle}`} htmlFor={props.name}>{props.labelText} <span className="text-[#FF2222]">*</span></label>
<select className={`${props.inputStyle}`} name={props.name} onChange={(e) => setValue(e.target.value)}>
{props.values.map((item, i) => {
return(
<option key={`option-${i}`} value={item.value}>{item.text}</option>
)
})}
</select>
<OtherPopup value={value} name={props.name}/>
</>
)
}
24 changes: 24 additions & 0 deletions apps/site/src/app/apply/sections/Form/AgeInformation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export default function AgeInformation() {
return(
<div className="flex flex-col gap-5 w-11/12">
<div className="flex flex-col gap-5">
<p className="m-0 text-lg">Because of limitations imposed by UCI, we are legally not allowed to host minors (those under 18) for IrvineHacks 2024. By answering yes, you affirm that you are and will be 18 years or older by January 26, 2024.</p>
<p className="text-[#FF2222] m-0 text-lg">We will be checking ID. If you are a minor, you will be turned away at the door.</p>
</div>

<div className="flex flex-col gap-1 w-full items-center">
<p className="text-xl font-bold m-0">Will you be 18 years or older by January 26th, 2024? <span className="text-[#FF2222]">*</span></p>
<div className="flex gap-5">
<div className="flex gap-2 items-center">
<input type="radio" id="minor-yes" name="minor-check" value="Yes" defaultChecked={true} required/>
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
<label htmlFor="minor-yes" className="font-bold text-xl">Yes</label>
</div>
<div className="flex gap-2 items-center">
<input type="radio" id="minor-no" name="minor-check" value="No" required/>
<label htmlFor="minor-no" className="font-bold text-xl">No</label>
</div>
</div>
</div>
</div>
)
}
110 changes: 110 additions & 0 deletions apps/site/src/app/apply/sections/Form/BasicInformation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"use client";

import { useState } from 'react';
import SingleSelect from "../Components/SingleSelect";
import PasswordEye from "../Components/PasswordEye";
import styles from "./Form.module.scss";

//I'm not sure if we want to have different submission values
//than display texts for the inputs, so I seperated both
//for good measure

const pronouns = [
{value: "she", text: "She/her/hers"},
{value: "he", text: "He/him/his"},
{value: "they", text: "They/them/theirs"},
{value: "ze", text: "Ze/zir/zirs"},
{value: "other", text: "Other:"},
];

const ethnicity = [
{value: "American", text: "American Indian or Alaskan"},
{value: "Asian", text: "Asian or Pacific Islander"},
{value: "Black", text: "Black or African American"},
{value: "Hispanic", text: "Hispanic"},
{value: "White", text: "White or Caucasian"},
{value: "Two-or-more", text: "Two or more races"},
{value: "Prefer not to answer", text: "Prefer not to answer"},
{value: "other", text: "Other:"}
]

export default function BasicInformation() {
//the password state here is supposed to be used for further validation when we
//check confirm password, but I'm pretty sure that will have to wait until we
//implement the submission process, so I just leave it here for now
const [password, setPassword] = useState("");
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved

const [passwordVisible, setPasswordVisible] = useState(false);
const [confirmPasswordVisible, setConfirmPasswordVisible] = useState(false);

const handleChangePassword = () => {
setPasswordVisible(!passwordVisible)
}

const handleChangeConfirmPassword = () => {
setConfirmPasswordVisible(!confirmPasswordVisible)
}
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved

return(
<div className="flex flex-col gap-5 w-11/12">
<p className="text-4xl m-0 font-bold text-center max-[700px]:text-3xl">Basic Information</p>

<div className="flex gap-5 w-full max-[1000px]:flex-col max-[1000px]:items-center">
<div className="flex flex-col w-6/12 max-[1000px]:w-full">
<label className={`${styles.label}`} htmlFor="first-name">First Name <span className="text-[#FF2222]">*</span></label>
<input className={`${styles.input}`} type="text" name="first-name" required/>
Bl20052005 marked this conversation as resolved.
Show resolved Hide resolved
</div>
<div className="flex flex-col w-6/12 max-[1000px]:w-full">
<label className={`${styles.label}`} htmlFor="last-name">Last Name <span className="text-[#FF2222]">*</span></label>
<input className={`${styles.input}`} type="text" name="last-name" required/>
</div>
</div>

<div className="flex gap-5 w-full max-[1000px]:flex-col max-[1000px]:items-center">
<div className="flex flex-col w-full">
<label className={`${styles.label}`} htmlFor="email">Email <span className="text-[#FF2222]">*</span></label>
<input className={`${styles.input}`} type="email" name="email" required/>
</div>
</div>

<div className="flex gap-5 w-full max-[1000px]:flex-col max-[1000px]:items-center">
<div className="flex flex-col w-6/12 max-[1000px]:w-full">
<label className={`${styles.label}`} htmlFor="password">Password <span className="text-[#FF2222]">*</span></label>
<div className="w-full flex items-center gap-3">
<input
className={`${styles.input} w-full`}
type={passwordVisible ? "text" : "password"}
name="password"
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"
tyleryy marked this conversation as resolved.
Show resolved Hide resolved
value={password}
onChange={(e) => setPassword(e.target.value)}
required
title="One number, uppercase, and lowercase letter; at least 8 or more characters"/>
<PasswordEye visible={passwordVisible} handler={handleChangePassword}/>
</div>
<small className="text-[#676767] m-0 text-sm">At least 8 characters with one number, one uppercase, and one lowercase letter</small>
</div>
<div className="flex flex-col w-6/12 max-[1000px]:w-full">
<label className={`${styles.label}`} htmlFor="confirm-password">Confirm Password <span className="text-[#FF2222]">*</span></label>
<div className="w-full flex items-center gap-3">
<input
className={`${styles.input} w-full`}
type={confirmPasswordVisible ? "text" : "password"}
name="confirm-password"
required />
<PasswordEye visible={confirmPasswordVisible} handler={handleChangeConfirmPassword}/>
</div>
</div>
</div>

<div className="flex gap-5 w-full max-[1000px]:flex-col max-[1000px]:items-center">
<div className="flex flex-col w-6/12 max-[1000px]:w-full">
<SingleSelect IdentifierID="gender-identifier" name="gender" labelText="Gender" values={pronouns} />
</div>
<div className="flex flex-col w-6/12 max-[1000px]:w-full">
<SingleSelect IdentifierID="ethnicity-identifier" name="ethnicity" labelText="Race / Ethnicity" values={ethnicity} />
</div>
</div>
</div>
)
}
21 changes: 21 additions & 0 deletions apps/site/src/app/apply/sections/Form/Form.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.form {
background-color: #FFFFFF;
border-radius: 10px;
}

.input {
background-color: #E1E1E1;
height: 38px;
padding: 5px;
border-radius: 4px;
font-size: 18px;
}

.label {
font-size: 18px;
margin-bottom: 8px;
}

.image {
z-index: -1;
}
51 changes: 51 additions & 0 deletions apps/site/src/app/apply/sections/Form/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Image from "next/image";
import BasicInformation from "./BasicInformation";
import AgeInformation from "./AgeInformation";
import SchoolInformation from "./SchoolInformation";
import ProfileInformation from "./ProfileInformation";
import Button from "@/lib/components/Button/Button";
import koiLeft from "@/assets/images/koi-swim-left.png";
import koiRight from "@/assets/images/koi-swim-right.png";
import styles from "./Form.module.scss";

export default function Form() {
return(
<div className="relative w-full flex flex-col items-center">
<Image
src={koiLeft}
height="250"
alt="Koi fish"
className={`${styles.image} absolute top-0 right-0`}
/>
<Image
src={koiRight}
height="250"
alt="Koi fish"
className={`${styles.image} absolute top-1/4 left-0`}
/>
<Image
src={koiLeft}
height="250"
alt="Koi fish"
className={`${styles.image} absolute top-1/2 right-0`}
/>
<Image
src={koiRight}
height="250"
alt="Koi fish"
className={`${styles.image} absolute top-3/4 left-0`}
/>
<form 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`}>
<BasicInformation />
<SchoolInformation />
<ProfileInformation />
<AgeInformation />
<Button
className=""
text="Submit Application"
/>
</form>
</div>

)
}
Loading
Loading