Skip to content

Commit

Permalink
Add preliminary Apply page and application form (#63)
Browse files Browse the repository at this point in the history
* small initial setup for apply page

* basic information and title added on form

* initial draft of form and fishes

* password work, responsive design formatting

* changed single select, font, and margins

* added key props

* border radius, text align, view issues

* font size changes, created other on dropdown

* come style and formatting changes

* Prettier reformatting

* various changes to address issues raised

* fix: rename dropdownSelect to DropdownSelect

* componentized code, delted email / password

* prettier styling changes

* fix: autofocus on text field when clicking Other

* fix: add displayName to forwardRef

* fixes for a couple of issues

* change to direct conjunction

* http to https placeholder

* updated imports

* changed == to ===

* logistics form changes

* typo changes

---------

Co-authored-by: Sam Der <[email protected]>
  • Loading branch information
Bl20052005 and samderanova authored Dec 16, 2023
1 parent 7f63eb8 commit ee174c3
Show file tree
Hide file tree
Showing 17 changed files with 699 additions and 0 deletions.
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>
);
}
71 changes: 71 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,71 @@
"use client";

import { useState } from "react";
import RequiredAsterisk from "./RequiredAsterisk";

interface SelectProps {
labelStyle: string;
inputStyle: string;
name: string;
labelText: string;
values: Array<{ value: string; text: string }>;
containerClass: string;
}

interface OtherProps {
value: string;
name: string;
}

const OtherPopup = ({ value, name }: OtherProps) => {
if (value == "other") {
return (
<div className="mt-2 flex gap-2">
<label htmlFor={`${name}-other-input`} className="text-lg">
Other: <RequiredAsterisk />
</label>
<input
type="text"
name={`${name}-other-input`}
id={`${name}-other-input`}
className="border-b-2 p-1 h-6 border-black w-6/12"
required
/>
</div>
);
}
};

export default function DropdownSelect({
labelStyle,
inputStyle,
name,
labelText,
values,
containerClass,
}: SelectProps) {
const [value, setValue] = useState("");

return (
<div className={containerClass}>
<label className={`${labelStyle}`} htmlFor={name}>
{labelText} <RequiredAsterisk />
</label>
<select
className={`${inputStyle}`}
name={name}
id={name}
onChange={(e) => setValue(e.target.value)}
>
{values.map((item, i) => {
return (
<option key={`option-${i}`} value={item.value}>
{item.text}
</option>
);
})}
</select>
<OtherPopup value={value} name={name} />
</div>
);
}
105 changes: 105 additions & 0 deletions apps/site/src/app/apply/sections/Components/RadioSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"use client";

import { useState, useEffect, useRef, forwardRef } from "react";

interface RadioInputs {
name: string;
labelText: string;
IdentifierId: string;
values: Array<{ value: string; text: string }>;
containerClass: string;
}

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

const OtherInput = forwardRef<HTMLInputElement, IsChecked>(
({ isChecked, id }, ref) => (
<input
ref={ref}
type="text"
name={`${id}-other`}
className={
isChecked
? "border-b-2 p-1 h-6 border-black w-6/12"
: "border-b-2 p-1 h-6 border-black w-6/12 bg-transparent"
}
required={isChecked}
disabled={!isChecked}
/>
),
);
OtherInput.displayName = "OtherInput";

