Skip to content

Commit

Permalink
add: improvement on quiz details
Browse files Browse the repository at this point in the history
  • Loading branch information
domysh committed Oct 12, 2024
1 parent a30a675 commit 0f6790c
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 72 deletions.
2 changes: 1 addition & 1 deletion .astro/astro/content.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,5 +356,5 @@ declare module 'astro:content' {

type AnyEntryMap = ContentEntryMap & DataEntryMap;

export type ContentConfig = typeof import("./../../src/content/config.js");
export type ContentConfig = typeof import("../../src/content/config.js");
}
13 changes: 10 additions & 3 deletions src/react/components/QuizCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@ import { useAppRouter } from '../utils/store';

export function QuizCard({ quiz }: { quiz: Quiz }) {

const { navigate } = useAppRouter()
const { navigateToQuiz } = useAppRouter()

return <Card role='button' className='border-2 rounded-lg border-white p-4 cursor-pointer' onClick={() => navigate(`quiz/${quiz.quizId}`)}>
return <Card role='button' className='border-2 rounded-lg border-white p-4 cursor-pointer' onClick={() => navigateToQuiz(quiz.quizId)}>
<div className="flex">
<div className="flex-1 flex flex-col items-start">
<p > {quiz.type} - {quiz.quizId}</p>
<p >{quiz.maxScore} points ({quiz.type} quiz)</p>
<h2 className='text-3xl font-semibold'> {quiz.title}</h2>
<p> {quiz.questionList.length} questions {quiz.isOpen == false? "(closed)": null}</p>
<div className='flex flex-1' />
<div className='flex justify-between w-full'>
<p> time: {quiz.timerDuration} s</p>
<small>{quiz.quizId}</small>
</div>

</div>
<div className='min-h-32 flex justify-center items-center'>
<FaChevronRight size={25} />
Expand Down
2 changes: 1 addition & 1 deletion src/react/pages/AppPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const AppPage = () => {
currentPage == "profile" ? <UserInfoPage user={user!} /> :
currentPage == "qrscan" ? <QRScan />:
currentPage == "leaderboard" ? <LeaderBoard /> :
currentPage.startsWith("quiz") ? <QuizInfo /> :
currentPage == "quiz-info" ? <QuizInfo /> :
"Loading..."
}
</Container>
Expand Down
84 changes: 64 additions & 20 deletions src/react/pages/app/QuizInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,51 @@ import { Button, Input, Radio } from "react-daisyui"
import { IoMdArrowRoundBack } from "react-icons/io"
import { TitleBar } from "../../components/TitleBar"
import { useAppRouter } from "../../utils/store"
import { useQuiz } from "../../utils/query";
import { type QuizDetails } from "../../utils/index";
import { useQuizes, useUserProfile } from "../../utils/query";
import { BsQrCodeScan } from "react-icons/bs";
import { useDisclosure } from "@mantine/hooks";
import { Modal } from '@mantine/core';
import { useRef, type MutableRefObject } from "react";
import { useEffect, useRef, type MutableRefObject } from "react";
import { QRCode } from "react-qrcode-logo"
import type { Quiz } from "../../utils";



export function QuizInfo() {
const [opened, { open, close }] = useDisclosure(false);
const { currentPage, navigate } = useAppRouter();
const quizId = currentPage.split('/')[1];
const quiz = useQuiz({ quizId });
const { navigate, quizId } = useAppRouter();
const quizes = useQuizes();
const quiz = quizes.data?.find(q => q.quizId == quizId)

const qrCodeRef = useRef<QRCode>();
if (quiz.data) {
console.log(quiz.data)
if (quiz) {
console.log(quiz)
}

useEffect(() => {
if (quizId == null){
navigate("app")
}
}, [quizId])



return <>
<Modal opened={opened} onClose={close} withCloseButton={true} size={'xl'} title="Qr code">
<div className="flex flex-col items-center">
<QRCode
size={400}
value={`quiz:${quizId}`}
value={`quiz:${quizId}`}
logoImage="/assets/images/icons/icon-512x512.png"
ref={qrCodeRef as MutableRefObject<QRCode>}
qrStyle="fluid"
removeQrCodeBehindLogo
logoPaddingStyle="circle"
logoWidth={100}
eyeRadius={20}
/>
<div className="h-8"></div>
<Button onClick={() => qrCodeRef.current?.download("png", quizId)}>Download Qr</Button>
<Button onClick={() => qrCodeRef.current?.download("png", quizId??"")}>Download Qr</Button>
</div>
</Modal>

Expand All @@ -40,28 +55,31 @@ export function QuizInfo() {
<Button className="btn-circle mr-4" onClick={() => navigate("app")} >
<IoMdArrowRoundBack size={32} />
</Button>,
...quiz.isSuccess ? [
...quiz != null ? [
<Button className="btn-circle " onClick={open} >
<BsQrCodeScan size={26} />
</Button>
] : []
]}
/>
<div className="h-10"></div>
{quiz.isLoading && <h1>Loading quiz {quizId}</h1>}
{quiz.isSuccess && <QuizDetails quiz={quiz.data} />}
{quiz.isError && quiz.error && <div className="flex-1 flex flex-col justify-center items-center">
<p className="mb-12 text-5xl font-bold">Ops!! {quiz.error.message}</p>
{quizes.isLoading && <h1>Loading quiz {quizId}</h1>}
{quiz && <QuizDetails quiz={quiz} />}
{quizes.isError && quizes.error && <div className="flex-1 flex flex-col justify-center items-center">
<p className="mb-12 text-5xl font-bold">Ops!! {quizes.error.message}</p>
<img src="https://media1.tenor.com/m/KWCVIqd2HmYAAAAd/boris-proda.gif" />
</div>}
</div>
</>

}

const QuizDetails = ({ quiz }: { quiz: QuizDetails }) => {
const QuizDetails = ({ quiz }: { quiz: Quiz }) => {

const createdBy = useUserProfile(quiz.creatorUid)

return <div className="flex flex-col items-stretch text-start">
<h2 className="text-4xl font-bold mr-4">Title: {quiz.title}</h2>
<h2 className="text-4xl font-bold mr-4">Title: {quiz.title} {quiz.isOpen == false && "(closed)"}</h2>
<div className="flex items-center mt-4">
<h2 className="text-xl font-bold mr-4 w-40">Quiz type: </h2>
<Input
Expand All @@ -70,12 +88,38 @@ const QuizDetails = ({ quiz }: { quiz: QuizDetails }) => {
readOnly={true}
/>
</div>
<div className="mt-4">
<h2 className="text-3xl font-semibold">Questions: </h2>
<div className="flex items-center mt-4">
<h2 className="text-xl font-bold mr-4 w-40">Max value: </h2>
<Input
className="input input-bordered w-full no-control text-white"
value={quiz.maxScore}
readOnly={true}
/>
</div>
<div className="flex items-center mt-4">
<h2 className="text-xl font-bold mr-4 w-40">Created by: </h2>
<Input
className="input input-bordered w-full no-control text-white"
value={createdBy.isLoading?`${quiz.creatorUid} (Loading data...)`:`${createdBy.data?.name} ${createdBy.data?.surname} (${createdBy.data?.nickname})`}
readOnly={true}
/>
</div>
<div className="flex items-center mt-4">
<h2 className="text-xl font-bold mr-4 w-40">Timer Duration: </h2>
<Input
className="input input-bordered w-full no-control text-white"
value={`${quiz.timerDuration} s`}
readOnly={true}
/>

</div>

<div className="mt-4">
{quiz.questionList.length == 0 && <h2 className="text-3xl font-semibold">No questions</h2>}
{quiz.questionList.length > 0 && <h2 className="text-3xl font-semibold">Questions: </h2>}
{quiz.questionList.map((q, i) => (
<div key={q.questionId} className="mt-4">
<h3 className="text-2xl font-medium">{i + 1}. {q.text} </h3>
<h3 className="text-2xl font-medium">{i + 1}. {q.text} ({q.value??"?"} points)</h3>

{q.answerList.map((a) => (
<div key={a.id} className="flex mt-4 gap-4">
Expand Down
10 changes: 8 additions & 2 deletions src/react/pages/app/QuizList.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { FaPlus } from "react-icons/fa";
import { useAppRouter } from "../../utils/store";
import { Button } from "react-daisyui";
import { Button, Checkbox } from "react-daisyui";
import { MdLeaderboard } from "react-icons/md";
import { TitleBar } from "../../components/TitleBar";
import { QuizCard } from "../../components/QuizCard";
import { useQuizes } from "../../utils/query";
import { BsQrCodeScan } from "react-icons/bs";
import { Space } from "@mantine/core";
import { useState } from "react";


export const QuizList = () => {

const { navigate } = useAppRouter()

const quizes = useQuizes()
const [showHidden, setShowHidden] = useState(false)

return <div className="h-full">
<TitleBar title="Quizes" actions={[
Expand All @@ -28,9 +30,13 @@ export const QuizList = () => {
</Button>]}
/>
<Space h="md" />
<div className="flex">
<b>Show hidden: </b>
<Checkbox className="ml-3 checkbox-white" size="md" onChange={(e)=>setShowHidden(e.target.checked)} checked={showHidden}/>
</div>
{quizes.isLoading && <div>Loading...</div>}
<div className="flex flex-col gap-4 mt-8">
{quizes && quizes.data?.map(q => (<QuizCard key={q.quizId} quiz={q}></QuizCard>))}
{quizes && quizes.data?.filter(q => showHidden || q.type != "hidden").map(q => (<QuizCard key={q.quizId} quiz={q}></QuizCard>))}
</div>
</div>
}
43 changes: 10 additions & 33 deletions src/react/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,35 +50,30 @@ type LeaderBoardData = {
}

export type Quiz = {
quizId: string;
quizId: string,
creatorUid: string,
isOpen: boolean,
maxScore: string,
questionList: Question[],
title: string,
timerDuration: number,
talkId: string,
sponsorId: string,
type: "talk" | "sponsor" | "special" | "hidden",
}

export type Question = {
questionId: string,
text: string,
correctAnswer: any,
value: any,
correctAnswer: string|null,
value: number|null,
answerList: Answer[],
}

export type Answer = {
id: string,
text: string,
}

export type QuizDetails = {
creatorUid: string,
isOpen: boolean,
maxScore: string,
questionList: Question[],
title: string,
talkId: string,
sponsorId: string,
type: "talk" | "sponsor" | "special" | "hidden",
}

export type UserProfile = {
userId: string,
nickname: string,
Expand Down Expand Up @@ -150,24 +145,6 @@ export const getQuizList = async () => {
return JSON.parse(data) as Quiz[];
}

export const getQuizInfo = async (quizid: string) => {
const func = httpsCallable<{ code: string }, String>(firebase.functions, "getQuiz");

const response = await func({ code: `quiz:${quizid}` });
const rawData = response.data;
const { error, data } = JSON.parse(rawData as string);

if (error) {
throw {
name: error['errorCode'],
message: error['details'],
stack : '/getQuiz'
} as Error;
}

return JSON.parse(data) as QuizDetails;
}

export const getUserProfileById = async (uid: string) => {
const func = httpsCallable(firebase.functions, "getUserProfileById");

Expand Down
12 changes: 3 additions & 9 deletions src/react/utils/query.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { onAuthStateChanged } from "firebase/auth"
import { useEffect, useState } from "react"
import { firebase, getQuizInfo, getQuizList, getUserProfileById } from "."
import { firebase, getQuizList, getUserProfileById } from "."
import { useQuery } from "@tanstack/react-query"


Expand All @@ -20,14 +20,8 @@ export const useFirebaseUserInfo = () => {
export const useQuizes = () => {
return useQuery({
queryKey: ["quizes"],
queryFn: getQuizList
})
}

export const useQuiz = ({ quizId: quizId }: { quizId: string }) => {
return useQuery({
queryKey: ["quiz"],
queryFn: () => getQuizInfo(quizId)
queryFn: getQuizList,
staleTime: 1000 * 60 * 5
})
}

Expand Down
10 changes: 7 additions & 3 deletions src/react/utils/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

import { create } from 'zustand'

export type AppPage = "app" | "verify-email" | "profile" | "add-quiz" | "leaderboard" | "qrscan" | string;
export type AppPage = "app" | "verify-email" | "profile" | "add-quiz" | "leaderboard" | "qrscan" | "quiz-info";

type AppRouterStore = {
currentPage: AppPage
navigate: (to:AppPage) => void
currentPage: AppPage,
navigate: (to:AppPage) => void,
quizId: string | null,
navigateToQuiz: (quizId: string) => void,
}

export const useAppRouter = create<AppRouterStore>()((set) => ({
currentPage: "app",
navigate: (to:AppPage) => set(() => ({ currentPage: to })),
quizId: null,
navigateToQuiz: (quizId: string) => set(() => ({ quizId, currentPage: "quiz-info" })),
}))
6 changes: 6 additions & 0 deletions src/style/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,12 @@ html.multicolored-bg{
--fallback-bc: #1e1e1e;
border-width: 2px;
}

.checkbox-white{
--chkfg: #1e1e1e;
--fallback-bc: #dedede ;
border-width: 2px;
}
.radio {
--chkfg: #dedede;
--fallback-bc: #dedede;
Expand Down

0 comments on commit 0f6790c

Please sign in to comment.