|
|
|
|
@ -10,25 +10,16 @@ import voucherBn from "../../assets/images/boletabn.jpg";
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
const examenStore = useExamenStore();
|
|
|
|
|
|
|
|
|
|
/** =========================
|
|
|
|
|
* UI State
|
|
|
|
|
* ========================= */
|
|
|
|
|
const showModal = ref(false);
|
|
|
|
|
const iniciandoExamen = ref(false);
|
|
|
|
|
const creandoExamen = ref(false);
|
|
|
|
|
const formRef = ref(null);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ✅ SEPARA LOADINGS:
|
|
|
|
|
* - loadingPage: solo para cargar data inicial de la página (no debe tumbar modales)
|
|
|
|
|
* - loadingAreas: solo para cargar áreas cuando cambias de proceso (se usa en el select)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
const loadingPage = ref(false);
|
|
|
|
|
const loadingAreas = ref(false);
|
|
|
|
|
|
|
|
|
|
/** =========================
|
|
|
|
|
* Form
|
|
|
|
|
* ========================= */
|
|
|
|
|
|
|
|
|
|
const formState = reactive({
|
|
|
|
|
proceso_id: undefined,
|
|
|
|
|
area_proceso_id: undefined,
|
|
|
|
|
@ -36,9 +27,7 @@ const formState = reactive({
|
|
|
|
|
codigo_pago: "",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/** =========================
|
|
|
|
|
* Modal Voucher
|
|
|
|
|
* ========================= */
|
|
|
|
|
|
|
|
|
|
const secuenciaModalOpen = ref(false);
|
|
|
|
|
const secuenciaTipo = ref("caja");
|
|
|
|
|
|
|
|
|
|
@ -74,9 +63,6 @@ const openSecuencia = (tipo) => {
|
|
|
|
|
|
|
|
|
|
const modalBodyStyle = computed(() => ({ maxHeight: "72vh", overflowY: "auto" }));
|
|
|
|
|
|
|
|
|
|
/** =========================
|
|
|
|
|
* Datos (computed)
|
|
|
|
|
* ========================= */
|
|
|
|
|
const hasExamen = computed(() => !!examenStore.examenActual);
|
|
|
|
|
|
|
|
|
|
const procesoNombre = computed(() => examenStore.examenActual?.proceso?.nombre || "No asignado");
|
|
|
|
|
@ -90,7 +76,7 @@ const estadoTexto = computed(() => {
|
|
|
|
|
return yaDioTest.value ? "Completado" : "Listo para iniciar";
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/** Progreso */
|
|
|
|
|
|
|
|
|
|
const stepCurrent = computed(() => {
|
|
|
|
|
if (!hasExamen.value) return 0;
|
|
|
|
|
return yaDioTest.value ? 2 : 1;
|
|
|
|
|
@ -113,14 +99,12 @@ const estadoAlertDesc = computed(() => {
|
|
|
|
|
: "Son 10 preguntas. Al finalizar verás tu resultado al instante.";
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/** ✅ Botones separados (visibilidad) */
|
|
|
|
|
const canIniciar = computed(() => hasExamen.value && !yaDioTest.value);
|
|
|
|
|
const canVerResultados = computed(() => hasExamen.value && yaDioTest.value);
|
|
|
|
|
const openSeleccionArea = () => {
|
|
|
|
|
showModal.value = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** Options */
|
|
|
|
|
const procesoOptions = computed(() =>
|
|
|
|
|
(examenStore.procesos || []).map((p) => ({
|
|
|
|
|
value: p.id,
|
|
|
|
|
@ -142,13 +126,9 @@ const tipoPagoOptions = [
|
|
|
|
|
{ value: "caja", label: "Caja" },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ✅ IMPORTANTE:
|
|
|
|
|
* La condición de pago debe funcionar aunque venga 1/"1"/true/"true"
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
const normalizeRequierePago = (v) => v === 1 || v === "1" || v === true || v === "true";
|
|
|
|
|
|
|
|
|
|
/** Si estás eligiendo proceso en el modal */
|
|
|
|
|
const procesoRequierePago = computed(() => {
|
|
|
|
|
const pid = formState.proceso_id;
|
|
|
|
|
if (pid === undefined || pid === null || pid === "") return false;
|
|
|
|
|
@ -161,7 +141,7 @@ const procesoRequierePago = computed(() => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Validación (condicional) */
|
|
|
|
|
|
|
|
|
|
const rules = {
|
|
|
|
|
proceso_id: [{ required: true, message: "Selecciona un proceso", trigger: "change" }],
|
|
|
|
|
area_proceso_id: [{ required: true, message: "Selecciona un área", trigger: "change" }],
|
|
|
|
|
@ -187,9 +167,7 @@ const rules = {
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** =========================
|
|
|
|
|
* Actions
|
|
|
|
|
* ========================= */
|
|
|
|
|
|
|
|
|
|
const refrescar = async () => {
|
|
|
|
|
try {
|
|
|
|
|
await examenStore.fetchExamenActual();
|
|
|
|
|
@ -199,19 +177,14 @@ const refrescar = async () => {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ✅ SOLO una forma de cargar áreas: por @change
|
|
|
|
|
* (NO uses watch(proceso_id) + @change a la vez)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
const handleProcesoChange = async (procesoId) => {
|
|
|
|
|
// reset dependientes
|
|
|
|
|
|
|
|
|
|
formState.area_proceso_id = undefined;
|
|
|
|
|
|
|
|
|
|
// reset pago cuando cambia proceso
|
|
|
|
|
formState.tipo_pago = undefined;
|
|
|
|
|
formState.codigo_pago = "";
|
|
|
|
|
|
|
|
|
|
// limpiar validaciones relacionadas
|
|
|
|
|
formRef.value?.clearValidate?.(["area_proceso_id", "tipo_pago", "codigo_pago"]);
|
|
|
|
|
|
|
|
|
|
if (!procesoId) {
|
|
|
|
|
@ -236,10 +209,8 @@ const crearExamen = async () => {
|
|
|
|
|
try {
|
|
|
|
|
creandoExamen.value = true;
|
|
|
|
|
|
|
|
|
|
// ✅ valida con reglas condicionales
|
|
|
|
|
if (formRef.value) await formRef.value.validate();
|
|
|
|
|
|
|
|
|
|
// ✅ si requiere pago, se manda en plano al store (payload final lo arma el store)
|
|
|
|
|
const pagoData = procesoRequierePago.value
|
|
|
|
|
? { tipo_pago: formState.tipo_pago, codigo_pago: formState.codigo_pago }
|
|
|
|
|
: null;
|
|
|
|
|
@ -316,7 +287,6 @@ watch(showModal, (open) => {
|
|
|
|
|
if (!open) resetModal();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/** Lifecycle */
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
loadingPage.value = true;
|
|
|
|
|
try {
|
|
|
|
|
@ -329,7 +299,6 @@ onMounted(async () => {
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<!-- WRAPPER estilo Convocatorias -->
|
|
|
|
|
|
|
|
|
|
<div class="heroKicker">Test diagnóstico</div>
|
|
|
|
|
<div class="heroTitle">Practica y conoce tu nivel</div>
|
|
|
|
|
@ -368,7 +337,6 @@ onMounted(async () => {
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- BOTONES DE BANCO -->
|
|
|
|
|
<div class="bankActions">
|
|
|
|
|
<div class="bankHead">
|
|
|
|
|
<div class="bankTitle">Requisito de acceso”</div>
|
|
|
|
|
@ -447,7 +415,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
|
|
|
|
|
<a-divider />
|
|
|
|
|
|
|
|
|
|
<!-- Progreso -->
|
|
|
|
|
<section class="section">
|
|
|
|
|
<div class="sectionTitle">Tu camino</div>
|
|
|
|
|
<div class="sectionSub">Simple, rápido y sin estrés.</div>
|
|
|
|
|
@ -467,7 +434,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
|
|
|
|
|
<a-divider />
|
|
|
|
|
|
|
|
|
|
<!-- ================== MODAL SECUENCIA ================== -->
|
|
|
|
|
<a-modal
|
|
|
|
|
v-model:open="secuenciaModalOpen"
|
|
|
|
|
:title="secuenciaTitle"
|
|
|
|
|
@ -515,7 +481,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
</a-row>
|
|
|
|
|
</a-modal>
|
|
|
|
|
|
|
|
|
|
<!-- ================== MODAL SELECCIÓN ÁREA ================== -->
|
|
|
|
|
<a-modal
|
|
|
|
|
v-model:open="showModal"
|
|
|
|
|
title="Seleccionar área"
|
|
|
|
|
@ -585,9 +550,7 @@ No se realiza ningún pago adicional.
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
/* =========================
|
|
|
|
|
BASE (estilo Convocatorias)
|
|
|
|
|
========================= */
|
|
|
|
|
|
|
|
|
|
.test-modern {
|
|
|
|
|
position: relative;
|
|
|
|
|
padding: 40px 0;
|
|
|
|
|
@ -630,15 +593,13 @@ No se realiza ningún pago adicional.
|
|
|
|
|
padding: 0 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Helpers */
|
|
|
|
|
|
|
|
|
|
.mt12 { margin-top: 12px; }
|
|
|
|
|
.mt16 { margin-top: 16px; }
|
|
|
|
|
.mb12 { margin-bottom: 12px; }
|
|
|
|
|
.muted { color: #666; line-height: 1.6; }
|
|
|
|
|
|
|
|
|
|
/* =========================
|
|
|
|
|
HERO (tarjeta principal tipo convocatorias)
|
|
|
|
|
========================= */
|
|
|
|
|
|
|
|
|
|
.hero {
|
|
|
|
|
position: relative;
|
|
|
|
|
border: none;
|
|
|
|
|
@ -653,7 +614,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Badge "Principal" estilo convocatorias */
|
|
|
|
|
.hero::after {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: -12px;
|
|
|
|
|
@ -688,7 +648,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
color: #666;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Facts */
|
|
|
|
|
.heroFacts {
|
|
|
|
|
margin-top: 16px;
|
|
|
|
|
display: grid;
|
|
|
|
|
@ -719,7 +678,7 @@ No se realiza ningún pago adicional.
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Banco */
|
|
|
|
|
|
|
|
|
|
.bankActions {
|
|
|
|
|
margin-top: 18px;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
@ -751,7 +710,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* CTA */
|
|
|
|
|
.ctaCard {
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 16px;
|
|
|
|
|
@ -784,7 +742,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
line-height: 1.45;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Status pill */
|
|
|
|
|
.statusTag {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
padding: 4px 12px;
|
|
|
|
|
@ -813,7 +770,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
color: #777;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Botones */
|
|
|
|
|
.ctaBtn {
|
|
|
|
|
height: 52px;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
@ -832,7 +788,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
|
|
|
|
|
.ctaDivider { margin: 14px 0 0; }
|
|
|
|
|
|
|
|
|
|
/* Secciones inferiores */
|
|
|
|
|
.sectionTitle {
|
|
|
|
|
font-size: 1.35rem;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
@ -852,7 +807,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
color: #666;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Modales */
|
|
|
|
|
:deep(.voucher-modal .ant-modal-content),
|
|
|
|
|
:deep(.select-modal .ant-modal-content) {
|
|
|
|
|
border-radius: 16px;
|
|
|
|
|
@ -863,7 +817,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
max-width: 92vw;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Pago */
|
|
|
|
|
.pay-block {
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
padding: 12px;
|
|
|
|
|
@ -872,7 +825,6 @@ No se realiza ningún pago adicional.
|
|
|
|
|
background: rgba(13, 27, 82, 0.03);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Voucher */
|
|
|
|
|
.voucher-caption {
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
color: #666;
|
|
|
|
|
@ -893,52 +845,42 @@ No se realiza ningún pago adicional.
|
|
|
|
|
gap: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Opcional: cards ant-design más “suaves” */
|
|
|
|
|
:deep(.ant-card) { border-radius: 12px; }
|
|
|
|
|
:deep(.ant-card-bordered) { border: 1px solid rgba(13, 27, 82, 0.10); }
|
|
|
|
|
|
|
|
|
|
/* =========================
|
|
|
|
|
FULL WIDTH + TOP 0 EN MÓVIL
|
|
|
|
|
========================= */
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
.hide-mobile{
|
|
|
|
|
display: none !important;
|
|
|
|
|
}
|
|
|
|
|
/* ✅ Quita el padding superior del wrapper (era 40px) */
|
|
|
|
|
.test-modern{
|
|
|
|
|
padding-top: 0 !important;
|
|
|
|
|
padding-bottom: 24px; /* opcional */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ✅ Quita padding lateral del container para que sea edge-to-edge */
|
|
|
|
|
.section-container{
|
|
|
|
|
max-width: none;
|
|
|
|
|
padding: 0 !important;
|
|
|
|
|
margin: 0 !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ✅ Card principal a todo el ancho y pegado arriba */
|
|
|
|
|
.hero{
|
|
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin: 0 !important;
|
|
|
|
|
border-radius: 0 !important;
|
|
|
|
|
|
|
|
|
|
/* importante: sin “espacio arriba” */
|
|
|
|
|
padding: 14px 16px 18px !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ✅ Asegura que el primer texto no empuje hacia abajo */
|
|
|
|
|
.heroKicker{ margin-top: 0 !important; }
|
|
|
|
|
.heroTitle{ margin-top: 6px !important; }
|
|
|
|
|
.heroText{ margin-top: 8px !important; }
|
|
|
|
|
|
|
|
|
|
/* Para que la sección “Tu camino” no se pegue a los bordes */
|
|
|
|
|
.section{
|
|
|
|
|
padding: 0 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Facts */
|
|
|
|
|
.heroFacts{
|
|
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
}
|
|
|
|
|
|