I'm new to NextJS 14 and I begin to understand the client and server side.
I'm building a quiz software to train for an incoming exam and the software is almost OK. I'm trying to see if the structure I tried is OK and if a small change can solve the problem. I know my code is ugly but I just want it functional right now.
My problem as I understand it :
In my page.tsx I have the link with DB where I get all my questions.
In my test2.tsx I init all the states I'll use to get the dynamix and the refresh of my component QuizQuestionsPage
In my QuizQuestionsPage.tsx, I display the question, i dynamically color the answer with answer buttons. The validate button would post ( onclick ) my reponse to database ( and previously get if it's a success or a fail ).
My problem : I would like to write on DB, but as the post is async, I cannot do it from a 'use client' component. I tried to encapsulate it in an useEffect, but same problem. I tried to pass an insertDB function from page => Toto => QuizQuestionsPage but didn't work.
running out of simple ideas ...
Structure :
app schema.tsx ( containing DB beans ) quiz.db QuizQuestionsPage.tsx
app > quiz_random page.tsx test2.tsx ( host of Toto Component )
PAGE.TSX :
`export default async function Page() {
const qs:any = await db.select().from(questionType)
const ques = shuffleArray(qs)
return (
<div>
<Toto ques={ques}/>
</div>
);
}`
TEST2.TSX :
`export default function Toto(
{ques}:{ques:any}
) {
const [selectedAnswer, setSelectedAnswer] = useState<null | string>(null);
const [selectedAnswer2, setSelectedAnswer2] = useState<null | string>(null);
const [indice, setIndice] = useState(0);
const [cours, setCours] = useState('Les_5_mouvements');
const [question, setQuestion] = useState(null);
return (
<div>
<QuizQuestionsPage cours_distinct='random'
questions={ques}
indice={indice}
selectedAnswer={selectedAnswer}
selectedAnswer2={selectedAnswer2}
setSelectedAnswer={setSelectedAnswer}
setSelectedAnswer2={setSelectedAnswer2}
setIndice={setIndice}
test_log={test_log}
/>
</div>
);
}`
QUIZQUESTIONSPAGE.TSX
`'use client';
import React, { useEffect, useState } from 'react';
import { questionType, reponse } from "./schema";
import { random_item } from './toolbox';
import { db } from './page';
import { title } from 'process';
import { lusitana } from './ui/fonts';
import { render } from 'react-dom';
export default function QuizQuestionsPage(
{ cours_distinct,
questions,
selectedAnswer,
selectedAnswer2,
setSelectedAnswer,
setSelectedAnswer2,
indice,
setIndice,
test_log
}:
{ cours_distinct: any,
questions: typeof questionType.$inferSelect [],
selectedAnswer:any,
selectedAnswer2:any,
setSelectedAnswer:any,
setSelectedAnswer2:any,
indice:any,
setIndice:any,
test_log:any
// push_reponse:any
}
) {
const date = new Date().toLocaleDateString("fr-CA", {year:"numeric", month: "2-digit", day:"2-digit"})
const handleSelectAnswer = (index: any) => {
setSelectedAnswer(index);
// Met à jour le state de manière asynchrone
};
const handleSelectAnswer2 = (index: any) => {
setSelectedAnswer2(index);
};
const fail = (question: typeof questionType.$inferSelect ) => {
console.log('ko init')
// db.insert(reponse).values({id_question: questions[indice-1].id,
// date: date,
// success: 0 })
// console.log('ko')
}
const success = (question: typeof questionType.$inferSelect ) => {
// const push_db = async () => {
// await db.insert(reponse).values({id_question: questions[indice-1].id,
// date: date,
// success: 0 })
// }
// push_db()
test_log()
console.log('ok')
}
const test = () => {
console.log('testtest')
}
const post_reponse = (question: typeof questionType.$inferSelect ) => {
if (question.type === 'K') {
if (question.ok === selectedAnswer) {
success(question);
} else {
fail(question);
}
}
if (question.type === 'A') {
if (question[question.ok] === selectedAnswer) {
success(question);
} else {
fail(question);
}
}
if (question.type === 'C') {
if (question[question.ok] === selectedAnswer) {
success(question);
} else {
fail(question);
}
if (question[question.ok2] === selectedAnswer2) {
success(question);
} else {
fail(question);
}
}
setIndice(indice + 1)
}
const renderTheme = () => {
return (
<><div>
<div className="w-full rounded-xl bg-slate-900 p-2 shadow-sm">
<p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>
COURS : {questions[indice].cours}
</p>
<br></br>
<p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>
THEME : {questions[indice].theme}
</p>
</div>
</div><br></br></>
)
}
const renderType = () => {
switch (questions[indice].type) {
case 'A':
return (
<>
<h3>Questions de type A</h3>
<br></br>
<h4>Chaque question ne possède qu'une seule bonne réponse parmi les 5 proposées.</h4>
</>
);
case 'K':
return (
<>
<h3>Questions de type K</h3>
<br></br>
<h4>Réponse A : Seulement les propositions P1, P2 et P3 sont justes.</h4>
<h4>Réponse B : Seulement les propositions P1 et P3 sont justes.</h4>
<h4>Réponse C : Seulement les propositions P2 et P4 sont justes.</h4>
<h4>Réponse D : Seulement la proposition P4 est juste.</h4>
<h4>Réponse E : Toutes les propositions sont justes.</h4>
</>
);
case 'C':
return (
<>
<h3>Questions de type C</h3>
<br></br>
<h4>Chaque question ne possède qu'une seule bonne réponse parmi les 5 proposées.</h4>
<h4>La réponse peut être la même pour les deux questions.</h4>
</>
);
}
}
const renderReponses = () => {
switch (questions[indice].type) {
case 'A':
return (
<div className='text-white'>
<div className='text-4xl'>{questions[indice].titre}</div>
<hr></hr>
<ol>
<li key={questions[indice].r1} style={{ color: selectedAnswer === questions[indice].r1 ? 'green' : 'white' }}>
<p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>
{questions[indice].r1}
</p>
</li>
<li key={questions[indice].r2} style={{ color: selectedAnswer === questions[indice].r2 ? 'green' : 'white' }}>
<p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>{questions[indice].r2}</p></li>
<li key={questions[indice].r3} style={{ color: selectedAnswer === questions[indice].r3 ? 'green' : 'white' }}>
<p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>{questions[indice].r3}</p></li>
<li key={questions[indice].r4} style={{ color: selectedAnswer === questions[indice].r4 ? 'green' : 'white' }}>
<p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>{questions[indice].r4}</p></li>
<li key={questions[indice].r5} style={{ color: selectedAnswer === questions[indice].r5 ? 'green' : 'white' }}>
<p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>{questions[indice].r5}</p></li>
</ol>
<br></br>
<div className="relative items-center w-full">
<div className="grid w-full grid-cols-5 gap-12 mx-auto">
<div className="p-2 w-full">
<div className="inline-flex items-center justify-center bg-slate-900 opacity-80 w-full">
<button onClick={() => handleSelectAnswer(questions[indice].r1)}>Reponse 1</button>
</div>
</div>
<div className="p-2 w-full">
<div className="inline-flex items-center justify-center g-slate-900 opacity-80 w-full">
<button onClick={() => handleSelectAnswer(questions[indice].r2)}>reponse 2</button>
</div>
</div>
<div className="p-2 w-full">
<div className="inline-flex items-center justify-center g-slate-900 opacity-80 w-full">
<button onClick={() => handleSelectAnswer(questions[indice].r3)}>reponse 3</button>
</div>
</div>
<div className="p-2 w-full">
<div className="inline-flex items-center justify-center g-slate-900 opacity-80 w-full">
<button onClick={() => handleSelectAnswer(questions[indice].r4)}>reponse 4</button>
</div>
</div>
<div className="p-2 w-full">
<div className="inline-flex items-center justify-center g-slate-900 opacity-80 w-full">
<button onClick={() => handleSelectAnswer(questions[indice].r5)}>reponse 5</button>
</div>
</div>
</div>
</div>
<br></br>
<div className="relative items-center w-full">
<div className="grid w-full grid-cols-1 gap-12 mx-auto">
<button id='rep1' onClick={() => post_reponse(questions[indice])}>valider</button>
</div>
</div>
</div>
);
case 'K':
return (
<div>
<div className='text-4xl'>{questions[indice].titre}</div>
<hr></hr>
<ol>
<li key={questions[indice].r1} style={{ color: (selectedAnswer === 'A' || selectedAnswer === 'B' || selectedAnswer === 'E') ? 'green' : 'white' }}><p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>p1 : {questions[indice].r1}</p></li>
<li key={questions[indice].r2} style={{ color: (selectedAnswer === 'A' || selectedAnswer === 'C' || selectedAnswer === 'E') ? 'green' : 'white' }}><p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>P2 : {questions[indice].r2}</p></li>
<li key={questions[indice].r3} style={{ color: (selectedAnswer === 'A' || selectedAnswer === 'B' || selectedAnswer === 'E') ? 'green' : 'white' }}><p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>P3 : {questions[indice].r3}</p></li>
<li key={questions[indice].r4} style={{ color: (selectedAnswer === 'C' || selectedAnswer === 'D' || selectedAnswer === 'E') ? 'green' : 'white' }}><p
className={`${lusitana.className}
truncate rounded-xl bg-slate-900 px-4 py-4 text-center text-5xl w-full`}
>P4 : {questions[indice].r4}</p></li>
</ol>
<div className='row'>
<div className='col-2'>
<button onClick={() => handleSelectAnswer('A')}>Reponse A</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer('B')}>reponse B</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer('C')}>reponse C</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer('D')}>reponse D</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer('E')}>reponse E</button>
</div>
<div className='row'>
<div className='col-2'>
<button id='rep1' onClick={() => post_reponse(questions[indice])}>valider</button>
</div>
</div>
</div>
</div>
);
case 'C':
return (
<div>
<div className='text-4xl'>Question 1 : {questions[indice].titre}</div>
<div className='text-4xl'>Question 2 : {questions[indice].titre2}</div>
<hr></hr>
<ol>
<li key={questions[indice].r1} style={{
color:
(selectedAnswer === questions[indice].r1 && selectedAnswer2 === questions[indice].r1 && selectedAnswer !== null && selectedAnswer2 !== null)
? 'red'
: selectedAnswer !== questions[indice].r1 && selectedAnswer2 === questions[indice].r1 && selectedAnswer2 !== null
? 'green'
: selectedAnswer === questions[indice].r1 && selectedAnswer2 !== questions[indice].r1 && selectedAnswer !== null
? 'blue'
: 'white'
}}>{questions[indice].r1}</li>
<li key={questions[indice].r2} style={{
color:
selectedAnswer === questions[indice].r2 && selectedAnswer2 === questions[indice].r2 && selectedAnswer !== null && selectedAnswer2 !== null
? 'red'
: selectedAnswer !== questions[indice].r2 && selectedAnswer2 === questions[indice].r2 && selectedAnswer2 !== null
? 'green'
: selectedAnswer === questions[indice].r2 && selectedAnswer2 !== questions[indice].r2 && selectedAnswer !== null
? 'blue'
: 'white'
}}>{questions[indice].r2}</li>
<li key={questions[indice].r3} style={{
color:
selectedAnswer === questions[indice].r3 && selectedAnswer2 === questions[indice].r3 && selectedAnswer !== null && selectedAnswer2 !== null
? 'red'
: selectedAnswer !== questions[indice].r3 && selectedAnswer2 === questions[indice].r3 && selectedAnswer2 !== null
? 'green'
: selectedAnswer === questions[indice].r3 && selectedAnswer2 !== questions[indice].r3 && selectedAnswer !== null
? 'blue'
: 'white'
}}>{questions[indice].r3}</li>
<li key={questions[indice].r4} style={{
color:
selectedAnswer === questions[indice].r4 && selectedAnswer2 === questions[indice].r4 && selectedAnswer !== null && selectedAnswer2 !== null
? 'red'
: selectedAnswer !== questions[indice].r4 && selectedAnswer2 === questions[indice].r4 && selectedAnswer2 !== null
? 'green'
: selectedAnswer === questions[indice].r4 && selectedAnswer2 !== questions[indice].r4 && selectedAnswer !== null
? 'blue'
: 'white'
}}>{questions[indice].r4}</li>
<li key={questions[indice].r5} style={{
color:
selectedAnswer === questions[indice].r5 && selectedAnswer2 === questions[indice].r5 && selectedAnswer !== null && selectedAnswer2 !== null
? 'red'
: selectedAnswer !== questions[indice].r5 && selectedAnswer2 === questions[indice].r5 && selectedAnswer2 !== null
? 'green'
: selectedAnswer === questions[indice].r5 && selectedAnswer2 !== questions[indice].r5 && selectedAnswer !== null
? 'blue'
: 'white'
}}>{questions[indice].r5}</li>
</ol>
<hr></hr>
<div className='row'>
<div className='col-2'>
<button onClick={() => handleSelectAnswer(questions[indice].r1)}>Reponse 1</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer(questions[indice].r2)}>reponse 2</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer(questions[indice].r3)}>reponse 3</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer(questions[indice].r4)}>reponse 4</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer(questions[indice].r5)}>reponse 5</button>
</div>
</div>
<div className='row'>
<div className='col-2'>
<button onClick={() => handleSelectAnswer2(questions[indice].r1)}>Reponse 1</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer2(questions[indice].r2)}>reponse 2</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer2(questions[indice].r3)}>reponse 3</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer2(questions[indice].r4)}>reponse 4</button>
</div>
<div className='col-2'>
<button onClick={() => handleSelectAnswer2(questions[indice].r5)}>reponse 5</button>
</div>
</div>
<div className='row'>
<div className='col-2'>
<button id='rep1' onClick={() => post_reponse(questions[indice])}>valider</button>
</div>
</div>
</div>
);
}
};
return (
<div className='text-white'>
<button id='test' onClick={() => test()}>valider FINAL</button>
{renderTheme()}
<div className="w-full rounded-xl bg-slate-900 p-2 shadow-sm">
<div className='text-4xl'>
{renderType()}
<br></br>
<hr></hr>
<br></br>
</div>
</div>
<div className="w-full rounded-xl bg-slate-900 p-2 shadow-sm">
{renderReponses()}
</div>
</div>
);
};`
I tried to encapsulate it in an useEffect, but same problem. I tried to pass an insertDB function from page => Toto => QuizQuestionsPage but didn't work.