|
|
<!-- components/process/ProcessSection.vue -->
|
|
|
<template>
|
|
|
<section class="process-section" aria-labelledby="process-title">
|
|
|
<div class="section-container">
|
|
|
<div class="section-header">
|
|
|
<!-- <h2 id="process-title" class="section-title">{{ tituloProceso }}</h2> -->
|
|
|
<h2 id="process-title" class="section-title">EXAMEN GENERAL 2026 - I SEDES</h2>
|
|
|
<p class="section-subtitle">
|
|
|
¿No sabes por dónde empezar? Aquí te guiamos paso a paso y te decimos qué debes hacer hoy.
|
|
|
</p>
|
|
|
</div>
|
|
|
|
|
|
<div class="process-card">
|
|
|
<!-- STEPS -->
|
|
|
<a-steps
|
|
|
:direction="isMobile ? 'vertical' : 'horizontal'"
|
|
|
:responsive="false"
|
|
|
class="modern-steps"
|
|
|
:items="stepsItems"
|
|
|
/>
|
|
|
|
|
|
<!-- GUÍA PARA POSTULANTES -->
|
|
|
<div class="help-box" v-if="store.procesoPrincipal">
|
|
|
<div class="help-title"> Guía rápida (para no perderte)</div>
|
|
|
|
|
|
<div class="help-grid">
|
|
|
<!-- Etapas activas -->
|
|
|
<div class="help-item">
|
|
|
<div class="help-label">1) ¿Qué etapa está activa hoy?</div>
|
|
|
|
|
|
<div class="badges">
|
|
|
<span v-if="active.pre" class="badge badge-blue">
|
|
|
Preinscripción activa (virtual / en línea)
|
|
|
</span>
|
|
|
|
|
|
<span v-if="active.ins" class="badge badge-blue">
|
|
|
Inscripción activa (presencial en el lugar indicado por las sedes)
|
|
|
</span>
|
|
|
|
|
|
<span v-if="active.exa" class="badge badge-green">
|
|
|
Hoy es el Examen
|
|
|
</span>
|
|
|
|
|
|
<span v-if="active.res" class="badge badge-green">
|
|
|
Hoy salen Resultados
|
|
|
</span>
|
|
|
|
|
|
<span v-if="active.bio" class="badge badge-orange">
|
|
|
Biométrico activo (solo ingresantes)
|
|
|
</span>
|
|
|
|
|
|
<span v-if="noActive" class="badge badge-gray">
|
|
|
Aún no inicia o ya terminó una etapa. Revisa las fechas del proceso.
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
<div class="tiny-hint">
|
|
|
Si ves “🟢” en una fecha, significa que esa etapa está activa hoy.
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="help-item">
|
|
|
<div class="help-label">2) ¿Qué debo hacer ahora?</div>
|
|
|
|
|
|
<ul class="help-list">
|
|
|
<li v-for="(t, i) in tareasHoy" :key="i">{{ t }}</li>
|
|
|
</ul>
|
|
|
|
|
|
<div class="help-actions">
|
|
|
<!-- Preinscripción Virtual (solo si existe link) -->
|
|
|
<a-button
|
|
|
v-if="store.procesoPrincipal.link_preinscripcion"
|
|
|
type="primary"
|
|
|
:href="store.procesoPrincipal.link_preinscripcion"
|
|
|
target="_blank"
|
|
|
>
|
|
|
Iniciar Preinscripción
|
|
|
</a-button>
|
|
|
|
|
|
<a-button
|
|
|
v-if="store.procesoPrincipal.link_reglamento"
|
|
|
type="default"
|
|
|
:href="store.procesoPrincipal.link_reglamento"
|
|
|
target="_blank"
|
|
|
>
|
|
|
Ver Reglamento
|
|
|
</a-button>
|
|
|
|
|
|
<a-button
|
|
|
v-if="store.procesoPrincipal.link_resultados"
|
|
|
type="default"
|
|
|
:href="store.procesoPrincipal.link_resultados"
|
|
|
target="_blank"
|
|
|
>
|
|
|
Ver Resultados
|
|
|
</a-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- Nota fija sobre inscripción presencial -->
|
|
|
<div class="campus-note">
|
|
|
<b>Importante:</b> La <b>Inscripción</b> se realiza de forma <b>presencial</b> en el
|
|
|
<b>Campus Universitario</b>. Lleva tu DNI y los requisitos solicitados.
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="process-note" v-else>
|
|
|
<span class="dot" />
|
|
|
<span>Cargando proceso...</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</section>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { ref, computed, onMounted, onUnmounted } from "vue"
|
|
|
import { useWebAdmisionStore } from "../../store/web"
|
|
|
|
|
|
const store = useWebAdmisionStore()
|
|
|
|
|
|
const isMobile = ref(false)
|
|
|
const checkScreen = () => (isMobile.value = window.innerWidth < 768)
|
|
|
|
|
|
const now = ref(new Date())
|
|
|
let timer = null
|
|
|
|
|
|
onMounted(() => {
|
|
|
checkScreen()
|
|
|
window.addEventListener("resize", checkScreen)
|
|
|
|
|
|
if (!store.procesoPrincipal) store.cargarProcesos()
|
|
|
|
|
|
timer = setInterval(() => (now.value = new Date()), 60_000)
|
|
|
})
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
window.removeEventListener("resize", checkScreen)
|
|
|
if (timer) clearInterval(timer)
|
|
|
})
|
|
|
|
|
|
const toDate = (value) => {
|
|
|
if (!value) return null
|
|
|
const d = new Date(value)
|
|
|
return isNaN(d.getTime()) ? null : d
|
|
|
}
|
|
|
|
|
|
const startOfDay = (d) => {
|
|
|
const x = new Date(d)
|
|
|
x.setHours(0, 0, 0, 0)
|
|
|
return x
|
|
|
}
|
|
|
|
|
|
const endOfDay = (d) => {
|
|
|
const x = new Date(d)
|
|
|
x.setHours(23, 59, 59, 999)
|
|
|
return x
|
|
|
}
|
|
|
|
|
|
const inRange = (n, start, end) => {
|
|
|
if (!start || !end) return false
|
|
|
return n >= startOfDay(start) && n <= endOfDay(end)
|
|
|
}
|
|
|
|
|
|
const sameDay = (a, b) =>
|
|
|
a.getFullYear() === b.getFullYear() &&
|
|
|
a.getMonth() === b.getMonth() &&
|
|
|
a.getDate() === b.getDate()
|
|
|
|
|
|
const fmtShort = (value) => {
|
|
|
const d = toDate(value)
|
|
|
if (!d) return "Por definir"
|
|
|
return d.toLocaleDateString("es-PE", { day: "2-digit", month: "short" })
|
|
|
}
|
|
|
|
|
|
const fmtLong = (value) => {
|
|
|
const d = toDate(value)
|
|
|
if (!d) return "Por definir"
|
|
|
return d.toLocaleDateString("es-PE", {
|
|
|
day: "2-digit",
|
|
|
month: "short",
|
|
|
year: "numeric",
|
|
|
})
|
|
|
}
|
|
|
|
|
|
const fmtRange = (start, end) => {
|
|
|
const s = toDate(start)
|
|
|
const e = toDate(end)
|
|
|
if (s && e) return `${fmtShort(s)} – ${fmtShort(e)}`
|
|
|
if (s && !e) return fmtLong(s)
|
|
|
if (!s && e) return fmtLong(e)
|
|
|
return "Por definir"
|
|
|
}
|
|
|
|
|
|
const tituloProceso = computed(() => {
|
|
|
const p = store.procesoPrincipal
|
|
|
if (!p) return "Proceso de Admisión"
|
|
|
return p.titulo || p.tipo_proceso || "Proceso de Admisión"
|
|
|
})
|
|
|
|
|
|
|
|
|
const active = computed(() => {
|
|
|
const p = store.procesoPrincipal
|
|
|
if (!p) return { pre: false, ins: false, exa: false, res: false, bio: false }
|
|
|
|
|
|
const n = now.value
|
|
|
|
|
|
const preIni = toDate(p.fecha_inicio_preinscripcion)
|
|
|
const preFin = toDate(p.fecha_fin_inscripcion) || toDate(p.fecha_fin_preinscripcion)
|
|
|
|
|
|
const insIni = toDate(p.fecha_inicio_inscripcion)
|
|
|
const insFin = toDate(p.fecha_fin_inscripcion)
|
|
|
|
|
|
const exa = toDate(p.fecha_examen1)
|
|
|
const res = toDate(p.fecha_resultados)
|
|
|
|
|
|
const bioIni = toDate(p.fecha_inicio_biometrico)
|
|
|
const bioFin = toDate(p.fecha_fin_biometrico)
|
|
|
|
|
|
return {
|
|
|
pre: inRange(n, preIni, preFin),
|
|
|
ins: inRange(n, insIni, insFin),
|
|
|
exa: exa ? sameDay(n, exa) : false,
|
|
|
res: res ? sameDay(n, res) : false,
|
|
|
bio: inRange(n, bioIni, bioFin),
|
|
|
}
|
|
|
})
|
|
|
|
|
|
const noActive = computed(() => {
|
|
|
const a = active.value
|
|
|
return !a.pre && !a.ins && !a.exa && !a.res && !a.bio
|
|
|
})
|
|
|
|
|
|
const getStepStatus = (index, p) => {
|
|
|
const n = now.value
|
|
|
|
|
|
const preIni = toDate(p.fecha_inicio_preinscripcion)
|
|
|
const preFin = toDate(p.fecha_fin_inscripcion) || toDate(p.fecha_fin_preinscripcion)
|
|
|
|
|
|
const insIni = toDate(p.fecha_inicio_inscripcion)
|
|
|
const insFin = toDate(p.fecha_fin_inscripcion)
|
|
|
|
|
|
const exa = toDate(p.fecha_examen1)
|
|
|
const res = toDate(p.fecha_resultados)
|
|
|
|
|
|
const bioIni = toDate(p.fecha_inicio_biometrico)
|
|
|
const bioFin = toDate(p.fecha_fin_biometrico)
|
|
|
|
|
|
const activePre = inRange(n, preIni, preFin)
|
|
|
const activeIns = inRange(n, insIni, insFin)
|
|
|
const activeExa = exa ? sameDay(n, exa) : false
|
|
|
const activeRes = res ? sameDay(n, res) : false
|
|
|
const activeBio = inRange(n, bioIni, bioFin)
|
|
|
|
|
|
const passed = (end) => (end ? n > endOfDay(end) : false)
|
|
|
|
|
|
const hasDates = [
|
|
|
Boolean(preIni || preFin),
|
|
|
Boolean(insIni || insFin),
|
|
|
Boolean(exa),
|
|
|
Boolean(res),
|
|
|
Boolean(bioIni || bioFin),
|
|
|
][index]
|
|
|
if (!hasDates) return "wait"
|
|
|
|
|
|
if (index === 0) return activePre ? "process" : passed(preFin) ? "finish" : "wait"
|
|
|
if (index === 1) return activeIns ? "process" : passed(insFin) ? "finish" : "wait"
|
|
|
if (index === 2) return activeExa ? "process" : passed(exa) ? "finish" : "wait"
|
|
|
if (index === 3) return activeRes ? "process" : passed(res) ? "finish" : "wait"
|
|
|
if (index === 4) return activeBio ? "process" : passed(bioFin) ? "finish" : "wait"
|
|
|
|
|
|
return "wait"
|
|
|
}
|
|
|
|
|
|
|
|
|
const withActiveBadge = (label, isActive) => (isActive ? `🟢 ${label}` : label)
|
|
|
|
|
|
const stepsItems = computed(() => {
|
|
|
const p = store.procesoPrincipal || {}
|
|
|
const a = active.value
|
|
|
|
|
|
return [
|
|
|
{
|
|
|
title: "Preinscripción Virtual",
|
|
|
description: withActiveBadge(
|
|
|
fmtRange(p.fecha_inicio_preinscripcion, p.fecha_fin_inscripcion),
|
|
|
a.pre
|
|
|
),
|
|
|
status: getStepStatus(0, p),
|
|
|
},
|
|
|
{
|
|
|
title: "Inscripción Presencial (Lugar establecido por las sedes)",
|
|
|
description: withActiveBadge(
|
|
|
fmtRange(p.fecha_inicio_inscripcion, p.fecha_fin_inscripcion),
|
|
|
a.ins
|
|
|
),
|
|
|
status: getStepStatus(1, p),
|
|
|
},
|
|
|
{
|
|
|
title: "Examen",
|
|
|
description: withActiveBadge(fmtLong(p.fecha_examen1), a.exa),
|
|
|
status: getStepStatus(2, p),
|
|
|
},
|
|
|
{
|
|
|
title: "Resultados",
|
|
|
description: withActiveBadge(fmtLong(p.fecha_resultados), a.res),
|
|
|
status: getStepStatus(3, p),
|
|
|
},
|
|
|
{
|
|
|
title: "Control Biométrico (Ingresantes)",
|
|
|
description: withActiveBadge(
|
|
|
fmtRange(p.fecha_inicio_biometrico, p.fecha_fin_biometrico),
|
|
|
a.bio
|
|
|
),
|
|
|
status: getStepStatus(4, p),
|
|
|
},
|
|
|
]
|
|
|
})
|
|
|
|
|
|
const tareasHoy = computed(() => {
|
|
|
const p = store.procesoPrincipal
|
|
|
if (!p) return []
|
|
|
|
|
|
const a = active.value
|
|
|
const tareas = []
|
|
|
|
|
|
if (a.pre) {
|
|
|
tareas.push("Entra a la Preinscripción Virtual y completa tus datos sin apuro (verifica nombres y DNI).")
|
|
|
tareas.push("Al terminar, descarga e imprime tu solicitud de preinscripción y revisa los Requisitos del proceso.")
|
|
|
}
|
|
|
|
|
|
if (a.ins) {
|
|
|
tareas.push("Acércate al Campus Universitario para la Inscripción Presencial.")
|
|
|
tareas.push("Lleva tu DNI y los documentos/pagos solicitados.")
|
|
|
}
|
|
|
|
|
|
if (a.exa) {
|
|
|
tareas.push("Hoy es el Examen: llega temprano, lleva tu DNI, constancia de inscripción y sigue las indicaciones del reglamento.")
|
|
|
}
|
|
|
|
|
|
if (a.res) {
|
|
|
tareas.push("Hoy salen Resultados.")
|
|
|
}
|
|
|
|
|
|
if (a.bio) {
|
|
|
tareas.push("Si ingresaste: acércate al control biométrico dentro de las fechas indicadas.")
|
|
|
}
|
|
|
|
|
|
if (tareas.length === 0) {
|
|
|
tareas.push("Revisa las fechas del proceso en los pasos de arriba.")
|
|
|
tareas.push("Si aún no inicia: alístate con requisitos, documentos y pagos para no correr a última hora.")
|
|
|
}
|
|
|
|
|
|
return tareas
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
.process-section {
|
|
|
padding: 30px 0;
|
|
|
background: #ffffff;
|
|
|
font-family: "Times New Roman", Times, serif;
|
|
|
}
|
|
|
|
|
|
.section-container {
|
|
|
max-width: 1100px;
|
|
|
margin: 0 auto;
|
|
|
padding: 0 20px;
|
|
|
}
|
|
|
|
|
|
.section-header {
|
|
|
text-align: center;
|
|
|
margin-bottom: 18px;
|
|
|
}
|
|
|
|
|
|
.section-title {
|
|
|
font-size: 2.1rem;
|
|
|
font-weight: 700;
|
|
|
color: #2c3e50;
|
|
|
margin: 0 0 6px 0;
|
|
|
line-height: 1.2;
|
|
|
}
|
|
|
|
|
|
.section-subtitle {
|
|
|
font-size: 1rem;
|
|
|
color: #777;
|
|
|
margin: 0;
|
|
|
line-height: 1.4;
|
|
|
}
|
|
|
|
|
|
.process-card {
|
|
|
border: 1px solid #e5e7eb;
|
|
|
border-radius: 14px;
|
|
|
padding: 16px 14px 12px;
|
|
|
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.04);
|
|
|
background: #fff;
|
|
|
}
|
|
|
|
|
|
.modern-steps {
|
|
|
padding: 8px 8px;
|
|
|
}
|
|
|
|
|
|
.modern-steps :deep(.ant-steps-item-title) {
|
|
|
white-space: normal !important;
|
|
|
overflow: visible !important;
|
|
|
text-overflow: clip !important;
|
|
|
max-width: none !important;
|
|
|
}
|
|
|
|
|
|
.modern-steps :deep(.ant-steps-item-content) {
|
|
|
min-width: 0;
|
|
|
width: 100%;
|
|
|
}
|
|
|
|
|
|
.modern-steps :deep(.ant-steps-item-container) {
|
|
|
align-items: flex-start;
|
|
|
}
|
|
|
|
|
|
.modern-steps :deep(.ant-steps-item) {
|
|
|
flex: 1 1 0;
|
|
|
}
|
|
|
|
|
|
.modern-steps :deep(.ant-steps-item-title) {
|
|
|
font-size: 0.95rem;
|
|
|
font-weight: 700;
|
|
|
color: #34495e;
|
|
|
line-height: 1.15;
|
|
|
}
|
|
|
|
|
|
.modern-steps :deep(.ant-steps-item-description) {
|
|
|
font-size: 0.82rem;
|
|
|
color: #888;
|
|
|
margin-top: 2px;
|
|
|
line-height: 1.25;
|
|
|
}
|
|
|
|
|
|
.modern-steps :deep(.ant-steps-item-icon) {
|
|
|
width: 30px;
|
|
|
height: 30px;
|
|
|
line-height: 30px;
|
|
|
font-size: 13px;
|
|
|
}
|
|
|
|
|
|
.modern-steps :deep(.ant-steps-item-tail::after) {
|
|
|
height: 2px;
|
|
|
background: #dfe6e9;
|
|
|
}
|
|
|
|
|
|
.modern-steps :deep(.ant-steps-item-process .ant-steps-item-icon) {
|
|
|
background-color: #1e3a8a;
|
|
|
border-color: #1e3a8a;
|
|
|
}
|
|
|
|
|
|
.modern-steps :deep(.ant-steps-item-finish .ant-steps-item-icon > .ant-steps-icon) {
|
|
|
color: #1e3a8a;
|
|
|
}
|
|
|
.modern-steps :deep(.ant-steps-item-finish .ant-steps-item-icon) {
|
|
|
border-color: #1e3a8a;
|
|
|
}
|
|
|
|
|
|
.process-note {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
|
margin-top: 10px;
|
|
|
font-size: 0.86rem;
|
|
|
color: #6b7280;
|
|
|
}
|
|
|
|
|
|
.dot {
|
|
|
width: 8px;
|
|
|
height: 8px;
|
|
|
border-radius: 50%;
|
|
|
background: #1e3a8a;
|
|
|
flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
.help-box {
|
|
|
margin-top: 14px;
|
|
|
border-top: 1px dashed #e5e7eb;
|
|
|
padding-top: 12px;
|
|
|
}
|
|
|
|
|
|
.help-title {
|
|
|
font-weight: 700;
|
|
|
color: #1e3a8a;
|
|
|
margin-bottom: 10px;
|
|
|
}
|
|
|
|
|
|
.help-grid {
|
|
|
display: grid;
|
|
|
grid-template-columns: 1fr 1fr;
|
|
|
gap: 12px;
|
|
|
}
|
|
|
|
|
|
.help-item {
|
|
|
border: 1px solid #eef2ff;
|
|
|
background: #fbfcff;
|
|
|
border-radius: 12px;
|
|
|
padding: 12px;
|
|
|
}
|
|
|
|
|
|
.help-label {
|
|
|
font-weight: 700;
|
|
|
color: #2c3e50;
|
|
|
margin-bottom: 8px;
|
|
|
}
|
|
|
|
|
|
.tiny-hint {
|
|
|
margin-top: 10px;
|
|
|
font-size: 0.86rem;
|
|
|
color: #6b7280;
|
|
|
}
|
|
|
|
|
|
.badges {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 8px;
|
|
|
}
|
|
|
|
|
|
.badge {
|
|
|
font-size: 0.82rem;
|
|
|
padding: 6px 10px;
|
|
|
border-radius: 999px;
|
|
|
font-weight: 700;
|
|
|
}
|
|
|
|
|
|
.badge-blue {
|
|
|
background: rgba(30, 58, 138, 0.08);
|
|
|
color: #1e3a8a;
|
|
|
border: 1px solid rgba(30, 58, 138, 0.18);
|
|
|
}
|
|
|
|
|
|
.badge-green {
|
|
|
background: rgba(16, 185, 129, 0.08);
|
|
|
color: #047857;
|
|
|
border: 1px solid rgba(16, 185, 129, 0.18);
|
|
|
}
|
|
|
|
|
|
.badge-orange {
|
|
|
background: rgba(245, 158, 11, 0.10);
|
|
|
color: #92400e;
|
|
|
border: 1px solid rgba(245, 158, 11, 0.22);
|
|
|
}
|
|
|
|
|
|
.badge-gray {
|
|
|
background: rgba(107, 114, 128, 0.08);
|
|
|
color: #374151;
|
|
|
border: 1px solid rgba(107, 114, 128, 0.18);
|
|
|
}
|
|
|
|
|
|
.help-list {
|
|
|
margin: 0;
|
|
|
padding-left: 18px;
|
|
|
color: #4b5563;
|
|
|
line-height: 1.55;
|
|
|
}
|
|
|
|
|
|
.help-actions {
|
|
|
margin-top: 10px;
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
/* Nota campus */
|
|
|
.campus-note {
|
|
|
margin-top: 12px;
|
|
|
padding: 10px 12px;
|
|
|
border-radius: 12px;
|
|
|
background: #f8fafc;
|
|
|
border: 1px solid #e5e7eb;
|
|
|
color: #374151;
|
|
|
line-height: 1.5;
|
|
|
}
|
|
|
|
|
|
@media (max-width: 992px) {
|
|
|
.section-title {
|
|
|
font-size: 1.85rem;
|
|
|
}
|
|
|
.modern-steps :deep(.ant-steps-item-title) {
|
|
|
font-size: 0.92rem;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
.process-section {
|
|
|
padding: 24px 0;
|
|
|
}
|
|
|
.section-title {
|
|
|
font-size: 1.55rem;
|
|
|
}
|
|
|
.process-card {
|
|
|
padding: 12px 10px 10px;
|
|
|
}
|
|
|
.modern-steps {
|
|
|
padding: 4px 4px;
|
|
|
}
|
|
|
.modern-steps :deep(.ant-steps-item-icon) {
|
|
|
width: 28px;
|
|
|
height: 28px;
|
|
|
line-height: 28px;
|
|
|
}
|
|
|
.help-grid {
|
|
|
grid-template-columns: 1fr;
|
|
|
}
|
|
|
}
|
|
|
</style>
|