Skip to content

Commit d176a5c

Browse files
committed
criar quiz manual completo
1 parent 0e46735 commit d176a5c

File tree

5 files changed

+422
-26
lines changed

5 files changed

+422
-26
lines changed

frontend/src/app/(home)/create/page.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Button } from "@/components/ui/button";
55
import Questions from "./questions/questions";
66
import { useTheme } from "@/hook/useTheme";
77
import { useSubTopic } from "@/hook/useSubTopic";
8+
import { useQuestion} from "@/hook/useQuestion";
89
import SetQuiz from "./theme/set";
910

1011
interface ThemeData {
@@ -41,6 +42,7 @@ export default function CreateQuiz() {
4142

4243
const { createTheme } = useTheme();
4344
const { createSubTopic } = useSubTopic();
45+
const { createQuestion } = useQuestion();
4446

4547
const handleNext = () => {
4648
if (!themeData?.title) {
@@ -91,17 +93,10 @@ export default function CreateQuiz() {
9193

9294
// 3. Cria todas as questões
9395
for (const question of questionsData) {
94-
await fetch(`/quiz-lab/api/questions`, {
95-
method: "POST",
96-
headers: {
97-
"Content-Type": "application/json",
98-
"Authorization": `Bearer ${localStorage.getItem("access_token")}`,
99-
},
100-
body: JSON.stringify({
101-
text: question.text,
102-
sub_topic_id: subTopicId,
103-
alternatives: question.alternatives,
104-
}),
96+
await createQuestion({
97+
text: question.text,
98+
sub_topic_id: subTopicId!,
99+
alternatives: question.alternatives,
105100
});
106101
}
107102

frontend/src/app/(home)/create/questions/manual.tsx

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ export default function ManualQuestions({
5555
alert("Digite o texto da questão!");
5656
return;
5757
}
58-
5958
const filledAlternatives = alternatives.filter((alt) =>
6059
alt.text.trim()
6160
);
@@ -70,6 +69,15 @@ export default function ManualQuestions({
7069
return;
7170
}
7271

72+
// Valida se todas as alternativas preenchidas têm explicação
73+
const allHaveExplanation = filledAlternatives.every((alt) =>
74+
alt.explanation.trim()
75+
);
76+
if (!allHaveExplanation) {
77+
alert("Todas as alternativas precisam ter uma explicação!");
78+
return;
79+
}
80+
7381
// Adiciona à lista
7482
onQuestionsChange([
7583
...questions,
@@ -113,7 +121,7 @@ export default function ManualQuestions({
113121
aria-label="Texto da questão"
114122
/>
115123
</div>
116-
124+
117125
<div className="space-y-4">
118126
{alternatives.map((alt, index) => (
119127
<div
@@ -128,8 +136,12 @@ export default function ManualQuestions({
128136
<Button
129137
variant="ghost"
130138
size="sm"
131-
onClick={() => removeAlternative(index)}
132-
aria-label={`Remover alternativa ${index + 1}`}
139+
onClick={() =>
140+
removeAlternative(index)
141+
}
142+
aria-label={`Remover alternativa ${
143+
index + 1
144+
}`}
133145
>
134146
✕ Remover
135147
</Button>
@@ -146,7 +158,9 @@ export default function ManualQuestions({
146158
e.target.value
147159
)
148160
}
149-
aria-label={`Texto da alternativa ${index + 1}`}
161+
aria-label={`Texto da alternativa ${
162+
index + 1
163+
}`}
150164
/>
151165
<div className="flex items-center space-x-2">
152166
<input
@@ -160,7 +174,9 @@ export default function ManualQuestions({
160174
e.target.checked
161175
)
162176
}
163-
aria-label={`Marcar alternativa ${index + 1} como correta`}
177+
aria-label={`Marcar alternativa ${
178+
index + 1
179+
} como correta`}
164180
/>
165181
<label className="text-sm font-medium">
166182
Está correta?
@@ -181,13 +197,15 @@ export default function ManualQuestions({
181197
e.target.value
182198
)
183199
}
184-
aria-label={`Explicação da alternativa ${index + 1}`}
200+
aria-label={`Explicação da alternativa ${
201+
index + 1
202+
}`}
185203
/>
186204
</div>
187205
</div>
188206
))}
189207
</div>
190-
208+
191209
<div className="flex justify-center gap-4 mt-4">
192210
<Button
193211
variant="subtle"
@@ -200,8 +218,8 @@ export default function ManualQuestions({
200218
</div>
201219

202220
<div className="mt-6">
203-
<Button
204-
onClick={handleAddQuestion}
221+
<Button
222+
onClick={handleAddQuestion}
205223
className="w-full"
206224
aria-label="Salvar questão e criar mais uma"
207225
>
@@ -216,7 +234,7 @@ export default function ManualQuestions({
216234
<h3 className="font-semibold mb-4">
217235
Questões adicionadas: {questions.length}
218236
</h3>
219-
237+
220238
{questions.length > 0 ? (
221239
<div className="space-y-2">
222240
{questions.map((q, idx) => (
@@ -225,14 +243,15 @@ export default function ManualQuestions({
225243
className="flex justify-between items-center py-3 px-4 border border-gray-200 hover:bg-gray-50 rounded-lg"
226244
>
227245
<span className="text-sm flex-1">
228-
<strong>{idx + 1}.</strong> {q.text.substring(0, 50)}
246+
<strong>{idx + 1}.</strong>{" "}
247+
{q.text.substring(0, 50)}
229248
{q.text.length > 50 && "..."}
230249
</span>
231250
<Button
232251
variant="ghost"
233252
size="sm"
234253
onClick={() => removeQuestion(idx)}
235-
className="ml-2 flex-shrink-0"
254+
className="ml-2 shrink-0"
236255
aria-label={`Remover questão ${idx + 1}`}
237256
>
238257
@@ -248,4 +267,4 @@ export default function ManualQuestions({
248267
</div>
249268
</div>
250269
);
251-
}
270+
}

frontend/src/app/(home)/create/questions/questions.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export default function Questions({
5454
questions={questions}
5555
/>
5656
) : (
57-
// <AutomaticQuestions onQuestionsChange={onQuestionsChange} />
5857
<AutomaticQuestions />
5958
)}
6059
</div>
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
"use client";
2+
import { useQuestion } from "@/hook/useQuestion";
3+
import { useParams } from "next/navigation";
4+
import { useEffect, useState } from "react";
5+
import { Question } from "@/util/types/question";
6+
import { Button } from "@/components/ui/button";
7+
8+
export default function SubTopicPlayPage() {
9+
const {
10+
getQuestionsBySubTopic,
11+
loading: hookLoading,
12+
error: hookError,
13+
} = useQuestion();
14+
const params = useParams();
15+
const subTopicId = Array.isArray(params?.sub_topic_id)
16+
? params.sub_topic_id[0]
17+
: (params?.sub_topic_id as string);
18+
19+
const [questions, setQuestions] = useState<Question[]>([]);
20+
const [loading, setLoading] = useState(true);
21+
const [error, setError] = useState<string | null>(null);
22+
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
23+
24+
useEffect(() => {
25+
const loadQuestions = async () => {
26+
if (subTopicId) {
27+
try {
28+
setLoading(true);
29+
const questionsData = await getQuestionsBySubTopic(
30+
subTopicId
31+
);
32+
setQuestions(questionsData || []);
33+
setError(null);
34+
} catch (err) {
35+
const errorMessage =
36+
err instanceof Error
37+
? err.message
38+
: "Erro ao carregar questões";
39+
setError(errorMessage);
40+
console.error("Error loading questions:", err);
41+
} finally {
42+
setLoading(false);
43+
}
44+
}
45+
};
46+
47+
loadQuestions();
48+
// eslint-disable-next-line react-hooks/exhaustive-deps
49+
}, [subTopicId]);
50+
51+
if (loading || hookLoading) {
52+
return (
53+
<div className="flex items-center justify-center min-h-screen">
54+
<div className="text-center">
55+
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4"></div>
56+
<p>Carregando questões...</p>
57+
</div>
58+
</div>
59+
);
60+
}
61+
62+
if (error || hookError) {
63+
return (
64+
<div className="p-6">
65+
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
66+
<p className="text-red-800">Erro: {error || hookError}</p>
67+
</div>
68+
</div>
69+
);
70+
}
71+
72+
if (questions.length === 0) {
73+
return (
74+
<div className="p-6">
75+
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4">
76+
<p className="text-yellow-800">
77+
Nenhuma questão encontrada para este subtópico.
78+
</p>
79+
</div>
80+
</div>
81+
);
82+
}
83+
84+
const currentQuestion = questions[currentQuestionIndex];
85+
86+
return (
87+
<div className="p-6 max-w-4xl mx-auto">
88+
<div className="mb-6">
89+
<h1 className="text-2xl font-bold mb-2">Quiz</h1>
90+
<p className="text-gray-600">
91+
Questão {currentQuestionIndex + 1} de {questions.length}
92+
</p>
93+
</div>
94+
95+
{/* Questão */}
96+
<div className="bg-layout-card rounded-lg p-6 mb-6 border">
97+
<h2 className="text-xl font-semibold mb-4">
98+
{currentQuestion.text}
99+
</h2>
100+
101+
{/* Alternativas */}
102+
<div className="space-y-3">
103+
{currentQuestion.alternatives.map((alternative, idx) => (
104+
<button
105+
key={idx}
106+
className="w-full text-left p-4 border rounded-lg hover:bg-blue-50 hover:border-blue-300 transition-colors"
107+
>
108+
<div className="flex items-start gap-3">
109+
<div className="shrink-0 w-6 h-6 rounded-full border-2 border-gray-300 flex items-center justify-center mt-0.5">
110+
<span className="text-sm">
111+
{String.fromCharCode(65 + idx)}
112+
</span>
113+
</div>
114+
<div className="flex-1">
115+
<p className="font-medium">
116+
{alternative.text}
117+
</p>
118+
</div>
119+
</div>
120+
</button>
121+
))}
122+
</div>
123+
</div>
124+
125+
{/* Navegação */}
126+
<div className="flex justify-between items-center">
127+
<Button
128+
variant="subtle"
129+
onClick={() =>
130+
setCurrentQuestionIndex((prev) => Math.max(0, prev - 1))
131+
}
132+
disabled={currentQuestionIndex === 0}
133+
>
134+
← Anterior
135+
</Button>
136+
137+
<div className="flex gap-2">
138+
{questions.map((_, idx) => (
139+
<button
140+
key={idx}
141+
onClick={() => setCurrentQuestionIndex(idx)}
142+
className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium transition-colors ${
143+
idx === currentQuestionIndex
144+
? "bg-blue-600 text-white"
145+
: "bg-gray-200 text-gray-700 hover:bg-gray-300"
146+
}`}
147+
>
148+
{idx + 1}
149+
</button>
150+
))}
151+
</div>
152+
153+
<Button
154+
onClick={() =>
155+
setCurrentQuestionIndex((prev) =>
156+
Math.min(questions.length - 1, prev + 1)
157+
)
158+
}
159+
disabled={currentQuestionIndex === questions.length - 1}
160+
>
161+
Próxima →
162+
</Button>
163+
</div>
164+
</div>
165+
);
166+
}

0 commit comments

Comments
 (0)