Explicação do Código 📜
Importação de Dependências 🛠️
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm, Controller } from 'react-hook-form';
import { z } from 'zod';
import { Button } from '@/components/ui/button';
import { Form, FormControl, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { Card, CardContent } from '@/components/ui/card';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import RichTextContent from '@/components/RichTextContent';
- zodResolver: Importa o resolver para integrar o Zod com o React Hook Form, utilizado para validação de dados.
- useForm: Hook do
react-hook-form
para gerenciar e validar o estado do formulário. - Controller: Componente que integra o React Hook Form com componentes personalizados, como
Select
. - z: Biblioteca de validação usada para definir e validar o esquema do formulário.
- Button, Form, Card: Componentes UI personalizados importados para o layout e funcionalidade do formulário.
Definição do Esquema de Validação com Zod 🔐
const FormSchema = z.object({
respostas: z.array(
z.object({
alternativaId: z.string(),
resposta: z.string().nonempty('Selecione uma opção (V ou F).'),
}),
),
});
- FormSchema: Define o esquema de validação usando Zod. O campo
respostas
é um array de objetos, onde cada objeto tem:- alternativaId: Identificador da alternativa.
- resposta: Valor da resposta, que não pode ser vazio, validado com a mensagem
"Selecione uma opção (V ou F)"
.
Componente VerdadeiroOuFalsoComponent
🎮
const VerdadeiroOuFalsoComponent = ({ data, cursoId, exercicioId }) => {
const form = useForm({
resolver: zodResolver(FormSchema),
defaultValues: {
respostas: data.alternativas.map((alternativa) => ({
alternativaId: alternativa.id,
resposta: '',
})),
},
});
- VerdadeiroOuFalsoComponent: Componente principal do quiz, que recebe os dados do exercício (
data
), ocursoId
e oexercicioId
. - useForm: Inicializa o formulário com o
resolver
do Zod para validação e define os valores padrão das respostas.
Função verificarResposta
🔍
const verificarResposta = async (formData) => {
try {
const formattedRespostas = formData.respostas.map((resposta) => ({
alternativaId: resposta.alternativaId,
resposta: resposta.resposta === 'V' ? 'true' : 'false',
}));
const response = await fetch('/api/verificar-resposta', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
cursoId,
exercicioId,
respostas: formattedRespostas,
blockType: 'verdadeiroOuFalso',
}),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Erro ao verificar a resposta.');
}
const dataResponse = await response.json();
form.setValue('resultado', dataResponse.isCorrect ? 'Correto!' : 'Incorreto.');
if (!dataResponse.isCorrect && data.explicacao) {
form.setValue('explicacao', data.explicacao);
} else {
form.setValue('explicacao', null);
}
} catch (error) {
console.error('Erro ao verificar a resposta:', error);
alert('Ocorreu um erro ao verificar a resposta.');
}
};
- verificarResposta: Função assíncrona que é chamada ao submeter o formulário. Ela:
- Formata as respostas para enviá-las ao backend.
- Realiza uma requisição
POST
para o endpoint/api/verificar-resposta
, passando os dados do exercício e as respostas. - Se a resposta for válida, atualiza o resultado como "Correto!" ou "Incorreto." e exibe uma explicação, se fornecida.
- Em caso de erro, exibe um alerta.
Renderização do Formulário 💻
return (
<Card className="mb-4">
<CardContent>
<RichTextContent content={data.pergunta} />
<Form {...form}>
<form onSubmit={form.handleSubmit(verificarResposta)} className="space-y-6">
{data.alternativas.map((alternativa, index) => (
<FormItem key={alternativa.id} className="flex items-center space-x-3">
<FormControl className="w-2">
<Controller
name={`respostas.${index}.resposta`}
control={form.control}
render={({ field }) => (
<Select onValueChange={field.onChange} value={field.value} className="">
<SelectTrigger className="w-19 !w-19">
<SelectValue placeholder="V ou F" />
</SelectTrigger>
<SelectContent>
<SelectItem value="V">V</SelectItem>
<SelectItem value="F">F</SelectItem>
</SelectContent>
</Select>
)}
/>
</FormControl>
<FormLabel className="flex-1 ">{alternativa.texto}</FormLabel>
<input
type="hidden"
{...form.register(`respostas.${index}.alternativaId`)}
value={alternativa.id}
/>
<FormMessage>
{form.formState.errors.respostas &&
form.formState.errors.respostas[index]?.resposta?.message}
</FormMessage>
</FormItem>
))}
<Button type="submit">Verificar Resposta</Button>
</form>
</Form>
{form.getValues('resultado') && (
<div
className={`mt-2 ${
form.getValues('resultado') === 'Correto!' ? 'text-green-600' : 'text-red-600'
}`}
>
{form.getValues('resultado')}
</div>
)}
{form.getValues('resultado') === 'Incorreto.' && form.getValues('explicacao') && (
<div className="mt-2 text-gray-600">{form.getValues('explicacao')}</div>
)}
</CardContent>
</Card>
);
- Renderização:
- Exibe a pergunta utilizando
RichTextContent
. - Mapeia as alternativas e renderiza um
Select
para o usuário escolher "V" ou "F". - Exibe o botão para submeter as respostas e mostrar o resultado.
- Dependendo do resultado, a cor do feedback será verde (para "Correto!") ou vermelha (para "Incorreto").
- Se a resposta for incorreta, exibe a explicação, caso haja.
- Exibe a pergunta utilizando
Código Completo 💻
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm, Controller } from 'react-hook-form';
import { z } from 'zod';
import { Button } from '@/components/ui/button';
import { Form, FormControl, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { Card, CardContent } from '@/components/ui/card';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import RichTextContent from '@/components/RichTextContent';
const FormSchema = z.object({
respostas: z.array(
z.object({
alternativaId: z.string(),
resposta: z.string().nonempty('Selecione uma opção (V ou F).'),
}),
),
});
const VerdadeiroOuFalsoComponent = ({ data, cursoId, exercicioId }) => {
const form = useForm({
resolver: zodResolver(FormSchema),
defaultValues: {
respostas: data.alternativas.map((alternativa) => ({
alternativaId: alternativa.id,
resposta: '',
})),
},
});
const verificarResposta = async (formData) => {
try {
const formattedRespostas = formData.respostas.map((resposta) => ({
alternativaId: resposta.alternativaId,
resposta: resposta.resposta === 'V' ? 'true' : 'false',
}));
const response = await fetch('/api/verificar-resposta', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
cursoId,
exercicioId,
respostas: formattedRespostas,
blockType: 'verdadeiroOuFalso',
}),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Erro ao verificar a resposta.');
}
const dataResponse = await response.json();
form.setValue('resultado', dataResponse.isCorrect ? 'Correto!' : 'Incorreto.');
if (!dataResponse.isCorrect && data.explicacao) {
form.setValue('explicacao', data.explicacao);
} else {
form.setValue('explicacao', null);
}
} catch (error) {
console.error('Erro ao verificar a resposta:', error);
alert('Ocor
reu um erro ao verificar a resposta.');
}
};
return (
<Card className="mb-4">
<CardContent>
<RichTextContent content={data.pergunta} />
<Form {...form}>
<form onSubmit={form.handleSubmit(verificarResposta)} className="space-y-6">
{data.alternativas.map((alternativa, index) => (
<FormItem key={alternativa.id} className="flex items-center space-x-3">
<FormControl className="w-2">
<Controller
name={`respostas.${index}.resposta`}
control={form.control}
render={({ field }) => (
<Select onValueChange={field.onChange} value={field.value} className="">
<SelectTrigger className="w-19 !w-19">
<SelectValue placeholder="V ou F" />
</SelectTrigger>
<SelectContent>
<SelectItem value="V">V</SelectItem>
<SelectItem value="F">F</SelectItem>
</SelectContent>
</Select>
)}
/>
</FormControl>
<FormLabel className="flex-1 ">{alternativa.texto}</FormLabel>
<input
type="hidden"
{...form.register(`respostas.${index}.alternativaId`)}
value={alternativa.id}
/>
<FormMessage>
{form.formState.errors.respostas &&
form.formState.errors.respostas[index]?.resposta?.message}
</FormMessage>
</FormItem>
))}
<Button type="submit">Verificar Resposta</Button>
</form>
</Form>
{form.getValues('resultado') && (
<div
className={`mt-2 ${
form.getValues('resultado') === 'Correto!' ? 'text-green-600' : 'text-red-600'
}`}
>
{form.getValues('resultado')}
</div>
)}
{form.getValues('resultado') === 'Incorreto.' && form.getValues('explicacao') && (
<div className="mt-2 text-gray-600">{form.getValues('explicacao')}</div>
)}
</CardContent>
</Card>
);
};