Skip to content

Commit

Permalink
Merge pull request #143 from FS-FAST-TRACK/feature/quizmaster-fronten…
Browse files Browse the repository at this point in the history
…d-quiz-room

[feat] Added the quiz room pages.
  • Loading branch information
JMark-FS authored Jan 12, 2024
2 parents 89296c3 + 0076582 commit 50d45d9
Show file tree
Hide file tree
Showing 14 changed files with 943 additions and 92 deletions.
125 changes: 65 additions & 60 deletions WebApp/frontend/quiz-master/api/api-routes.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,91 @@
//#region Account API endpoints
const QUIZMASTER_ACCOUNT=`${process.env.QUIZMASTER_GATEWAY}/gateway/api/account`
const QUIZMASTER_ACCOUNT_GET=`${QUIZMASTER_ACCOUNT}`
const QUIZMASTER_ACCOUNT_POST=`${QUIZMASTER_ACCOUNT}/create`
const QUIZMASTER_ACCOUNT_POST_PARTIAL=`${QUIZMASTER_ACCOUNT}/create_partial`
const QUIZMASTER_ACCOUNT_DELETE=`${QUIZMASTER_ACCOUNT}/delete/`
const QUIZMASTER_ACCOUNT_PATCH=`${QUIZMASTER_ACCOUNT}/update/`
const QUIZMASTER_ACCOUNT_PASSWORD_RESET_POST= (id: Number) => {return `${QUIZMASTER_ACCOUNT}/${id}/update_password`}
const QUIZMASTER_ACCOUNT_POST_SET_ADMIN=`${QUIZMASTER_ACCOUNT}/set_admin/`
const QUIZMASTER_ACCOUNT = `${process.env.QUIZMASTER_GATEWAY}/gateway/api/account`;
const QUIZMASTER_ACCOUNT_GET = `${QUIZMASTER_ACCOUNT}`;
const QUIZMASTER_ACCOUNT_POST = `${QUIZMASTER_ACCOUNT}/create`;
const QUIZMASTER_ACCOUNT_POST_PARTIAL = `${QUIZMASTER_ACCOUNT}/create_partial`;
const QUIZMASTER_ACCOUNT_DELETE = `${QUIZMASTER_ACCOUNT}/delete/`;
const QUIZMASTER_ACCOUNT_PATCH = `${QUIZMASTER_ACCOUNT}/update/`;
const QUIZMASTER_ACCOUNT_PASSWORD_RESET_POST = (id: Number) => {
return `${QUIZMASTER_ACCOUNT}/${id}/update_password`;
};
const QUIZMASTER_ACCOUNT_POST_SET_ADMIN = `${QUIZMASTER_ACCOUNT}/set_admin/`;
//#endregion

//#region Authentication API endpoints
const QUIZMASTER_AUTH=`${process.env.QUIZMASTER_GATEWAY}/gateway/api/auth`
const QUIZMASTER_AUTH_POST_LOGIN=`${QUIZMASTER_AUTH}/login`
const QUIZMASTER_AUTH_POST_PARTIAL_LOGIN=`${QUIZMASTER_AUTH}/partialLogin`
const QUIZMASTER_AUTH_POST_PARTIAL_LOGOUT=`${QUIZMASTER_AUTH}/logout`
const QUIZMASTER_AUTH_GET_COOKIE_INFO=`${QUIZMASTER_AUTH}/info`
const QUIZMASTER_AUTH_POST_SET_ADMIN=`${QUIZMASTER_AUTH}/set_admin/`
const QUIZMASTER_AUTH = `${process.env.QUIZMASTER_GATEWAY}/gateway/api/auth`;
const QUIZMASTER_AUTH_POST_LOGIN = `${QUIZMASTER_AUTH}/login`;
const QUIZMASTER_AUTH_POST_PARTIAL_LOGIN = `${QUIZMASTER_AUTH}/partialLogin`;
const QUIZMASTER_AUTH_POST_PARTIAL_LOGOUT = `${QUIZMASTER_AUTH}/logout`;
const QUIZMASTER_AUTH_GET_COOKIE_INFO = `${QUIZMASTER_AUTH}/info`;
const QUIZMASTER_AUTH_POST_SET_ADMIN = `${QUIZMASTER_AUTH}/set_admin/`;
//#endregion

