feat: Añadir soporte para múltiples tipos de preguntas en la generación de plantillas de prueba y actualizar la interfaz correspondiente

This commit is contained in:
2026-04-02 11:24:33 -03:00
parent 83a25b3d28
commit d0a8e13fb6
4 changed files with 589 additions and 76 deletions
@@ -20,7 +20,7 @@ interface Question {
question_type: QuestionType;
question_text: string;
options?: string[];
correct_answer?: number | number[] | string;
correct_answer?: unknown;
explanation?: string;
points: number;
metadata?: any;
@@ -52,6 +52,7 @@ export default function TestTemplateForm({ onSuccess, onCancel }: TestTemplateFo
const [generatingAI, setGeneratingAI] = useState(false);
const [expandedQuestion, setExpandedQuestion] = useState<string | null>(null);
const [aiContext, setAiContext] = useState('');
const [aiQuestionType, setAiQuestionType] = useState<QuestionType>('multiple-choice');
// MySQL course selection state
const [mysqlPlans, setMysqlPlans] = useState<MySqlPlan[]>([]);
@@ -102,10 +103,10 @@ export default function TestTemplateForm({ onSuccess, onCancel }: TestTemplateFo
const handleCourseSelect = (courseId: number | '') => {
setSelectedCourseId(courseId);
// Store the MySQL course ID directly - level/course_type can be derived from mysql_courses table
setFormData({
...formData,
setFormData((prev) => ({
...prev,
mysql_course_id: courseId === '' ? undefined : courseId,
});
}));
};
const handleSubmit = async (e: React.FormEvent) => {
@@ -163,7 +164,8 @@ export default function TestTemplateForm({ onSuccess, onCancel }: TestTemplateFo
onSuccess?.();
} catch (error) {
console.error('Failed to create template:', error);
alert('Error al crear la plantilla');
const message = error instanceof Error ? error.message : 'Error al crear la plantilla';
alert(message);
} finally {
setSaving(false);
}
@@ -248,6 +250,7 @@ export default function TestTemplateForm({ onSuccess, onCancel }: TestTemplateFo
body: JSON.stringify({
topic: aiContext,
num_questions: 5,
question_type: aiQuestionType,
}),
});
@@ -266,10 +269,11 @@ export default function TestTemplateForm({ onSuccess, onCancel }: TestTemplateFo
question_order: questions.length + idx,
question_type: q.question_type || 'multiple-choice',
question_text: q.question_text || q.text,
options: q.options || [],
correct_answer: q.correct_answer || q.correct,
options: Array.isArray(q.options) ? q.options : [],
correct_answer: q.correct_answer ?? q.correct,
explanation: q.explanation || '',
points: q.points || 1,
metadata: q.metadata,
}));
setQuestions([...questions, ...questionsToAdd]);
@@ -409,6 +413,10 @@ export default function TestTemplateForm({ onSuccess, onCancel }: TestTemplateFo
onChange={(e) => {
setSelectedPlanId(e.target.value ? Number(e.target.value) : '');
setSelectedCourseId('');
setFormData((prev) => ({
...prev,
mysql_course_id: undefined,
}));
}}
disabled={loadingPlans}
className="w-full px-3 py-2 border border-blue-300 rounded-lg focus:ring-2 focus:ring-blue-500 bg-white"
@@ -554,6 +562,21 @@ export default function TestTemplateForm({ onSuccess, onCancel }: TestTemplateFo
placeholder="Describe el tema o contenido (ej: 'Past Simple tense, vocabulary about travel, 5 questions')"
disabled={generatingAI}
/>
<select
value={aiQuestionType}
onChange={(e) => setAiQuestionType(e.target.value as QuestionType)}
className="px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 bg-white"
disabled={generatingAI}
>
<option value="multiple-choice">Opcion multiple</option>
<option value="true-false">Verdadero/Falso</option>
<option value="short-answer">Respuesta corta</option>
<option value="essay">Ensayo</option>
<option value="matching">Emparejamiento</option>
<option value="ordering">Ordenar</option>
<option value="fill-in-the-blanks">Completar espacios</option>
<option value="audio-response">Respuesta de audio</option>
</select>
<button
type="button"
onClick={handleGenerateWithAI}
@@ -565,7 +588,7 @@ export default function TestTemplateForm({ onSuccess, onCancel }: TestTemplateFo
</button>
</div>
<p className="text-xs text-gray-500">
La IA generará preguntas de opción múltiple con explicaciones automáticas.
La IA genera varios tipos de ejercicios. Hotspot y Code Lab quedan para creacion manual del instructor.
</p>
</div>
+3 -3
View File
@@ -1059,8 +1059,8 @@ export const cmsApi = {
apiFetch(`/test-templates/${templateId}/sections/${sectionId}`, { method: 'DELETE' }, false),
applyTemplateToLesson: (templateId: string, lessonId: string, gradingCategoryId?: string): Promise<void> =>
apiFetch(`/test-templates/${templateId}/apply`, { method: 'POST', body: JSON.stringify({ lesson_id: lessonId, grading_category_id: gradingCategoryId }) }, false),
generateQuestionsWithRAG: (courseId?: number, topic?: string, numQuestions?: number): Promise<TestTemplateQuestion[]> =>
apiFetch('/test-templates/generate-with-rag', { method: 'POST', body: JSON.stringify({ course_id: courseId, topic, num_questions: numQuestions }) }, false),
generateQuestionsWithRAG: (courseId?: number, topic?: string, numQuestions?: number, questionType?: QuestionType): Promise<TestTemplateQuestion[]> =>
apiFetch('/test-templates/generate-with-rag', { method: 'POST', body: JSON.stringify({ course_id: courseId, topic, num_questions: numQuestions, question_type: questionType }) }, false),
// Admin - AI Usage Global
getGlobalAiUsage: (startDate?: string, endDate?: string): Promise<GlobalAiUsageResponse> =>
@@ -1382,7 +1382,7 @@ export interface BackgroundTask {
export type CourseLevel = 'beginner' | 'beginner_1' | 'beginner_2' | 'intermediate' | 'intermediate_1' | 'intermediate_2' | 'advanced' | 'advanced_1' | 'advanced_2';
export type CourseType = 'intensive' | 'regular';
export type TestType = 'CA' | 'MWT' | 'MOT' | 'FOT' | 'FWT';
export type QuestionType = 'multiple-choice' | 'true-false' | 'short-answer' | 'essay' | 'matching' | 'ordering';
export type QuestionType = 'multiple-choice' | 'true-false' | 'short-answer' | 'essay' | 'matching' | 'ordering' | 'fill-in-the-blanks' | 'audio-response';
export interface TestTemplate {
id: string;