Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -39,8 +39,8 @@ export default function TestTemplateManager({ onSelectTemplate, onCreateTemplate
|
|||||||
const [applyCourses, setApplyCourses] = useState<MySqlCourse[]>([]);
|
const [applyCourses, setApplyCourses] = useState<MySqlCourse[]>([]);
|
||||||
const [applyPlanId, setApplyPlanId] = useState<number | ''>('');
|
const [applyPlanId, setApplyPlanId] = useState<number | ''>('');
|
||||||
const [applyCourseId, setApplyCourseId] = useState<number | ''>('');
|
const [applyCourseId, setApplyCourseId] = useState<number | ''>('');
|
||||||
|
const [applyCoursesError, setApplyCoursesError] = useState('');
|
||||||
|
|
||||||
const [selectedCourse, setSelectedCourse] = useState<string>('');
|
|
||||||
const [selectedLesson, setSelectedLesson] = useState<string>('');
|
const [selectedLesson, setSelectedLesson] = useState<string>('');
|
||||||
const [applying, setApplying] = useState(false);
|
const [applying, setApplying] = useState(false);
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ export default function TestTemplateManager({ onSelectTemplate, onCreateTemplate
|
|||||||
setSelectedTemplate(null);
|
setSelectedTemplate(null);
|
||||||
setApplyPlanId('');
|
setApplyPlanId('');
|
||||||
setApplyCourseId('');
|
setApplyCourseId('');
|
||||||
setSelectedCourse('');
|
setApplyCoursesError('');
|
||||||
setSelectedLesson('');
|
setSelectedLesson('');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -87,11 +87,64 @@ export default function TestTemplateManager({ onSelectTemplate, onCreateTemplate
|
|||||||
if (!applyPlanId) {
|
if (!applyPlanId) {
|
||||||
setApplyCourses([]);
|
setApplyCourses([]);
|
||||||
setApplyCourseId('');
|
setApplyCourseId('');
|
||||||
|
setApplyCoursesError('');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
questionBankApi.getMySQLCoursesByPlan(applyPlanId).then(setApplyCourses).catch(() => {});
|
questionBankApi
|
||||||
|
.getMySQLCoursesByPlan(applyPlanId)
|
||||||
|
.then((courses) => {
|
||||||
|
setApplyCourses(courses);
|
||||||
|
setApplyCoursesError('');
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to load MySQL courses by plan:', error);
|
||||||
|
setApplyCourses([]);
|
||||||
|
setApplyCoursesError('No se pudieron cargar los cursos para este plan.');
|
||||||
|
});
|
||||||
}, [applyPlanId]);
|
}, [applyPlanId]);
|
||||||
|
|
||||||
|
const getCourseTypeFromPlan = (planName: string): 'intensive' | 'regular' => {
|
||||||
|
const planLower = (planName || '').toLowerCase();
|
||||||
|
return planLower.includes('intensive') || planLower.includes('intensivo') ? 'intensive' : 'regular';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCourseLevelFromMysql = (nivelCurso?: number, planNombre?: string): string => {
|
||||||
|
if (typeof nivelCurso === 'number') {
|
||||||
|
if (nivelCurso >= 1 && nivelCurso <= 2) return 'beginner';
|
||||||
|
if (nivelCurso >= 3 && nivelCurso <= 4) return 'beginner_1';
|
||||||
|
if (nivelCurso >= 5 && nivelCurso <= 6) return 'beginner_2';
|
||||||
|
if (nivelCurso >= 7 && nivelCurso <= 8) return 'intermediate';
|
||||||
|
if (nivelCurso >= 9 && nivelCurso <= 10) return 'intermediate_1';
|
||||||
|
if (nivelCurso >= 11 && nivelCurso <= 12) return 'intermediate_2';
|
||||||
|
return 'advanced';
|
||||||
|
}
|
||||||
|
|
||||||
|
const planLower = (planNombre || '').toLowerCase();
|
||||||
|
if (planLower.includes('basic') || planLower.includes('beginner')) return 'beginner';
|
||||||
|
if (planLower.includes('intermediate') || planLower.includes('intermedio')) return 'intermediate';
|
||||||
|
return 'advanced';
|
||||||
|
};
|
||||||
|
|
||||||
|
const matchingPlatformCourses = useMemo(() => {
|
||||||
|
if (!applyCourseId) return [] as Course[];
|
||||||
|
const mysqlCourse = applyCourses.find((c) => c.idCursos === applyCourseId);
|
||||||
|
if (!mysqlCourse) return [] as Course[];
|
||||||
|
|
||||||
|
const expectedLevel = getCourseLevelFromMysql(mysqlCourse.NivelCurso, mysqlCourse.NombrePlan);
|
||||||
|
const expectedType = getCourseTypeFromPlan(mysqlCourse.NombrePlan);
|
||||||
|
|
||||||
|
return courses.filter((course) => {
|
||||||
|
const courseLevel = String((course as { level?: string }).level || '').toLowerCase();
|
||||||
|
const courseType = String((course as { course_type?: string }).course_type || '').toLowerCase();
|
||||||
|
return courseLevel === expectedLevel && courseType === expectedType;
|
||||||
|
});
|
||||||
|
}, [applyCourseId, applyCourses, courses]);
|
||||||
|
|
||||||
|
const selectedPlatformCourseId = useMemo(() => {
|
||||||
|
if (matchingPlatformCourses.length === 0) return '';
|
||||||
|
return matchingPlatformCourses[0].id;
|
||||||
|
}, [matchingPlatformCourses]);
|
||||||
|
|
||||||
const filteredTemplates = useMemo(() => {
|
const filteredTemplates = useMemo(() => {
|
||||||
if (!searchTerm.trim()) return templates;
|
if (!searchTerm.trim()) return templates;
|
||||||
const q = searchTerm.toLowerCase();
|
const q = searchTerm.toLowerCase();
|
||||||
@@ -120,8 +173,8 @@ export default function TestTemplateManager({ onSelectTemplate, onCreateTemplate
|
|||||||
setShowApplyModal(true);
|
setShowApplyModal(true);
|
||||||
setApplyPlanId('');
|
setApplyPlanId('');
|
||||||
setApplyCourseId('');
|
setApplyCourseId('');
|
||||||
setSelectedCourse('');
|
|
||||||
setSelectedLesson('');
|
setSelectedLesson('');
|
||||||
|
setApplyCoursesError('');
|
||||||
|
|
||||||
cmsApi.getCourses()
|
cmsApi.getCourses()
|
||||||
.then(setCourses)
|
.then(setCourses)
|
||||||
@@ -129,8 +182,8 @@ export default function TestTemplateManager({ onSelectTemplate, onCreateTemplate
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleApplyToLesson = async () => {
|
const handleApplyToLesson = async () => {
|
||||||
if (!selectedTemplate || !selectedLesson) {
|
if (!selectedTemplate || !selectedPlatformCourseId || !selectedLesson) {
|
||||||
alert('Selecciona una lección');
|
alert('Selecciona plan, curso y lección');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,7 +466,6 @@ export default function TestTemplateManager({ onSelectTemplate, onCreateTemplate
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setApplyPlanId(e.target.value ? Number(e.target.value) : '');
|
setApplyPlanId(e.target.value ? Number(e.target.value) : '');
|
||||||
setApplyCourseId('');
|
setApplyCourseId('');
|
||||||
setSelectedCourse('');
|
|
||||||
setSelectedLesson('');
|
setSelectedLesson('');
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
||||||
@@ -432,7 +484,6 @@ export default function TestTemplateManager({ onSelectTemplate, onCreateTemplate
|
|||||||
value={applyCourseId}
|
value={applyCourseId}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setApplyCourseId(e.target.value ? Number(e.target.value) : '');
|
setApplyCourseId(e.target.value ? Number(e.target.value) : '');
|
||||||
setSelectedCourse('');
|
|
||||||
setSelectedLesson('');
|
setSelectedLesson('');
|
||||||
}}
|
}}
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
||||||
@@ -442,33 +493,32 @@ export default function TestTemplateManager({ onSelectTemplate, onCreateTemplate
|
|||||||
<option key={c.idCursos} value={c.idCursos}>{c.NombreCurso}</option>
|
<option key={c.idCursos} value={c.idCursos}>{c.NombreCurso}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
{applyCoursesError && (
|
||||||
|
<p className="text-xs text-red-600 mt-1">{applyCoursesError}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{applyCourseId && (
|
{applyCourseId && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">3. Curso en la Plataforma</label>
|
<label className="block text-sm font-medium text-gray-700 mb-1">3. Curso destino detectado</label>
|
||||||
<select
|
{matchingPlatformCourses.length > 0 ? (
|
||||||
value={selectedCourse}
|
<div className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-gray-50 text-sm text-gray-700">
|
||||||
onChange={(e) => {
|
{matchingPlatformCourses[0].title}
|
||||||
setSelectedCourse(e.target.value);
|
</div>
|
||||||
setSelectedLesson('');
|
) : (
|
||||||
}}
|
<div className="w-full px-3 py-2 border border-red-200 rounded-lg bg-red-50 text-sm text-red-700">
|
||||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
|
No se encontró un curso de plataforma compatible para este curso SAM.
|
||||||
>
|
</div>
|
||||||
<option value="">Selecciona el curso destino...</option>
|
)}
|
||||||
{courses.map((course) => (
|
|
||||||
<option key={course.id} value={course.id}>{course.title}</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{selectedCourse && (
|
{selectedPlatformCourseId && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-1">4. Lección</label>
|
<label className="block text-sm font-medium text-gray-700 mb-1">4. Lección</label>
|
||||||
<LessonSelector
|
<LessonSelector
|
||||||
courseId={selectedCourse}
|
courseId={selectedPlatformCourseId}
|
||||||
selectedLesson={selectedLesson}
|
selectedLesson={selectedLesson}
|
||||||
onSelect={setSelectedLesson}
|
onSelect={setSelectedLesson}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1154,17 +1154,73 @@ export const questionBankApi = {
|
|||||||
apiFetch(`/question-bank/${id}`, { method: 'DELETE' }, false),
|
apiFetch(`/question-bank/${id}`, { method: 'DELETE' }, false),
|
||||||
importFromMySQL: (courseId?: number, questionIds?: number[], importAll?: boolean): Promise<QuestionBank[]> =>
|
importFromMySQL: (courseId?: number, questionIds?: number[], importAll?: boolean): Promise<QuestionBank[]> =>
|
||||||
apiFetch('/question-bank/import-mysql', { method: 'POST', body: JSON.stringify({ mysql_course_id: courseId, question_ids: questionIds, import_all: importAll }) }, false),
|
apiFetch('/question-bank/import-mysql', { method: 'POST', body: JSON.stringify({ mysql_course_id: courseId, question_ids: questionIds, import_all: importAll }) }, false),
|
||||||
getMySQLPlans: (): Promise<MySqlPlan[]> =>
|
getMySQLPlans: async (): Promise<MySqlPlan[]> => {
|
||||||
apiFetch('/question-bank/mysql-plans', {}, false),
|
const plans = (await apiFetch('/question-bank/mysql-plans', {}, false)) as MySqlPlanRaw[];
|
||||||
getMySQLCoursesByPlan: (planId: number): Promise<MySqlCourse[]> =>
|
return plans.reduce((acc: MySqlPlan[], p: MySqlPlanRaw) => {
|
||||||
apiFetch(`/question-bank/mysql-courses?plan_id=${planId}`, {}, false),
|
const idPlanDeEstudios = p.idPlanDeEstudios ?? p.id_plan_de_estudios;
|
||||||
|
const nombrePlan = p.NombrePlan ?? p.nombre_plan;
|
||||||
|
if (typeof idPlanDeEstudios === 'number' && typeof nombrePlan === 'string' && nombrePlan.trim()) {
|
||||||
|
acc.push({ idPlanDeEstudios, NombrePlan: nombrePlan });
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
},
|
||||||
|
getMySQLCoursesByPlan: async (planId: number): Promise<MySqlCourse[]> => {
|
||||||
|
const courses = (await apiFetch(`/question-bank/mysql-courses?plan_id=${planId}`, {}, false)) as MySqlCourseRaw[];
|
||||||
|
return courses.reduce((acc: MySqlCourse[], c: MySqlCourseRaw) => {
|
||||||
|
const idCursos = c.idCursos ?? c.id_cursos;
|
||||||
|
const nombreCurso = c.NombreCurso ?? c.nombre_curso;
|
||||||
|
const idPlanDeEstudios = c.idPlanDeEstudios ?? c.id_plan_de_estudios;
|
||||||
|
const nombrePlan = c.NombrePlan ?? c.nombre_plan;
|
||||||
|
if (
|
||||||
|
typeof idCursos === 'number' &&
|
||||||
|
typeof nombreCurso === 'string' &&
|
||||||
|
nombreCurso.trim() &&
|
||||||
|
typeof idPlanDeEstudios === 'number' &&
|
||||||
|
typeof nombrePlan === 'string' &&
|
||||||
|
nombrePlan.trim()
|
||||||
|
) {
|
||||||
|
acc.push({
|
||||||
|
idCursos,
|
||||||
|
NombreCurso: nombreCurso,
|
||||||
|
NivelCurso: c.NivelCurso ?? c.nivel_curso,
|
||||||
|
idPlanDeEstudios,
|
||||||
|
NombrePlan: nombrePlan,
|
||||||
|
Duracion: c.Duracion ?? c.duracion,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface MySqlPlanRaw {
|
||||||
|
idPlanDeEstudios?: number;
|
||||||
|
NombrePlan?: string;
|
||||||
|
id_plan_de_estudios?: number;
|
||||||
|
nombre_plan?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MySqlPlan {
|
export interface MySqlPlan {
|
||||||
idPlanDeEstudios: number;
|
idPlanDeEstudios: number;
|
||||||
NombrePlan: string;
|
NombrePlan: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MySqlCourseRaw {
|
||||||
|
idCursos?: number;
|
||||||
|
NombreCurso?: string;
|
||||||
|
NivelCurso?: number;
|
||||||
|
idPlanDeEstudios?: number;
|
||||||
|
NombrePlan?: string;
|
||||||
|
Duracion?: number;
|
||||||
|
id_cursos?: number;
|
||||||
|
nombre_curso?: string;
|
||||||
|
nivel_curso?: number;
|
||||||
|
id_plan_de_estudios?: number;
|
||||||
|
nombre_plan?: string;
|
||||||
|
duracion?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MySqlCourse {
|
export interface MySqlCourse {
|
||||||
idCursos: number;
|
idCursos: number;
|
||||||
NombreCurso: string;
|
NombreCurso: string;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user