//#region Media API endpoints
const QUIZMASTER_MEDIA=`${process.env.QUIZMASTER_GATEWAY}/gateway/api/media`
const QUIZMASTER_MEDIA_POST=`${QUIZMASTER_MEDIA}/upload`
const QUIZMASTER_MEDIA_GET_MEDIAS=`${QUIZMASTER_MEDIA}/get_all_media`
const QUIZMASTER_MEDIA_GET_MEDIA=`${QUIZMASTER_MEDIA}/get_media/`
const QUIZMASTER_MEDIA_GET_DOWNLOAD=`${QUIZMASTER_MEDIA}/download_media/`
const QUIZMASTER_MEDIA_DELETE=`${QUIZMASTER_MEDIA}/delete_media/`
//#region Media API endpoints
const QUIZMASTER_MEDIA = `${process.env.QUIZMASTER_GATEWAY}/gateway/api/media`;
const QUIZMASTER_MEDIA_POST = `${QUIZMASTER_MEDIA}/upload`;
const QUIZMASTER_MEDIA_GET_MEDIAS = `${QUIZMASTER_MEDIA}/get_all_media`;
const QUIZMASTER_MEDIA_GET_MEDIA = `${QUIZMASTER_MEDIA}/get_media/`;
const QUIZMASTER_MEDIA_GET_DOWNLOAD = `${QUIZMASTER_MEDIA}/download_media/`;
const QUIZMASTER_MEDIA_DELETE = `${QUIZMASTER_MEDIA}/delete_media/`;
//#endregion

//#region Question API endpoints
const QUIZMASTER_QUESTION=`${process.env.QUIZMASTER_GATEWAY}/api/gateway/question`
const QUIZMASTER_QUESTION_GET_QUESTIONS=`${QUIZMASTER_QUESTION}/get_questions`
const QUIZMASTER_QUESTION_GET_QUESTION=`${QUIZMASTER_QUESTION}/get_question/`
const QUIZMASTER_QUESTION_POST=`${QUIZMASTER_QUESTION}/add_question`
const QUIZMASTER_QUESTION_DELETE=`${QUIZMASTER_QUESTION}/delete_question/`
const QUIZMASTER_QUESTION_PATCH=`${QUIZMASTER_QUESTION}/update_qustion/`
const QUIZMASTER_QUESTION = `${process.env.QUIZMASTER_GATEWAY}/api/gateway/question`;
const QUIZMASTER_QUESTION_GET_QUESTIONS = `${QUIZMASTER_QUESTION}/get_questions`;
const QUIZMASTER_QUESTION_GET_QUESTION = `${QUIZMASTER_QUESTION}/get_question/`;
const QUIZMASTER_QUESTION_POST = `${QUIZMASTER_QUESTION}/add_question`;
const QUIZMASTER_QUESTION_DELETE = `${QUIZMASTER_QUESTION}/delete_question/`;
const QUIZMASTER_QUESTION_PATCH = `${QUIZMASTER_QUESTION}/update_qustion/`;
//#endregion

//#region Difficulty API endpoints
const QUIZMASTER_QDIFFICULTY=`${process.env.QUIZMASTER_GATEWAY}/api/gateway/question/difficulty`
const QUIZMASTER_QDIFFICULTY_GET_DIFFICULTIES=`${QUIZMASTER_QDIFFICULTY}/get_difficulties`
const QUIZMASTER_QDIFFICULTY_GET_DIFFICULTY=`${QUIZMASTER_QDIFFICULTY}/get_difficulty/`
const QUIZMASTER_QDIFFICULTY_POST=`${QUIZMASTER_QDIFFICULTY}/add_difficulty`
const QUIZMASTER_QDIFFICULTY_DELETE=`${QUIZMASTER_QDIFFICULTY}/delete_difficulty/`
const QUIZMASTER_QDIFFICULTY_PATCH=`${QUIZMASTER_QDIFFICULTY}/update_difficulty/`
//#region Difficulty API endpoints
const QUIZMASTER_QDIFFICULTY = `${process.env.QUIZMASTER_GATEWAY}/api/gateway/question/difficulty`;
const QUIZMASTER_QDIFFICULTY_GET_DIFFICULTIES = `${QUIZMASTER_QDIFFICULTY}/get_difficulties`;
const QUIZMASTER_QDIFFICULTY_GET_DIFFICULTY = `${QUIZMASTER_QDIFFICULTY}/get_difficulty/`;
const QUIZMASTER_QDIFFICULTY_POST = `${QUIZMASTER_QDIFFICULTY}/add_difficulty`;
const QUIZMASTER_QDIFFICULTY_DELETE = `${QUIZMASTER_QDIFFICULTY}/delete_difficulty/`;
const QUIZMASTER_QDIFFICULTY_PATCH = `${QUIZMASTER_QDIFFICULTY}/update_difficulty/`;
//#endregion