export default function RadioSelect({
name,
labelText,
IdentifierId,
values,
containerClass,
}: RadioInputs) {
const [isOtherChecked, setIsOtherChecked] = useState(false);
const otherRef = useRef<HTMLInputElement>(null);

useEffect(() => {
if (isOtherChecked) {
otherRef.current?.focus();
}
}, [isOtherChecked]);

return (
<div className={containerClass}>
<p className="m-0 text-lg mb-4">
{labelText} <span className="text-[#FF2222]">*</span>
</p>
<div className="w-10/12 flex flex-col gap-2">
{values.map((item, i) => {
const keyandId = `${IdentifierId}-${i}`;
if (item.value === "other") {
return (
<div key={keyandId} className="flex gap-2">
<input
id={keyandId}
type="radio"
key={`option-${i}`}
name={name}
value={item.value}
onChange={(e) =>
setIsOtherChecked(e.target.checked)
}
required
/>
<label className="text-lg" htmlFor={keyandId}>
{item.text}
</label>
<OtherInput
isChecked={isOtherChecked}
id={IdentifierId}
ref={otherRef}
/>
</div>
);
}
return (
<div key={keyandId} className="flex gap-2">
<input
id={keyandId}
type="radio"
key={`option-${i}`}
name={name}
value={item.value}
onChange={() => setIsOtherChecked(false)}
required
/>
<label className="text-lg" htmlFor={keyandId}>
{item.text}
</label>
</div>
);
})}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function RequiredAsterisk() {
return <span className="text-[#FF2222]">*</span>;
}
59 changes: 59 additions & 0 deletions apps/site/src/app/apply/sections/Components/SimpleRadio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import RequiredAsterisk from "./RequiredAsterisk";

interface TextfieldProps {
name: string;
values: Array<{
id: string;
labelText: string;
inputValue: string;
}>;
title: string;
titleClass: string;
containerClassTotal: string;
containerClassInputLabels: string;
containerClassValues: string;
labelClass: string;
isRequired: boolean;
}

export default function SimpleRadio({
name,
values,
title,
titleClass,
containerClassTotal,
containerClassInputLabels,
containerClassValues,
labelClass,
isRequired,
}: TextfieldProps) {
return (
<div className={containerClassTotal}>
<p className={titleClass}>
{`${title} `}
{isRequired && <RequiredAsterisk />}
</p>
<div className={containerClassValues}>
{values.map((value, i) => {
return (
<div
key={`${name}-${i}`}
className={containerClassInputLabels}
>
<input
type="radio"
id={value.id}
name={name}
value={value.inputValue}
required={isRequired}
/>
<label htmlFor={value.id} className={labelClass}>
{value.labelText}
</label>
</div>
);
})}
</div>
</div>
);
}
39 changes: 39 additions & 0 deletions apps/site/src/app/apply/sections/Components/TextInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import RequiredAsterisk from "./RequiredAsterisk";

interface TextProps {
name: string;
labelClass: string;
labelText: string;
inputClass: string;
containerClass: string;
type: string;
placeholder: string;
isRequired: boolean;
}

export default function TextInput({
name,
labelClass,
labelText,
inputClass,
containerClass,
placeholder,
type,
isRequired,
}: TextProps) {
return (
<div className={containerClass}>
<label className={labelClass} htmlFor={name}>
{`${labelText} `} {isRequired && <RequiredAsterisk />}
</label>
<input
className={inputClass}
type={type}
name={name}
id={name}
required={isRequired}
placeholder={placeholder}
/>
</div>
);
}
36 changes: 36 additions & 0 deletions apps/site/src/app/apply/sections/Components/Textfield.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import RequiredAsterisk from "./RequiredAsterisk";

interface TextfieldProps {
name: string;
labelClass: string;
labelText: string;
inputClass: string;
containerClass: string;
isRequired: boolean;
}

export default function Textfield({
name,
labelClass,
labelText,
inputClass,
containerClass,
isRequired,
}: TextfieldProps) {
return (
<div className={containerClass}>
<div className="flex flex-col w-full">
<label className={labelClass} htmlFor={name}>
{`${labelText} `}
{isRequired && <RequiredAsterisk />}
</label>
<textarea
className={inputClass}
id={name}
name={name}
required={isRequired}
/>
</div>
</div>
);
}
45 changes: 45 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,45 @@
import SimpleRadio from "@/app/apply/sections/Components/SimpleRadio";

const yesNoOptions = [
{
id: "minor-yes",
labelText: "Yes",
inputValue: "Yes",
},
{
id: "minor-no",
labelText: "No",
inputValue: "No",
},
];

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>

<SimpleRadio
name="minor-check"
values={yesNoOptions}
title="Will you be 18 years or older by January 26th, 2024?"
titleClass="text-xl font-bold m-0 text-center"
containerClassTotal="flex flex-col gap-1 w-full items-center"
isRequired={true}
labelClass="font-bold text-xl"
containerClassInputLabels="flex gap-2 items-center"
containerClassValues="flex gap-5"
/>
</div>
);
}
Loading

0 comments on commit ee174c3

Please sign in to comment.