From 46545d44a862557d46e68b5468c43bd25a3bbe9a Mon Sep 17 00:00:00 2001
From: elmer-20 <80175046+elmer-20@users.noreply.github.com>
Date: Fri, 20 Feb 2026 14:35:14 -0500
Subject: [PATCH] test_v2
---
back/app/Http/Controllers/WebController.php | 146 ++--
back/routes/api.php | 63 +-
.../WebPageSections/ProcessSection.vue | 3 +-
.../administrador/cursos/MarkdownLatex.vue | 10 +-
front/src/views/postulante/Dashboard.vue | 544 +++++-------
front/src/views/postulante/PortalView.vue | 69 +-
.../src/views/postulante/PreguntasExamen.vue | 791 ++++++++----------
front/src/views/postulante/Resultados.vue | 483 +++++++----
front/src/views/postulante/Test.vue | 175 ++--
9 files changed, 1108 insertions(+), 1176 deletions(-)
diff --git a/back/app/Http/Controllers/WebController.php b/back/app/Http/Controllers/WebController.php
index 4a793b7..9b7ca21 100644
--- a/back/app/Http/Controllers/WebController.php
+++ b/back/app/Http/Controllers/WebController.php
@@ -1,70 +1,100 @@
with([
+ 'detalles' => function ($query) {
+ $query->select(
+ 'id',
+ 'proceso_admision_id',
+ 'tipo',
+ 'titulo_detalle',
+ 'descripcion',
+ 'listas',
+ 'meta',
+ 'url',
+ 'imagen_path',
+ 'imagen_path_2',
+ 'created_at',
+ 'updated_at'
+ );
+ }
+ ])
+ ->latest()
+ ->get();
-public function GetProcesoAdmision()
-{
- $procesos = ProcesoAdmision::select(
- 'id',
- 'titulo',
- 'subtitulo',
- 'descripcion',
- 'slug',
- 'tipo_proceso',
- 'modalidad',
- 'publicado',
- 'fecha_publicacion',
- 'fecha_inicio_preinscripcion',
- 'fecha_fin_preinscripcion',
- 'fecha_inicio_inscripcion',
- 'fecha_fin_inscripcion',
- 'fecha_examen1',
- 'fecha_examen2',
- 'fecha_resultados',
- 'fecha_inicio_biometrico',
- 'fecha_fin_biometrico',
- 'imagen_path',
- 'banner_path',
- 'brochure_path',
- 'link_preinscripcion',
- 'link_inscripcion',
- 'link_resultados',
- 'link_reglamento',
- 'estado',
- 'created_at',
- 'updated_at'
- )
- ->with([
- 'detalles' => function ($query) {
- $query->select(
- 'id',
- 'proceso_admision_id',
- 'tipo',
- 'titulo_detalle',
- 'descripcion',
- 'listas',
- 'meta',
- 'url',
- 'imagen_path',
- 'imagen_path_2',
- 'created_at',
- 'updated_at'
- );
- }
- ])
- ->latest() // 🔥 Esto ordena por created_at DESC
- ->get();
+ return response()->json([
+ 'success' => true,
+ 'data' => $procesos
+ ]);
+ }
- return response()->json([
- 'success' => true,
- 'data' => $procesos
- ]);
-}
+ public function obtenerProcesosDisponiblesPreinscripcion(Request $request)
+ {
+ $now = Carbon::now();
+ $postulante = $request->user();
+ $procesos = ProcesoAdmision::query()
+ ->select([
+ 'id',
+ 'titulo',
+ 'slug',
+ 'link_preinscripcion',
+ 'fecha_inicio_preinscripcion',
+ 'fecha_fin_preinscripcion',
+ ])
+ ->where('publicado', 1)
+ ->whereIn('estado', ['publicado', 'en_proceso'])
+ ->whereNotNull('link_preinscripcion')
+ ->whereNotNull('fecha_inicio_preinscripcion')
+ ->whereNotNull('fecha_fin_preinscripcion')
+ ->where('fecha_inicio_preinscripcion', '<=', $now)
+ ->where('fecha_fin_preinscripcion', '>=', $now)
+ ->orderByDesc('fecha_inicio_preinscripcion')
+ ->orderBy('titulo')
+ ->get();
+ return response()->json([
+ 'success' => true,
+ 'data' => $procesos
+ ]);
+ }
}
diff --git a/back/routes/api.php b/back/routes/api.php
index f9f913a..e789920 100644
--- a/back/routes/api.php
+++ b/back/routes/api.php
@@ -52,12 +52,9 @@ Route::middleware(['auth:sanctum'])->prefix('admin')->group(function () {
Route::put('/areas/{id}', [AreaController::class, 'update']);
Route::delete('/areas/{id}', [AreaController::class, 'destroy']);
Route::patch('/areas/{id}/toggle', [AreaController::class, 'toggleEstado']);
-
Route::post('/areas/{area}/vincular-cursos', [AreaController::class, 'vincularCursosArea']);
Route::post('/areas/{area}/desvincular-curso', [AreaController::class, 'desvincularCursoArea']);
Route::get('/areas/{area}/cursos-disponibles', [AreaController::class, 'getCursosPorArea']);
-
-
Route::post('areas/{area}/vincular-procesos', [AreaController::class, 'vincularProcesosArea']);
Route::get('areas/{area}/procesos-disponibles', [AreaController::class, 'getProcesosPorArea'] );
Route::post('areas/{area}/desvincular-procesos', [AreaController::class, 'desvincularProcesoArea'] );
@@ -67,19 +64,14 @@ Route::middleware(['auth:sanctum'])->prefix('admin')->group(function () {
Route::middleware(['auth:sanctum'])->prefix('admin')->group(function () {
- // NOTICIAS
Route::get('/noticias', [NoticiaController::class, 'index']);
Route::get('/noticias/{noticia}', [NoticiaController::class, 'show']);
-
Route::post('/noticias', [NoticiaController::class, 'store']);
-
- // usa SOLO UNA (PUT o PATCH). Aquí dejo PUT:
Route::put('/noticias/{noticia}', [NoticiaController::class, 'update']);
-
Route::delete('/noticias/{noticia}', [NoticiaController::class, 'destroy']);
});
-Route::get('/noticias', [NoticiaController::class, 'index']);
-Route::get('/noticias/{noticia:slug}', [NoticiaController::class, 'showPublic']);
+
+
Route::middleware(['auth:sanctum'])->prefix('admin')->group(function () {
@@ -99,8 +91,6 @@ Route::middleware(['auth:sanctum'])->prefix('admin')->group(function () {
});
-
-
Route::middleware(['auth:sanctum'])->prefix('admin')->group(function () {
Route::get('cursos/{cursoId}/preguntas', [PreguntaController::class, 'getPreguntasCurso']);
@@ -113,7 +103,6 @@ Route::middleware(['auth:sanctum'])->prefix('admin')->group(function () {
Route::middleware('auth:sanctum')->group(function () {
-
Route::get('/calificaciones', [CalificacionController::class, 'index']);
Route::post('/calificaciones', [CalificacionController::class, 'store']);
Route::get('/calificaciones/{id}', [CalificacionController::class, 'show']);
@@ -122,17 +111,11 @@ Route::middleware('auth:sanctum')->group(function () {
});
-
-
Route::prefix('postulante')->group(function () {
- // Registro
Route::post('/register', [PostulanteAuthController::class, 'register']);
-
- // Login
Route::post('/login', [PostulanteAuthController::class, 'login']);
- // Rutas protegidas por token
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [PostulanteAuthController::class, 'logout']);
Route::get('/me', [PostulanteAuthController::class, 'me']);
@@ -142,10 +125,8 @@ Route::prefix('postulante')->group(function () {
});
-
});
-
// Route::middleware('auth:sanctum')->group(function () {
// Route::get('/procesos', [ExamenController::class, 'procesoexamen']);
// Route::get('/areas', [ExamenController::class, 'areas']);
@@ -157,51 +138,35 @@ Route::prefix('postulante')->group(function () {
Route::middleware(['auth:sanctum'])->prefix('area-proceso')->group(function () {
Route::get('areasprocesos', [ReglaAreaProcesoController::class, 'areasProcesos']);
Route::prefix('{areaProcesoId}/reglas')->group(function () {
- Route::get('/', [ReglaAreaProcesoController::class, 'index']); // Listar reglas
- Route::post('/', [ReglaAreaProcesoController::class, 'store']); // Crear/actualizar regla individual
- Route::post('/multiple', [ReglaAreaProcesoController::class, 'storeMultiple']); // Guardar múltiples reglas
+ Route::get('/', [ReglaAreaProcesoController::class, 'index']);
+ Route::post('/', [ReglaAreaProcesoController::class, 'store']);
+ Route::post('/multiple', [ReglaAreaProcesoController::class, 'storeMultiple']);
});
});
Route::middleware(['auth:sanctum'])->prefix('reglas')->group(function () {
- Route::put('/{reglaId}', [ReglaAreaProcesoController::class, 'update']); // Editar regla
- Route::delete('/{reglaId}', [ReglaAreaProcesoController::class, 'destroy']); // Eliminar regla
+ Route::put('/{reglaId}', [ReglaAreaProcesoController::class, 'update']);
+ Route::delete('/{reglaId}', [ReglaAreaProcesoController::class, 'destroy']);
});
-
-// Examen - Flujo separado
-Route::middleware(['auth:postulante'])->group(function () {
+Route::middleware(['auth:sanctum'])->group(function () {
Route::get('/examen/procesos', [ExamenController::class, 'procesoexamen']);
Route::get('/examen/areas', [ExamenController::class, 'areas']);
Route::get('/examen/actual', [ExamenController::class, 'miExamenActual']);
-
- // Crear examen (sin preguntas)
Route::post('/examen/crear', [ExamenController::class, 'crearExamen']);
-
- // Generar preguntas
Route::post('/examen/{examen}/generar-preguntas', [ExamenController::class, 'generarPreguntas']);
-
- // Obtener preguntas
Route::get('/examen/{examen}/preguntas', [ExamenController::class, 'obtenerPreguntas']);
-
- // Iniciar examen (marcar hora inicio)
Route::post('/examen/iniciar', [ExamenController::class, 'iniciarExamen']);
-
- // Responder preguntas
Route::post('/examen/pregunta/{pregunta}/responder', [ExamenController::class, 'responderPregunta']);
-
- // Finalizar examen
Route::post('/examen/{examen}/finalizar', [ExamenController::class, 'finalizarExamen']);
Route::post('/examen/{examenId}/calificar', [ExamenController::class, 'calificarExamen']);
});
+Route::middleware('auth:sanctum')->prefix('admin')->group(function () {
- Route::middleware('auth:sanctum')->prefix('admin')->group(function () {
-
- // PROCESOS
Route::prefix('procesos-admision')->group(function () {
Route::get('/', [ProcesoAdmisionController::class, 'index'])->name('procesos-admision.index');
Route::post('/', [ProcesoAdmisionController::class, 'store'])->name('procesos-admision.store');
@@ -219,8 +184,16 @@ Route::middleware(['auth:postulante'])->group(function () {
Route::delete('/{id}', [ProcesoAdmisionDetalleController::class, 'destroy'])->name('detalles-admision.destroy');
});
- });
+});
+
+ Route::middleware('auth:sanctum')->group(function () {
+
+ Route::get('/procesos-disponibles-preinscripcion', [WebController::class, 'obtenerProcesosDisponiblesPreinscripcion']);
+});
+
Route::get('/procesos-admision', [WebController::class, 'GetProcesoAdmision']);
+Route::get('/noticias', [NoticiaController::class, 'index']);
+Route::get('/noticias/{noticia:slug}', [NoticiaController::class, 'showPublic']);
\ No newline at end of file
diff --git a/front/src/components/WebPageSections/ProcessSection.vue b/front/src/components/WebPageSections/ProcessSection.vue
index b5aab08..bdd755c 100644
--- a/front/src/components/WebPageSections/ProcessSection.vue
+++ b/front/src/components/WebPageSections/ProcessSection.vue
@@ -54,11 +54,10 @@
- Tip: Si ves “🟢” en una fecha, significa que esa etapa está activa hoy.
+ Si ves “🟢” en una fecha, significa que esa etapa está activa hoy.
-
2) ¿Qué debo hacer ahora?
diff --git a/front/src/views/administrador/cursos/MarkdownLatex.vue b/front/src/views/administrador/cursos/MarkdownLatex.vue
index 715da1c..e4b6c65 100644
--- a/front/src/views/administrador/cursos/MarkdownLatex.vue
+++ b/front/src/views/administrador/cursos/MarkdownLatex.vue
@@ -122,11 +122,11 @@ onMounted(() => {
}
.markdown-content :deep(.katex-display) {
- margin: 0.75em 0;
- text-align: center;
-
- overflow-x: auto; /* si es largo, que haga scroll horizontal */
- overflow-y: hidden; /* IMPORTANTE: no scroll vertical -> no flechas */
+ margin: 14px 0; /* espacio arriba/abajo */
+ padding: 10px 0; /* aire dentro del bloque */
+ line-height: 1.25; /* evita que el contenedor aplaste */
+ overflow-x: auto;
+ overflow-y: visible; /* ✅ CLAVE: que NO recorte arriba/abajo */
}
/* Quitar botones/flechas del scrollbar (Chrome/Edge) */
.markdown-content :deep(.katex-display::-webkit-scrollbar-button) {
diff --git a/front/src/views/postulante/Dashboard.vue b/front/src/views/postulante/Dashboard.vue
index 7d49148..7bbe0eb 100644
--- a/front/src/views/postulante/Dashboard.vue
+++ b/front/src/views/postulante/Dashboard.vue
@@ -1,122 +1,118 @@
-
-
-
-
-
-
-
Bienvenido, {{ authStore.userName }}
-
DNI: {{ authStore.userDni || "No registrado" }}
-
-
+
+
+
+
+
+
Bienvenido, {{ authStore.userName }}
+
DNI: {{ authStore.userDni || "No registrado" }}
+
-
-
-
-
-
-
-
-
Tu test diagnóstico
-
- Es un test referencial para practicar y medir tu nivel. No afecta tu postulación oficial.
-
+
-
-
-
-
- 10 preguntas • 10 min aprox. • resultado inmediato
-
-
-
-
- Si tu proceso pide secuencia, también puedes usar la del pago de tu
- Carpeta de Postulante (no pagas extra por este test).
-
-
-
-
+
+
+
+
+
Tu test diagnóstico
+
+ Es un test referencial para practicar y medir tu nivel. No afecta tu postulación oficial.
-
-
- Ir al test
-
-
-
Aún no tienes un test asignado.
+
+
+
+
+ 10 preguntas • 10 min aprox. • resultado inmediato
+
+
+
+
+ Si tu proceso pide secuencia, también puedes usar la del pago de tu
+ Carpeta de Postulante (no pagas extra por este test).
+
+
+
-
-
-
-
-
-
-
Procesos activos
-
Revisa los procesos habilitados y postula cuando corresponda.
-
+
+
+ Ir al test
+
-
+
Aún no tienes un test asignado.
+
+
-
-
-
-
- {{ record.statusText }}
-
-
-
-
-
-
-
-
-
-
- {{ record.blockReason }}
-
-
-
-
-
+
+
+
+
+
+
Procesos activos
+
Revisa los procesos habilitados y postula cuando corresponda.
-
+
+
+
+
+
+
+
+ {{ formatDate(record.fecha_inicio_preinscripcion) }}
+
+
+
+ {{ formatDate(record.fecha_fin_preinscripcion) }}
+
+
+
+
+
+
+
+
+
+
+
@@ -125,10 +121,7 @@ import { computed, onMounted, reactive, ref } from "vue";
import { Modal, message } from "ant-design-vue";
import { useRouter } from "vue-router";
import { useAuthStore } from "../../store/postulanteStore";
-
-
-const ROUTE_TEST_PANEL = { name: "PanelTest" };
-const ROUTE_PROCESS_DETAIL = (id) => ({ name: "ProcesoDetalle", params: { id } });
+import axios from "../../axiosPostulante";
const router = useRouter();
const authStore = useAuthStore();
@@ -143,56 +136,35 @@ const state = reactive({
const canGoTest = computed(() => !!state.test.hasAssigned);
const processColumns = computed(() => [
- { title: "Proceso", dataIndex: "name", key: "name", ellipsis: true },
- { title: "Estado", key: "status", responsive: ["sm"] },
- { title: "Aptitud", key: "eligibility", responsive: ["md"] },
+ { title: "Título", dataIndex: "titulo", key: "titulo", ellipsis: true },
+ { title: "Inicio preinscripción", dataIndex: "fecha_inicio_preinscripcion", key: "fecha_inicio_preinscripcion" },
+ { title: "Fin preinscripción", dataIndex: "fecha_fin_preinscripcion", key: "fecha_fin_preinscripcion" },
{ title: "Acciones", key: "actions" },
]);
+function formatDate(val) {
+ if (!val) return "-";
+ if (typeof val === "string" && val.includes("T")) return val.split("T")[0];
+ const d = new Date(val);
+ if (isNaN(d.getTime())) return String(val);
+ return d.toLocaleDateString();
+}
const api = {
- async getDashboard() {
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve({
- test: { hasAssigned: true },
- processes: [
- {
- id: 10,
- name: "Admisión 2026-I",
- statusText: "ABIERTO",
- statusColor: "blue",
- isEligible: true,
- canApply: true,
- blockReason: "",
- },
- {
- id: 11,
- name: "Admisión Extraordinaria 2026",
- statusText: "PRONTO",
- statusColor: "gold",
- isEligible: false,
- canApply: false,
- blockReason: "Este proceso aún no permite postular.",
- },
- ],
- });
- }, 250);
- });
- },
- async applyToProcess(processId) {
- return new Promise((resolve) => setTimeout(resolve, 300));
+ async getProcesosActivos() {
+ const { data } = await axios.get("/procesos-disponibles-preinscripcion");
+ return data;
},
};
async function fetchDashboard() {
loading.value = true;
try {
- const data = await api.getDashboard();
- state.test = { ...state.test, ...data.test };
- state.processes = Array.isArray(data.processes) ? data.processes : [];
- } catch {
- message.error("No se pudo cargar el dashboard.");
+ const resp = await api.getProcesosActivos();
+ state.processes = Array.isArray(resp?.data) ? resp.data : [];
+ } catch (e) {
+ console.error(e);
+ state.processes = [];
} finally {
loading.value = false;
}
@@ -212,91 +184,33 @@ function goToTest() {
});
}
-function onViewProcess(process) {
-
- message.info(`Abrir detalle del proceso: ${process.name}`);
-}
-
function onApply(process) {
- if (!process.canApply) return;
-
- Modal.confirm({
- title: "Confirmar postulación",
- content: `¿Deseas postular al proceso "${process.name}"?`,
- okText: "Postular",
- cancelText: "Cancelar",
- async onOk() {
- try {
- await api.applyToProcess(process.id);
- message.success("Postulación registrada.");
- await fetchDashboard();
- } catch {
- message.error("No se pudo completar la postulación.");
- }
- },
- });
+ if (!process.link_preinscripcion) return;
+ window.open(process.link_preinscripcion, "_blank", "noopener,noreferrer");
}
onMounted(fetchDashboard);
+
\ No newline at end of file
diff --git a/front/src/views/postulante/PortalView.vue b/front/src/views/postulante/PortalView.vue
index fecca08..c4052cf 100644
--- a/front/src/views/postulante/PortalView.vue
+++ b/front/src/views/postulante/PortalView.vue
@@ -690,23 +690,16 @@ onUnmounted(() => {
height: 100%;
}
-@media (max-width: 992px) {
- .main-layout,
- .main-layout.layout-collapsed {
- margin-left: 0 !important;
- }
-
- .content-container {
- max-width: 100%;
- }
-}
-
@media (max-width: 768px) {
.header,
.header-container {
height: 56px;
}
+ .header-container {
+ padding: 0 12px; /* ✅ menos padding lateral en móvil */
+ }
+
.sidebar,
.sidebar.sidebar-mobile {
top: 56px;
@@ -714,8 +707,8 @@ onUnmounted(() => {
}
.logo-img {
- height: 36px;
- width: 36px;
+ height: 34px;
+ width: 34px;
}
.portal-title {
@@ -726,12 +719,58 @@ onUnmounted(() => {
display: none;
}
+ /* ✅ CONTENT: padding externo pequeño (se ve pro y no encoge tanto) */
.content {
- padding: 12px;
+ padding: 8px !important;
+ background: var(--ant-colorBgLayout, #f5f5f5);
}
+ .content-container {
+ max-width: 100% !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ }
+
+ /* ✅ CARD: sin borde/sombra pesada en móvil y radio moderado */
.content-card {
- border-radius: 14px;
+ border-radius: 12px !important;
+ margin: 0 !important;
+ border: 1px solid var(--ant-colorBorderSecondary, rgba(0,0,0,.06)) !important;
+ box-shadow: 0 6px 16px rgba(0,0,0,.06) !important;
+ background: #fff;
+ overflow: hidden;
+ }
+
+ /* ✅ CLAVE: padding interno del body (no exagerado) */
+ .content-card :deep(.ant-card-body) {
+ padding: 12px !important;
+ }
+
+ /* ✅ opcional: quita el “papel cuadriculado” en móvil (se ve más limpio) */
+ .content-card::before {
+ display: none !important;
+ }
+}
+
+/* Extra: teléfonos chicos (más pro aún) */
+@media (max-width: 480px) {
+ .content {
+ padding: 6px !important;
+ }
+ .content-card :deep(.ant-card-body) {
+ padding: 10px !important;
+ }
+}
+/* ===== FIX MÓVIL REAL ===== */
+@media (max-width: 992px) {
+ .main-layout,
+ .main-layout.layout-collapsed {
+ margin-left: 0 !important;
+ }
+
+ .sidebar {
+ position: fixed !important;
+ left: 0;
}
}
\ No newline at end of file
diff --git a/front/src/views/postulante/PreguntasExamen.vue b/front/src/views/postulante/PreguntasExamen.vue
index 7ae9a8d..d3cef05 100644
--- a/front/src/views/postulante/PreguntasExamen.vue
+++ b/front/src/views/postulante/PreguntasExamen.vue
@@ -1,14 +1,10 @@
-
+
-
- {{ examenInfo.proceso || "Proceso no disponible" }}
-
-
{{ examenInfo.area || "Área no disponible" }}
•
@@ -22,7 +18,6 @@
Intentos: {{ examenInfo.intentos }} / {{ examenInfo.intentos_maximos }}
-
Progreso: {{ preguntasRespondidas }} guardadas de {{ totalPreguntas }}
@@ -30,13 +25,20 @@
-
Tiempo restante
-
+
+
+ {{ porcentajeCompletado === 100 ? "Listo para finalizar" : "En progreso" }}
+
+
+
@@ -64,46 +66,59 @@
-
+
-
-
-
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
- {{ getLetraOpcion(op.key) }})
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+ {{ getLetraOpcion(op.key) }})
+
+
+
+
+
+
+
@@ -116,13 +131,9 @@
-
+
-
+
Atrás
@@ -136,7 +147,7 @@
-
+
Al presionar Siguiente, tu respuesta se guarda. Evita recargar o cerrar durante el examen.
@@ -159,264 +170,12 @@
-
-
-
+
\ No newline at end of file
diff --git a/front/src/views/postulante/Resultados.vue b/front/src/views/postulante/Resultados.vue
index 50bef88..de72e26 100644
--- a/front/src/views/postulante/Resultados.vue
+++ b/front/src/views/postulante/Resultados.vue
@@ -1,169 +1,171 @@
-
+
-
-
-
-
Resultados del examen
-
Resumen claro + detalle por curso.
-
-
-
-
- Volver
-
- Actualizar
-
-
-
-
-
+
-
-
-
-
-
-
-
- Calificado
-
-
-
- Puesto: #{{ resultado.orden_merito }}
-
-
-
- {{ fmt2(resultado.total_puntos) }}
- Puntaje total
-
-
-
-
-
-
-
-
+
+
+
+
+
+
Resultados del examen
+
Resumen claro + detalle por curso.
+
+
+
+
+ Volver
+
+ Actualizar
+
+
+
+
+
+
-
-
- Correctas
- {{ resultado.total_correctas }}
- Aciertos
-
-
+
+
+
+
+
+ Calificado
+
-
-
- Incorrectas
- {{ resultado.total_incorrectas }}
- Errores
-
-
+
+ Puesto: #{{ resultado.orden_merito }}
+
+
+
+ {{ fmt2(resultado.total_puntos) }}
+ Puntaje total
-
-
- Blanco/Nulas
- {{ resultado.total_nulas }}
- Sin respuesta
+
+
+
-
-
-
-
-
% Correctas
-
{{ fmt2(resultado.porcentaje_correctas) }}%
-
+
+
+
+
+ Correctas
+ {{ resultado.total_correctas }}
+ Aciertos
+
+
+
+
+
+ Incorrectas
+ {{ resultado.total_incorrectas }}
+ Errores
+
+
+
+
+
+ Blanco/Nulas
+ {{ resultado.total_nulas }}
+ Sin respuesta
+
+
+
+
+
+
+
+
% Correctas
+
{{ fmt2(resultado.porcentaje_correctas) }}%
+
-
-
-
-
{{ fmt2(resultado.calificacion_sobre_20) }}
-
/20
+
+
+
+
{{ fmt2(resultado.calificacion_sobre_20) }}
+
/20
+
+
-
-
-
+
+
+
+
+
+ Nota sobre 20
+ {{ fmt2(resultado.calificacion_sobre_20) }}/20
+ Equivalencia según puntaje máximo del proceso
+
+
+
+
-
-
- Nota sobre 20
- {{ fmt2(resultado.calificacion_sobre_20) }}/20
- Equivalencia según puntaje máximo del proceso
+
+
+
+
+
+
+
+
+
+
+
{{ record.curso }}
+
{{ record.ratio }}%
+
+
+
+
+
+ {{ record.correctas }}
+
+
+
+ {{ record.incorrectas }}
+
+
+
+ {{ record.total }}
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ record.curso }}
-
{{ record.ratio }}%
-
-
-
-
-
- {{ record.correctas }}
-
-
-
- {{ record.incorrectas }}
-
-
-
- {{ record.total }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Calcular resultados
-
-
-
+
+
+
+
+
+ Calcular resultados
+
+
+
+
+
@@ -242,15 +244,17 @@ const rowsCursos = computed(() => {
const rows = Array.from(keys).map((k) => {
const correctas = parseCorrectas(corr?.[k] ?? 0)
- const total = parseTotal(k) || (() => {
- const val = corr?.[k]
- if (typeof val === 'string' && val.includes('de')) {
- const [, b] = val.split('de')
- const y = Number(b.trim())
- return Number.isFinite(y) ? y : 0
- }
- return 0
- })()
+ const total =
+ parseTotal(k) ||
+ (() => {
+ const val = corr?.[k]
+ if (typeof val === 'string' && val.includes('de')) {
+ const [, b] = val.split('de')
+ const y = Number(b.trim())
+ return Number.isFinite(y) ? y : 0
+ }
+ return 0
+ })()
const incorrectas = parseIncorrectas(k)
const ratio = total > 0 ? Math.round((correctas / total) * 100) : 0
return { curso: k, correctas, incorrectas, total, ratio }
@@ -262,7 +266,6 @@ const rowsCursos = computed(() => {
const recalcular = async () => {
if (!examenId.value) return
-
cargando.value = true
try {
const r = await examenStore.calificarExamen(examenId.value)
@@ -286,17 +289,58 @@ onMounted(async () => {
+
\ No newline at end of file
diff --git a/front/src/views/postulante/Test.vue b/front/src/views/postulante/Test.vue
index 2c32c19..cfb24fd 100644
--- a/front/src/views/postulante/Test.vue
+++ b/front/src/views/postulante/Test.vue
@@ -550,10 +550,10 @@ No se realiza ningún pago adicional.
+ .heroTitle {
+ font-size: 1.55rem;
+ }
+
+ .ctaBtn {
+ height: 48px;
+ }
+}
+
\ No newline at end of file