//#region Question Type API endpoints
const QUIZMASTER_QTYPE=`${process.env.QUIZMASTER_GATEWAY}/api/gateway/question/type`
const QUIZMASTER_QTYPE_GET_TYPES=`${QUIZMASTER_QTYPE}/get_all_type`
const QUIZMASTER_QTYPE_GET_TYPE=`${QUIZMASTER_QTYPE}/get_type/`
const QUIZMASTER_QTYPE = `${process.env.QUIZMASTER_GATEWAY}/api/gateway/question/type`;
const QUIZMASTER_QTYPE_GET_TYPES = `${QUIZMASTER_QTYPE}/get_all_type`;
const QUIZMASTER_QTYPE_GET_TYPE = `${QUIZMASTER_QTYPE}/get_type/`;
//#endregion

//#region Question Category API endpoints
const QUIZMASTER_QCATEGORY=`${process.env.QUIZMASTER_GATEWAY}/gateway/api/question/category`
const QUIZMASTER_QCATEGORY_GET_CATEGORIES=`${QUIZMASTER_QCATEGORY}/get_all_category`
const QUIZMASTER_QCATEGORY_GET_CATEGORY=`${QUIZMASTER_QCATEGORY}/get_category/`
const QUIZMASTER_QCATEGORY_POST=`${QUIZMASTER_QCATEGORY}/create_category`
const QUIZMASTER_QCATEGORY_DELETE=`${QUIZMASTER_QCATEGORY}/delete/`
const QUIZMASTER_QCATEGORY_PATCH=`${QUIZMASTER_QCATEGORY}/update_category/`
const QUIZMASTER_QCATEGORY = `${process.env.QUIZMASTER_GATEWAY}/gateway/api/question/category`;
const QUIZMASTER_QCATEGORY_GET_CATEGORIES = `${QUIZMASTER_QCATEGORY}/get_all_category`;
const QUIZMASTER_QCATEGORY_GET_CATEGORY = `${QUIZMASTER_QCATEGORY}/get_category/`;
const QUIZMASTER_QCATEGORY_POST = `${QUIZMASTER_QCATEGORY}/create_category`;
const QUIZMASTER_QCATEGORY_DELETE = `${QUIZMASTER_QCATEGORY}/delete/`;
const QUIZMASTER_QCATEGORY_PATCH = `${QUIZMASTER_QCATEGORY}/update_category/`;
//#endregion

//#region Question Detail API endpoints
const QUIZMASTER_QUESTIONDETAIL="question_detail"
const QUIZMASTER_QUESTIONDETAIL_GET_QUESTIONDETAILS=`${QUIZMASTER_QUESTIONDETAIL}/get_question_details`
const QUIZMASTER_QUESTIONDETAIL_GET_QUESTIONDETAIL=`${QUIZMASTER_QUESTIONDETAIL}/get_question_detail/`
const QUIZMASTER_QUESTIONDETAIL_POST=`${QUIZMASTER_QUESTIONDETAIL}/add_question_detail`
const QUIZMASTER_QUESTIONDETAIL_DELETE=`${QUIZMASTER_QUESTIONDETAIL}/delete_question_detail/`
const QUIZMASTER_QUESTIONDETAIL_PATCH=`${QUIZMASTER_QUESTIONDETAIL}/update_qustion_detail/`
const QUIZMASTER_QUESTIONDETAIL = "question_detail";
const QUIZMASTER_QUESTIONDETAIL_GET_QUESTIONDETAILS = `${QUIZMASTER_QUESTIONDETAIL}/get_question_details`;
const QUIZMASTER_QUESTIONDETAIL_GET_QUESTIONDETAIL = `${QUIZMASTER_QUESTIONDETAIL}/get_question_detail/`;
const QUIZMASTER_QUESTIONDETAIL_POST = `${QUIZMASTER_QUESTIONDETAIL}/add_question_detail`;
const QUIZMASTER_QUESTIONDETAIL_DELETE = `${QUIZMASTER_QUESTIONDETAIL}/delete_question_detail/`;
const QUIZMASTER_QUESTIONDETAIL_PATCH = `${QUIZMASTER_QUESTIONDETAIL}/update_qustion_detail/`;
//#endregion

