made the quiz functional
Signed-off-by: Alex Stan <alex.stan.2010@proton.me> Fixed merge conflicts
This commit is contained in:
parent
48efcce544
commit
1c6c5327c3
6 changed files with 127 additions and 32 deletions
61
app/components/question_component.tsx
Normal file
61
app/components/question_component.tsx
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import { ReactComponentElement, ReactNode } from "react";
|
||||||
|
import { AnswerType, Question } from "../lib/question";
|
||||||
|
import { SessionState } from "../lib/session";
|
||||||
|
|
||||||
|
const QuestionView = ({ question, setState, state }: { question: Question, setState: any, state: SessionState }) => {
|
||||||
|
|
||||||
|
const handleOpenClick = () => {
|
||||||
|
setState( (prevState: SessionState) => ({
|
||||||
|
...prevState,
|
||||||
|
opened: {
|
||||||
|
...prevState.opened,
|
||||||
|
[question.id]: !prevState.opened[ question.id ]
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAnswerClick = (right: boolean) => {
|
||||||
|
setState( (prevState: SessionState) => ({
|
||||||
|
...prevState,
|
||||||
|
answer: {
|
||||||
|
...prevState.answer,
|
||||||
|
[question.id]: right ? AnswerType.Right : AnswerType.Wrong
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
question.answered = right ? AnswerType.Right : AnswerType.Wrong;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-2xl py-32 sm:py-48 lg:py-56">
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="text-4xl font-bold tracking-tight text-gray-50">
|
||||||
|
{question.text}
|
||||||
|
</h1>
|
||||||
|
{state.opened[ question.id ] ? (
|
||||||
|
<p className="mt-6 text-lg leading-8 text-gray-200">
|
||||||
|
{question.answer}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
<button className="text-sm font-semibold leading-6 text-gray-200" onClick={handleOpenClick}>
|
||||||
|
Show answer
|
||||||
|
</button>
|
||||||
|
<div className="mt-10 flex items-center justify-center gap-x-6">
|
||||||
|
<button
|
||||||
|
onClick={() => { handleAnswerClick( true )}}
|
||||||
|
className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||||
|
>
|
||||||
|
I knew that!
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {handleAnswerClick( false )}}
|
||||||
|
className="text-sm font-semibold leading-6 text-gray-300"
|
||||||
|
>
|
||||||
|
I didn't know that!
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default QuestionView;
|
|
@ -1,5 +1,5 @@
|
||||||
import { PlayIcon } from '@heroicons/react/24/outline'
|
import { PlayIcon } from '@heroicons/react/24/outline'
|
||||||
const TopicCard = ({ title, children, link }) => {
|
const TopicCard = ({ title, children, link }: { title: string, children: any, link: string }) => {
|
||||||
return(
|
return(
|
||||||
<div className="mx-auto mt-16 max-w-2xl rounded-3xl sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0" style={{marginTop: '20px', paddingTop: '0px', paddingBottom: '24px'}}>
|
<div className="mx-auto mt-16 max-w-2xl rounded-3xl sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0" style={{marginTop: '20px', paddingTop: '0px', paddingBottom: '24px'}}>
|
||||||
<svg
|
<svg
|
||||||
|
|
|
@ -8,7 +8,51 @@ import { CheckIcon } from '@heroicons/react/20/solid';
|
||||||
|
|
||||||
const Page = ({ params }: { params: { category: string }}) => {
|
const Page = ({ params }: { params: { category: string }}) => {
|
||||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
||||||
|
const [state, setState]: [SessionState, any] = useState<SessionState>( {
|
||||||
|
answered: 0,
|
||||||
|
wrong: 0,
|
||||||
|
right: 0,
|
||||||
|
category: '',
|
||||||
|
opened: [],
|
||||||
|
answer: []
|
||||||
|
} );
|
||||||
|
|
||||||
|
// const category = params.category;
|
||||||
|
|
||||||
|
// const [all_questions, set_all_questions] = useState<Question[]>([]);
|
||||||
|
const [questions, setQuestions] = useState<Question[]>([]);
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
// console.log( params.category );
|
||||||
|
const fetch_all_questions = async () => {
|
||||||
|
let result = await getQuestions();
|
||||||
|
console.log( `all_questions: ${ JSON.stringify( result ) }` );
|
||||||
|
// set_all_questions( result );
|
||||||
|
result = result.filter( q => q.category === params.category)
|
||||||
|
.map(it=>({...it, answered: AnswerType.Unset}));
|
||||||
|
//result = result.filter
|
||||||
|
setQuestions( result );
|
||||||
|
console.log( `q12 uestions: ${ JSON.stringify( questions ) }`, result );
|
||||||
|
}
|
||||||
|
fetch_all_questions();
|
||||||
|
|
||||||
|
var ans: AnswerType[];
|
||||||
|
questions?.forEach(q => {
|
||||||
|
ans[ q.id ] = AnswerType.Unset;
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
setState( (prevState: SessionState) => ({
|
||||||
|
...prevState,
|
||||||
|
answer: ans
|
||||||
|
}));
|
||||||
|
*/
|
||||||
|
}, [ params.category ]);
|
||||||
|
|
||||||
|
console.log( "questions16", questions );
|
||||||
|
|
||||||
|
|
||||||
|
console.log( "state15", state );
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
@ -27,35 +71,11 @@ const Page = ({ params }: { params: { category: string }}) => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx-auto max-w-2xl py-32 sm:py-48 lg:py-56">
|
<div style={{paddingTop: '72px'}}></div>
|
||||||
<div className="text-center">
|
<Logo/>
|
||||||
<h1 className="text-4xl font-bold tracking-tight text-gray-50">
|
{/* <QuestionView key = {questions[0].id} question={questions[0]} state={state} setState={setState}/> */}
|
||||||
[insert question about {params.category}]
|
{questions.map( (q) => (q.answered == AnswerType.Unset) ? <QuestionView key = {q.id} question={q} state={state} setState={setState}/> : <></>)}
|
||||||
</h1>
|
|
||||||
<p className="mt-6 text-lg leading-8 text-gray-200">
|
|
||||||
[Insert hidden answer]
|
|
||||||
</p>
|
|
||||||
<a href="#" className="text-sm font-semibold leading-6 text-gray-200">
|
|
||||||
Show answer
|
|
||||||
</a>
|
|
||||||
<div className="mt-10 flex items-center justify-center gap-x-6">
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
|
||||||
>
|
|
||||||
<CheckIcon width={'100px'}></CheckIcon>
|
|
||||||
I did know
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="#"
|
|
||||||
className="rounded-md bg-gray-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
|
||||||
>
|
|
||||||
<XMarkIcon width={'100px'}></XMarkIcon>
|
|
||||||
I didn't know
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
className="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]"
|
className="absolute inset-x-0 top-[calc(100%-13rem)] -z-10 transform-gpu overflow-hidden blur-3xl sm:top-[calc(100%-30rem)]"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
|
|
@ -7,18 +7,29 @@ export const enum QuizQuestion {
|
||||||
DR
|
DR
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const enum AnswerType {
|
||||||
|
Unset,
|
||||||
|
Right,
|
||||||
|
Wrong
|
||||||
|
};
|
||||||
|
|
||||||
export type Question = {
|
export type Question = {
|
||||||
id: number;
|
id: number;
|
||||||
category: string | null;
|
category: string | null;
|
||||||
type: string | null; // TODO: make a TS type for every question type
|
type?: string; // TODO: make a TS type for every question type
|
||||||
text: string;
|
text: string;
|
||||||
answer: string | boolean | QuizQuestion;
|
answer: string | boolean | QuizQuestion;
|
||||||
|
answered?: AnswerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getQuestion = async (id: number): Promise<Question> => {
|
export const getQuestion = async (id: number): Promise<Question> => {
|
||||||
return makeGetRequest( `questions/${id}/` );
|
return makeGetRequest( `questions/${id}/` );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getQuestions = async (): Promise<Question[]> => {
|
||||||
|
return makeGetRequest( `questions/` );
|
||||||
|
}
|
||||||
|
|
||||||
export const addQuestion = async (question: Question): Promise<Question> => {
|
export const addQuestion = async (question: Question): Promise<Question> => {
|
||||||
console.log( `adding question ${JSON.stringify( question)}` );
|
console.log( `adding question ${JSON.stringify( question)}` );
|
||||||
return makePostRequest( `questions`, question );
|
return makePostRequest( `questions`, question );
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { makeGetRequest, makePostRequest } from "./api";
|
import { makeGetRequest, makePostRequest } from "./api";
|
||||||
|
import { AnswerType } from "./question";
|
||||||
|
|
||||||
export type Session = {
|
export type Session = {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -13,6 +14,8 @@ export type SessionState = {
|
||||||
wrong: number;
|
wrong: number;
|
||||||
right: number;
|
right: number;
|
||||||
category: string;
|
category: string;
|
||||||
|
opened: boolean[];
|
||||||
|
answer: AnswerType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSession = async (id: number): Promise<Session> => {
|
export const getSession = async (id: number): Promise<Session> => {
|
||||||
|
|
Loading…
Reference in a new issue