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

feat: add course to attendee #579

Merged
merged 11 commits into from
Nov 29, 2023
12 changes: 3 additions & 9 deletions components/Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ export function InputBase({
let textColor = `text-${fgColor}`;
let backColor = `bg-${bgColor}`;

if (enabled === false) {
textColor = "text-gray-500";
backColor = "bg-gray-100";
} else if (enabled === true) {
textColor = `bg-${fgColor}`;
backColor = `bg-${bgColor}`;
}

return (
<div>
<label
Expand All @@ -41,7 +33,9 @@ export function InputBase({
{text}
</label>
<div
className={`text-iregular mt-2 flex items-center ${textColor} ${backColor} appearance-none rounded-full border border-gray-300 px-3 py-2 pl-6 placeholder-gray-400 shadow-sm sm:text-sm`}
className={`text-iregular mt-2 flex items-center ${
enabled == false ? "text-gray-500" : textColor
} ${backColor} appearance-none rounded-full border border-gray-300 px-3 py-2 pl-6 placeholder-gray-400 shadow-sm sm:text-sm`}
>
{children}
</div>
Expand Down
65 changes: 65 additions & 0 deletions components/Select/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { SelectHTMLAttributes } from "react";

import { ChevronDownIcon } from "@heroicons/react/solid";

interface Option {
key: any;
name: string;
}

interface Props extends SelectHTMLAttributes<HTMLSelectElement> {
text: string;
options: Option[];
customStyle?: string;
fgColor: string;
bgColor: string;
enabled?: boolean;
}

export default function Select({
id,
text,
options,
enabled,
fgColor,
bgColor,
customStyle,
...rest
}: Props) {
let textColor = `text-${fgColor}`;
let backColor = `bg-${bgColor}`;
let disabled = enabled == false;

return (
<div>
<label
htmlFor={id}
className={`pl-6 font-iregular text-${fgColor} mt-5 block text-sm`}
>
{text}
</label>
<div
className={`text-iregular relative mt-2 flex ${textColor} ${backColor} block w-full appearance-none rounded-full border border-gray-300 py-2 placeholder-gray-400 shadow-sm focus:outline-none sm:text-sm`}
>
<select
id={id}
disabled={disabled}
className={`text-iregular ${
disabled ? "text-gray-500" : textColor
} ${backColor} block w-full appearance-none rounded-full px-3 pr-10 pl-6 placeholder-gray-400 opacity-100 focus:outline-none sm:text-sm`}
{...rest}
>
{options.map((option) => (
<option key={option.key} value={option.key}>{option.name}</option>
))}
</select>
<ChevronDownIcon
className={`${
!disabled ? "block" : "hidden"
} pointer-events-none absolute top-0 bottom-0 right-3 m-auto h-7 text-${fgColor}`}
aria-hidden="true"
/>
</div>
</div>
);
}
31 changes: 27 additions & 4 deletions layout/Attendee/Profile/Profile.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useState } from "react";
import { useState, useEffect } from "react";

import { withAuth, useAuth } from "@context/Auth";

import Form from "@components/Form";
import Input from "@components/Input";
import Select from "@components/Select";

import Layout from "@components/Layout";
import Button from "@components/Button";
Expand All @@ -14,11 +15,17 @@ import CVInput from "./components/CVInput";
import { resetPassword } from "@lib/api";
import { getFirstName } from "@lib/naming";

function Profile() {
interface Course {
id: any;
name: string;
}

function Profile({ courses }) {
const { user, editUser } = useAuth();
const [avatar, setAvatar] = useState(null);
const [editing, setEditing] = useState(false);
const [username, setUsername] = useState(user.nickname || "");
const [course, setCourse] = useState(user.course || "");

const [photoFileUrl, setPhotoFileUrl] = useState<string>(user.avatar);

Expand Down Expand Up @@ -61,6 +68,7 @@ function Profile() {
e.preventDefault();
const formData = new FormData();
formData.append("attendee[nickname]", username);
formData.append("attendee[course_id]", course);
formData.append("attendee[avatar]", avatar);

if (editing) {
Expand Down Expand Up @@ -96,6 +104,8 @@ function Profile() {
<Button
customStyle="w-full items-center rounded-full border border-quinary bg-quinary py-2 px-4 text-center font-iregular text-sm text-secondary shadow-sm"
title={editing ? "Save Changes" : "Edit"}
form="profile-form"
type="submit"
/>
</div>
</Heading>
Expand Down Expand Up @@ -135,7 +145,7 @@ function Profile() {
id="name"
name="name"
value={user.name || ""}
bgColor="white"
bgColor="primary"
fgColor="white"
enabled={false}
/>
Expand All @@ -144,11 +154,24 @@ function Profile() {
id="username"
name="username"
value={username}
bgColor="white"
bgColor="primary"
fgColor="white"
enabled={editing}
onChange={(e) => setUsername(e.currentTarget.value)}
/>
<Select
text="COURSE"
id="course"
bgColor="primary"
fgColor="white"
value={course}
options={courses.map((course) => ({
key: course.id,
name: course.name,
}))}
enabled={editing}
onChange={(e) => setCourse(e.currentTarget.value)}
/>

<button
className="inline-block h-auto pl-6 pb-5 text-quinary underline"
Expand Down
4 changes: 2 additions & 2 deletions layout/SignUp/SignUp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import { SignUpForm } from "./components";
import Title from "@layout/moonstone/authentication/Title";
import Text from "@layout/moonstone/authentication/Text";

function Signup() {
function Signup({ courses }) {
return (
<div className="min-h-screen overflow-hidden bg-secondary">
<Return componentStyle="sm:ml-14 mt-10 sm:mt-20 mb-6" />
<div className="flex flex-col items-center justify-center sm:mt-16">
<Title text="Sign up" />
<SignUpForm />
<SignUpForm courses={courses} />
<Text text="Already have an account?" link="Login here" href="/login" />
<div className="absolute bottom-0 right-60 hidden lg:block">
<Motion.div
Expand Down
22 changes: 21 additions & 1 deletion layout/SignUp/components/SignUpForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,23 @@ import { useAuth } from "@context/Auth";
import Button from "@components/Button";
import Form from "@components/Form";
import Input from "@components/Input";
import Select from "@components/Select";
import PasswordInput from "@components/PasswordInput";

import BarebonesQRScanner from "@components/QRScanner/BarebonesQRScanner";

export default function SignUpForm() {
interface Course {
id: any;
name: string;
}

export default function SignUpForm({ courses }) {
const { sign_up, isLoading, errors } = useAuth();

const [name, updateName] = useState("");
const [email, updateEmail] = useState("");
const [nickname, updateNickname] = useState("");
const [course, updateCourse] = useState("");
const [password, updatePassword] = useState("");
const [password_confirmation, updatePasswordConfirmation] = useState("");
const [uuid, setUUID] = useState();
Expand Down Expand Up @@ -60,6 +67,7 @@ export default function SignUpForm() {
password_confirmation,
nickname,
uuid,
course,
});
}
};
Expand Down Expand Up @@ -93,6 +101,18 @@ export default function SignUpForm() {
bgColor="primary"
onChange={(e) => updateNickname(e.currentTarget.value)}
/>
<Select
text="COURSE"
id="course"
fgColor="white"
bgColor="primary"
defaultValue={0}
options={courses.map((course) => ({
key: course.id,
name: course.name,
}))}
onChange={(e) => updateCourse(e.currentTarget.value)}
/>
<PasswordInput
text="PASSWORD"
id="password"
Expand Down
13 changes: 13 additions & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export async function sign_up({
name,
nickname,
uuid,
course,
}) {
const response = await API.post("/api/auth/sign_up", {
user: {
Expand All @@ -27,13 +28,25 @@ export async function sign_up({
id: uuid,
nickname: nickname,
name: name,
course_id: course,
},
},
});

return response.data;
}

/*
The courses returned by this API call are already ordered in the way
they should be displayed on all course listings, and the first entry
in the array represents the default value for a course.
*/
export async function getCourses() {
const response = await API.get("/api/attendee/courses");

return response.data;
}

export async function getLeaderboard(date) {
const response = await API.get(`/api/leaderboard/${date}`);

Expand Down
19 changes: 18 additions & 1 deletion pages/attendee/profile.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
export { default } from "@layout/Attendee/Profile";
import Profile from "@layout/Attendee/Profile";

import { getCourses } from "@lib/api";

export async function getServerSideProps(context) {
context.res.setHeader(
"Cache-Control",
"public, s-maxage=3600, stale-while-revalidate=3600"
);

const courses = await getCourses().then((response) =>
response.data.concat({ id: "", name: "None" })
);

return { props: { courses: courses } };
}

export default Profile;
34 changes: 32 additions & 2 deletions pages/register/[uuid].js
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { motion as Motion } from "framer-motion";

import { withoutAuth, useAuth } from "@context/Auth";

import { getCourses } from "@lib/api";

import Button from "@components/Button";
import Card from "@components/Card";
import Return from "@components/Return";
import Form from "@components/Form";
import Input from "@components/Input";
import Select from "@components/Select";
import PasswordInput from "@components/PasswordInput";

import Title from "@layout/moonstone/authentication/Title";
import Text from "@layout/moonstone/authentication/Text";

function Register() {
export async function getServerSideProps(context) {
context.res.setHeader(
"Cache-Control",
"public, s-maxage=3600, stale-while-revalidate=3600"
);

const courses = await getCourses().then((response) =>
response.data.concat({ id: "", name: "None" })
);

return { props: { courses: courses } };
}

function Register({ courses }) {
const { sign_up, errors, isLoading } = useAuth();
const router = useRouter();
const { uuid } = router.query;

const [name, updateName] = useState("");
const [email, updateEmail] = useState("");
const [nickname, updateNickname] = useState("");
const [course, updateCourse] = useState("0");
const [password, updatePassword] = useState("");
const [password_confirmation, updatePasswordConfirmation] = useState("");

Expand All @@ -34,6 +51,7 @@ function Register() {
password_confirmation,
nickname,
uuid,
course,
});
};

Expand Down Expand Up @@ -69,6 +87,18 @@ function Register() {
bgColor="primary"
onChange={(e) => updateNickname(e.currentTarget.value)}
/>
<Select
text="COURSE"
id="course"
fgColor="white"
bgColor="primary"
defaultValue={0}
options={courses.map((course) => ({
key: course.id,
name: course.name,
}))}
onChange={(e) => updateCourse(e.currentTarget.value)}
/>
<PasswordInput
text="PASSWORD"
id="confirm"
Expand Down
19 changes: 18 additions & 1 deletion pages/signup.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
export { default } from "@layout/SignUp";
import { getCourses } from "@lib/api";

import SignUp from "@layout/SignUp";

export async function getServerSideProps(context) {
context.res.setHeader(
"Cache-Control",
"public, s-maxage=3600, stale-while-revalidate=3600"
);

const courses = await getCourses().then((response) =>
response.data.concat({ id: "", name: "None" })
);

return { props: { courses: courses } };
}

export default SignUp;
Loading