//#region Set API endpoints
const QUIZMASTER_SET=`${process.env.QUIZMASTER_GATEWAY}/gateway/api/set`
const QUIZMASTER_SET_POST=`${QUIZMASTER_SET}/create`
const QUIZMASTER_SET_POST_SUBMIT_ANSWER=`${QUIZMASTER_SET}/submitAnswer`
const QUIZMASTER_SET_GET_SETS=`${QUIZMASTER_SET}/all_set`
const QUIZMASTER_SET_GET_SET=`${QUIZMASTER_SET}/`
const QUIZMASTER_SET_GET_SETQUESTIONS=`${QUIZMASTER_SET}/all_question_set`
const QUIZMASTER_SET_GET_SETQUESTION=`${QUIZMASTER_SET}/get_question_set/`
const QUIZMASTER_SET_PUT=`${QUIZMASTER_SET}/update_set/`
const QUIZMASTER_SET_DELETE=`${QUIZMASTER_SET}/delete_set/`
const QUIZMASTER_SET = `${process.env.QUIZMASTER_GATEWAY}/gateway/api/set`;
const QUIZMASTER_SET_POST = `${QUIZMASTER_SET}/create`;
const QUIZMASTER_SET_POST_SUBMIT_ANSWER = `${QUIZMASTER_SET}/submitAnswer`;
const QUIZMASTER_SET_GET_SETS = `${QUIZMASTER_SET}/all_set`;
const QUIZMASTER_SET_GET_SET = `${QUIZMASTER_SET}/`;
const QUIZMASTER_SET_GET_SETQUESTIONS = `${QUIZMASTER_SET}/all_question_set`;
const QUIZMASTER_SET_GET_SETQUESTION = `${QUIZMASTER_SET}/get_question_set/`;
const QUIZMASTER_SET_PUT = `${QUIZMASTER_SET}/update_set/`;
const QUIZMASTER_SET_DELETE = `${QUIZMASTER_SET}/delete_set/`;
//#endregion


// #region Hub
const QUIZMASTER_GATEWAY_SESSION_HUB = `${process.env.QUIZMASTER_GATEWAY}/gateway/hub/session`;
//#endregion
export {
QUIZMASTER_ACCOUNT_GET,
QUIZMASTER_ACCOUNT_POST,
Expand Down Expand Up @@ -131,4 +135,5 @@ export {
QUIZMASTER_SET_POST_SUBMIT_ANSWER,
QUIZMASTER_SET_PUT,
QUIZMASTER_SET_DELETE,
};
QUIZMASTER_GATEWAY_SESSION_HUB,
};
3 changes: 2 additions & 1 deletion WebApp/frontend/quiz-master/app/auth/[authPage]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export default function Page({
const searchParams = useSearchParams();

const authPage = params.authPage || "login";
const callbackUrl = searchParams.get("callbackUrl") || "/dashboard";
const callbackUrl =
searchParams.get("callbackUrl") || "http://localhost:3000/dashboard";
const { status } = useSession();

if (status === "loading") {
Expand Down
229 changes: 229 additions & 0 deletions WebApp/frontend/quiz-master/app/quiz-rooms/create-quiz-room/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
"use client";

import {
Anchor,
Breadcrumbs,
Button,
Checkbox,
InputLabel,
LoadingOverlay,
TextInput,
Tooltip,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import { useDisclosure } from "@mantine/hooks";
import { useCallback } from "react";
import styles from "@/styles/input.module.css";
import { CreateQuizRoom, RoomOptionTypes } from "@/lib/definitions/quizRoom";
import { validatorFactory } from "@/lib/validation/creators";
import { isRequired } from "@/lib/validation/regex";
import { validate } from "@/lib/validation/validate";
import { InformationCircleIcon } from "@heroicons/react/24/outline";

const items = [
{ label: "All", href: "/quiz-rooms" },
{ label: "Create a Quiz Room", href: "#" },
{ label: "", href: "#" },
].map((item, index) => (
<Anchor href={item.href} key={index}>
<p className="text-black">{item.label}</p>
</Anchor>
));

const maxChar = validatorFactory(100, "max");
const minChar = validatorFactory(3, "min");

export default function Page() {
const [visible, { close, open }] = useDisclosure(false);

const form = useForm<CreateQuizRoom>({
initialValues: {
roomName: "",
questionSets: [],
roomOptions: [
"mode:normal",
"displaytop10only:false",
"allowjoinonquizstarted:false",
"allowreconnect:false",
"showLeaderboardEachRound:false",
],
},
clearInputErrorOnChange: true,
validateInputOnChange: true,
validate: {
roomName: (value, values) => {
const validators = [isRequired, minChar, maxChar];
return validate(value, validators);
},
},
});

// Creates new quiz room
const handelSubmit = useCallback(async () => {
open();

close();
}, [form.values]);

// toggles the mode for quizRoom
const toggleMode = useCallback(() => {
var options = form.values.roomOptions;
const modeIndex = options.findIndex(
(o) => o === "mode:normal" || o === "mode:elimination"
);
if (modeIndex !== -1) {
const deleted = options.splice(modeIndex, 1);
if (deleted[0] === "mode:normal") {
options = [...options, "mode:elimination"];
} else {
options = [...options, "mode:normal"];
}
} else {
options = [...options, "mode:normal"];
}
form.setFieldValue("roomOptions", options);
}, [form.values]);

const toogleOptions = useCallback(
(
option:
| "showLeaderboardEachRound"
| "displaytop10only"
| "allowreconnect"
| "allowjoinonquizstarted"
) => {
var options = form.values.roomOptions;
var optionIndex = options.findIndex((o) => o.startsWith(option));
if (optionIndex !== -1) {
const deleted = options.splice(optionIndex, 1);
if (deleted[0].endsWith("false")) {
options = [...options, `${option}:true`];
} else {
options = [...options, `${option}:false`];
}
} else {
options = [...options, `${option}:true`];
}
form.setFieldValue("roomOptions", options);
},
[form.values]
);

return (
<div className="flex flex-col px-6 md:px-16 md:pb-20 py-5 space-y-5 grow">
<Breadcrumbs>{items}</Breadcrumbs>
<div className="flex flex-col md:flex-row justify-between text-2xl font-bold">
<h3>Create New Question</h3>
</div>
<form
className="flex flex-col gap-8 relative"
onSubmit={form.onSubmit(() => {
handelSubmit();
})}
onReset={() => form.reset()}
>
<LoadingOverlay
visible={visible}
zIndex={1000}
overlayProps={{ radius: "sm", blur: 2 }}
/>
<TextInput
label="Room Name"
variant="filled"
withAsterisk
classNames={styles}
placeholder="Room name"
{...form.getInputProps("qStatement")}
/>
<div>
<InputLabel>Mode</InputLabel>
<div className="flex gap-10">
<Checkbox
label="Normal"
color="green"
checked={form.values.roomOptions.includes(
"mode:normal"
)}
onChange={(e) => {
toggleMode();
}}
/>
<div className="flex">
<Checkbox
label="Elimination"
color="green"
checked={form.values.roomOptions.includes(
"mode:elimination"
)}
onChange={(e) => {
toggleMode();
}}
/>
<Tooltip
label="Only top 50% of the participants willproceed to the next round"
multiline
w={220}
offset={{ mainAxis: 0, crossAxis: 100 }}
>
<InformationCircleIcon className="w-6" />
</Tooltip>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-5 max-w-2xl">
<Checkbox
label="Allow reconnect"
color="green"
checked={form.values.roomOptions.includes(
"allowreconnect:true"
)}
onChange={() => {
toogleOptions("allowreconnect");
}}
/>
<Checkbox
label="Show leaderboard each round"
color="green"
checked={form.values.roomOptions.includes(
"showLeaderboardEachRound:true"
)}
onChange={() => {
toogleOptions("showLeaderboardEachRound");
}}
/>
<Checkbox
id="allow-join"
label="Allow join on game started"
color="green"
onChange={() => {
toogleOptions("allowjoinonquizstarted");
}}
checked={form.values.roomOptions.includes(
"allowjoinonquizstarted:true"
)}
/>
<Checkbox
id="top-10-only"
label="Display top 10 participants only"
color="green"
onChange={() => {
toogleOptions("displaytop10only");
}}
checked={form.values.roomOptions.includes(
"displaytop10only:true"
)}
/>
</div>

<div className="flex justify-end">
<Button variant="transparent" color="gray" type="reset">
Cancel
</Button>
<Button variant="filled" color="green" type="submit">
Create Quiz Room
</Button>
</div>
</form>
</div>
);
}
Loading

0 comments on commit 50d45d9

Please sign in to comment.