You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1284 lines
31 KiB
Vue

<!-- views/LoginView.vue -->
<template>
<div class="login-page">
<!-- Encabezado con logo UNA -->
<header class="login-header">
<div class="header-container">
<div class="una-logo">
<div class="logo-wrapper">
<img src="/logotiny.png" alt="Logo UNA" class="logo-img" />
</div>
<div class="una-titles">
<h1 class="una-main-title">Universidad Nacional del Altiplano</h1>
<h2 class="una-subtitle">Portal del Postulante</h2>
</div>
</div>
<div class="header-contact">
<div class="contact-item">
<span class="contact-label">Admisión:</span>
<span class="contact-value">(051) 123-4567</span>
</div>
<div class="contact-item">
<span class="contact-label">Email:</span>
<span class="contact-value">admision@una.edu.pe</span>
</div>
</div>
</div>
</header>
<!-- Contenido principal -->
<main class="login-main">
<div class="login-container">
<!-- Card de Login/Registro -->
<div class="auth-card">
<!-- Logo más pequeño dentro del card -->
<div class="card-logo">
<div class="logo-icon-small">
<img src="/logotiny.png" alt="Logo UNA" />
</div>
<div class="card-logo-text">
<h2>Acceso al Sistema</h2>
<span>Ingresa o crea tu cuenta para continuar</span>
</div>
</div>
<!-- Tabs para Login/Registro -->
<a-tabs v-model:activeKey="activeTab" centered class="auth-tabs">
<a-tab-pane key="login" tab="Iniciar Sesión">
<div class="form-section">
<div class="form-header">
<h3>Acceder a mi cuenta</h3>
<p>Ingresa tus credenciales para continuar</p>
</div>
<a-form
:model="loginForm"
:rules="loginRules"
layout="vertical"
@finish="handleLogin"
class="auth-form"
ref="loginFormRef"
>
<a-form-item label="Correo Electrónico" name="email">
<a-input
v-model:value="loginForm.email"
placeholder="ejemplo@correo.com"
size="large"
:disabled="authStore.loading"
autocomplete="username"
>
<template #prefix>
<MailOutlined />
</template>
</a-input>
</a-form-item>
<a-form-item label="Contraseña" name="password">
<a-input-password
v-model:value="loginForm.password"
placeholder="Tu contraseña"
size="large"
:disabled="authStore.loading"
autocomplete="current-password"
>
<template #prefix>
<LockOutlined />
</template>
</a-input-password>
</a-form-item>
<!-- Opciones adicionales login -->
<div class="form-options">
<a-checkbox v-model:checked="rememberMe">
Recordar sesión
</a-checkbox>
<a-button type="link" @click="showForgotPassword">
¿Olvidaste tu contraseña?
</a-button>
</div>
<!-- Mensaje de error -->
<div v-if="authStore.error && activeTab === 'login'" class="error-message">
<AlertOutlined />
<span>{{ authStore.error }}</span>
</div>
<!-- Botón de submit -->
<a-form-item>
<a-button
type="primary"
html-type="submit"
size="large"
:loading="authStore.loading && activeTab === 'login'"
block
class="submit-btn"
>
<template v-if="!authStore.loading || activeTab !== 'login'">
<LoginOutlined />
Iniciar Sesión
</template>
<span v-else>Verificando...</span>
</a-button>
</a-form-item>
<!-- Enlace para registrarse -->
<div class="switch-auth">
<span>¿No tienes una cuenta? </span>
<a-button type="link" @click="activeTab = 'register'">
Regístrate aquí
</a-button>
</div>
</a-form>
</div>
</a-tab-pane>
<a-tab-pane key="register" tab="Registrarse">
<div class="form-section">
<div class="form-header">
<h3>Crear nueva cuenta</h3>
<p>Completa el formulario para registrarte</p>
</div>
<a-form
:model="registerForm"
:rules="registerRules"
layout="vertical"
@finish="handleRegister"
class="auth-form"
ref="registerFormRef"
>
<a-row :gutter="16">
<a-col :xs="24" :sm="24" :md="12">
<a-form-item label="Nombres" name="name">
<a-input
v-model:value="registerForm.name"
placeholder="Tus nombres completos"
size="large"
:disabled="authStore.loading"
autocomplete="username"
>
<template #prefix>
<UserOutlined />
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :xs="24" :sm="24" :md="12">
<a-form-item label="DNI" name="dni">
<a-input
v-model:value="registerForm.dni"
placeholder="87654321"
size="large"
:maxlength="8"
:disabled="authStore.loading"
>
<template #prefix>
<IdcardOutlined />
</template>
</a-input>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="Correo Electrónico" name="email">
<a-input
v-model:value="registerForm.email"
placeholder="ejemplo@correo.com"
size="large"
:disabled="authStore.loading"
autocomplete="email"
>
<template #prefix>
<MailOutlined />
</template>
</a-input>
</a-form-item>
<a-row :gutter="16">
<a-col :xs="24" :sm="24" :md="12">
<a-form-item label="Contraseña" name="password">
<a-input-password
v-model:value="registerForm.password"
placeholder="Mínimo 6 caracteres"
size="large"
:disabled="authStore.loading"
autocomplete="new-password"
>
<template #prefix>
<LockOutlined />
</template>
</a-input-password>
</a-form-item>
</a-col>
<a-col :xs="24" :sm="24" :md="12">
<a-form-item label="Confirmar Contraseña" name="password_confirmation">
<a-input-password
v-model:value="registerForm.password_confirmation"
placeholder="Repite tu contraseña"
size="large"
:disabled="authStore.loading"
autocomplete="new-password"
>
<template #prefix>
<SafetyOutlined />
</template>
</a-input-password>
</a-form-item>
</a-col>
</a-row>
<!-- Términos y condiciones -->
<a-form-item name="terms">
<a-checkbox v-model:checked="registerForm.terms">
Acepto los
<a href="#" @click.prevent="showTermsModal">términos y condiciones</a>
y la
<a href="#" @click.prevent="showPrivacyModal">política de privacidad</a>
</a-checkbox>
</a-form-item>
<!-- Mensaje de error -->
<div v-if="authStore.error && activeTab === 'register'" class="error-message">
<AlertOutlined />
<span>{{ authStore.error }}</span>
</div>
<!-- Botón de submit -->
<a-form-item>
<a-button
type="primary"
html-type="submit"
size="large"
:loading="authStore.loading && activeTab === 'register'"
block
class="submit-btn"
>
<template v-if="!authStore.loading || activeTab !== 'register'">
<UserAddOutlined />
Crear Cuenta
</template>
<span v-else>Registrando...</span>
</a-button>
</a-form-item>
<!-- Enlace para iniciar sesión -->
<div class="switch-auth">
<span>¿Ya tienes una cuenta? </span>
<a-button type="link" @click="activeTab = 'login'">
Inicia sesión aquí
</a-button>
</div>
</a-form>
</div>
</a-tab-pane>
</a-tabs>
<!-- Enlace de ayuda -->
<div class="help-section">
<a-divider>¿Necesitas ayuda?</a-divider>
<p class="help-text">
Si tienes problemas para acceder, comunícate con la
Dirección de Admisión al
<a href="tel:0511234567">(051) 123-4567</a>
</p>
</div>
</div>
</div>
</main>
<!-- Footer minimalista -->
<footer class="login-footer">
<div class="footer-container">
<p>© 2024 Universidad Nacional del Altiplano - Dirección de Admisión</p>
<div class="footer-links">
<a href="#" @click.prevent="showTermsModal">Términos y Condiciones</a>
<a href="#" @click.prevent="showPrivacyModal">Política de Privacidad</a>
<a href="#" @click.prevent="showHelpModal">Ayuda</a>
</div>
</div>
</footer>
<!-- Modales -->
<a-modal
v-model:visible="termsModalVisible"
title="Términos y Condiciones"
:width="modalWidth"
:footer="null"
>
<div class="modal-content">
<h3>Términos y Condiciones del Portal del Postulante</h3>
<p>1. El postulante se compromete a proporcionar información veraz y actualizada.</p>
<p>2. La Universidad se reserva el derecho de verificar la autenticidad de los documentos.</p>
<p>3. El acceso al portal es personal e intransferible.</p>
<p>4. El postulante es responsable de mantener la confidencialidad de sus credenciales.</p>
</div>
</a-modal>
<a-modal
v-model:visible="privacyModalVisible"
title="Política de Privacidad"
:width="modalWidth"
:footer="null"
>
<div class="modal-content">
<h3>Política de Privacidad</h3>
<p>1. Protegemos tus datos personales conforme a la Ley de Protección de Datos Personales.</p>
<p>2. Tu información será utilizada exclusivamente para fines de admisión.</p>
<p>3. No compartiremos tu información con terceros sin tu consentimiento.</p>
<p>4. Puedes solicitar la eliminación de tus datos según la normativa vigente.</p>
</div>
</a-modal>
<a-modal
v-model:visible="forgotPasswordModalVisible"
title="Recuperar Contraseña"
:width="modalWidth"
:footer="null"
>
<div class="modal-content">
<p>Ingresa tu correo electrónico para recibir un enlace de recuperación:</p>
<a-form
:model="forgotPasswordForm"
:rules="forgotPasswordRules"
layout="vertical"
@finish="handleForgotPassword"
>
<a-form-item label="Correo Electrónico" name="email">
<a-input
v-model:value="forgotPasswordForm.email"
placeholder="ejemplo@correo.com"
size="large"
>
<template #prefix>
<MailOutlined />
</template>
</a-input>
</a-form-item>
<a-button type="primary" html-type="submit" block :loading="sendingResetLink">
<template v-if="!sendingResetLink">
Enviar enlace de recuperación
</template>
<span v-else>Enviando...</span>
</a-button>
</a-form>
</div>
</a-modal>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, computed, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '../../store/postulanteStore'
import {
UserOutlined,
MailOutlined,
LockOutlined,
LoginOutlined,
UserAddOutlined,
SafetyOutlined,
IdcardOutlined,
AlertOutlined
} from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
const router = useRouter()
const authStore = useAuthStore()
// Refs
const activeTab = ref('login')
const loginFormRef = ref()
const registerFormRef = ref()
const rememberMe = ref(false)
const termsModalVisible = ref(false)
const privacyModalVisible = ref(false)
const forgotPasswordModalVisible = ref(false)
const sendingResetLink = ref(false)
// Computed para ancho de modal responsivo
const modalWidth = computed(() => {
const width = window.innerWidth
if (width < 640) return '90%'
if (width < 768) return '85%'
if (width < 992) return '80%'
return '600px'
})
// Estados de formularios
const loginForm = reactive({
email: '',
password: ''
})
const registerForm = reactive({
name: '',
email: '',
dni: '',
password: '',
password_confirmation: '',
terms: false
})
const forgotPasswordForm = reactive({
email: ''
})
// Reglas de validación
const loginRules = {
email: [
{ required: true, message: 'Por favor ingresa tu correo', trigger: 'blur' },
{ type: 'email', message: 'Correo electrónico inválido', trigger: 'blur' }
],
password: [
{ required: true, message: 'Por favor ingresa tu contraseña', trigger: 'blur' },
{ min: 6, message: 'La contraseña debe tener al menos 6 caracteres', trigger: 'blur' }
]
}
const registerRules = {
name: [
{ required: true, message: 'Por favor ingresa tus nombres', trigger: 'blur' },
{ min: 3, message: 'El nombre debe tener al menos 3 caracteres', trigger: 'blur' }
],
email: [
{ required: true, message: 'Por favor ingresa tu correo', trigger: 'blur' },
{ type: 'email', message: 'Correo electrónico inválido', trigger: 'blur' }
],
dni: [
{ required: true, message: 'Por favor ingresa tu DNI', trigger: 'blur' },
{ pattern: /^\d{8}$/, message: 'El DNI debe tener 8 dígitos', trigger: 'blur' }
],
password: [
{ required: true, message: 'Por favor crea una contraseña', trigger: 'blur' },
{ min: 6, message: 'La contraseña debe tener al menos 6 caracteres', trigger: 'blur' }
],
password_confirmation: [
{
required: true,
message: 'Por favor confirma tu contraseña',
trigger: 'blur'
},
{
async validator(_, value) {
if (!value) return
if (value !== registerForm.password) {
throw new Error('Las contraseñas no coinciden')
}
},
trigger: 'blur'
}
]
,
terms: [
{
async validator(_, value) {
if (!value) {
throw new Error('Debes aceptar los términos y condiciones')
}
},
trigger: 'change'
}
]
}
const forgotPasswordRules = {
email: [
{ required: true, message: 'Por favor ingresa tu correo', trigger: 'blur' },
{ type: 'email', message: 'Correo electrónico inválido', trigger: 'blur' }
]
}
// Manejar login
const handleLogin = async (values) => {
const deviceId = rememberMe.value ? generateDeviceId() : null
const result = await authStore.login({
email: values.email,
password: values.password,
device_id: deviceId
})
if (result.success) {
// Redirigir al portal
router.push('/portal-postulante')
}
}
// Manejar registro - MODIFICADO: Ahora cambia a login después del registro
const handleRegister = async (values) => {
try {
const result = await authStore.register({
name: values.name,
email: values.email,
dni: values.dni,
password: values.password,
password_confirmation: values.password_confirmation
})
if (result.success) {
// Mostrar mensaje de éxito
message.success({
content: '¡Registro exitoso! Ahora puedes iniciar sesión con tus credenciales.',
duration: 5,
})
// Guardar el email del registro para facilitar el login
loginForm.email = values.email
loginForm.password = '' // Limpiar la contraseña por seguridad
// Cambiar a la pestaña de login
activeTab.value = 'login'
// Limpiar el formulario de registro después de un breve delay
setTimeout(() => {
registerForm.name = ''
registerForm.email = ''
registerForm.dni = ''
registerForm.password = ''
registerForm.password_confirmation = ''
registerForm.terms = false
// Resetear errores de validación
if (registerFormRef.value) {
registerFormRef.value.clearValidate()
}
}, 500)
return
}
// Si hay error en el registro, mostrar mensaje
if (result.error) {
message.error(result.error)
}
} catch (error) {
console.error('Error en registro:', error)
message.error('Error en el proceso de registro. Por favor, inténtalo nuevamente.')
}
}
// Funciones auxiliares
const generateDeviceId = () => {
return 'device_' + Math.random().toString(36).substr(2, 9)
}
const showForgotPassword = () => {
forgotPasswordModalVisible.value = true
}
const handleForgotPassword = async (values) => {
sendingResetLink.value = true
try {
// Aquí implementarías la llamada a la API para recuperar contraseña
// await api.post('/postulante/forgot-password', { email: values.email })
// Simulando envío
await new Promise(resolve => setTimeout(resolve, 1500))
message.success('Se ha enviado un enlace de recuperación a tu correo electrónico')
forgotPasswordModalVisible.value = false
forgotPasswordForm.email = ''
} catch (error) {
console.error('Error enviando enlace de recuperación:', error)
message.error('Error al enviar el enlace de recuperación')
} finally {
sendingResetLink.value = false
}
}
const showTermsModal = () => {
termsModalVisible.value = true
}
const showPrivacyModal = () => {
privacyModalVisible.value = true
}
const showHelpModal = () => {
alert('Para ayuda, contacta a la Dirección de Admisión al (051) 123-4567 o escribe a admision@una.edu.pe')
}
// Si ya está autenticado, redirigir al portal
onMounted(async () => {
const isAuthenticated = await authStore.checkAuth()
if (isAuthenticated) {
router.push('/portal')
}
})
</script>
<style scoped>
/* Variables */
:root {
--primary-color: #1890ff;
--secondary-color: #4a5568;
--accent-color: #4299e1;
--light-gray: #f7fafc;
--medium-gray: #e2e8f0;
--dark-gray: #2d3748;
--white: #ffffff;
--shadow-light: 0 2px 8px rgba(0, 0, 0, 0.06);
--shadow-medium: 0 4px 12px rgba(0, 0, 0, 0.08);
--shadow-heavy: 0 8px 24px rgba(0, 0, 0, 0.12);
--font-family: "Times New Roman", Times, serif;
--una-blue: #1a365d;
--una-gold: #d4af37;
}
/* Estructura de la página */
.login-page {
min-height: 100vh;
display: flex;
flex-direction: column;
font-family: var(--font-family);
background: #f8f9fa;
}
/* =========================================== */
/* ENCABEZADO CON LOGO UNA */
/* =========================================== */
.login-header {
background: var(--una-blue);
color: white;
padding: 12px 0;
box-shadow: var(--shadow-heavy);
position: sticky;
top: 0;
z-index: 1000;
}
.header-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
.una-logo {
display: flex;
align-items: center;
gap: 16px;
}
.logo-wrapper {
width: 50px;
height: 50px;
border-radius: 8px;
background: white;
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
box-shadow: var(--shadow-medium);
}
.logo-img {
width: 100%;
height: 100%;
object-fit: contain;
}
.una-titles {
display: flex;
flex-direction: column;
}
.una-main-title {
margin: 0;
font-size: 1.25rem;
font-weight: 700;
color: white;
line-height: 1.2;
}
.una-subtitle {
margin: 4px 0 0;
font-size: 1rem;
font-weight: 500;
color: var(--una-gold);
letter-spacing: 0.5px;
}
.header-contact {
display: flex;
flex-direction: column;
gap: 4px;
text-align: right;
}
.contact-item {
display: flex;
gap: 8px;
align-items: center;
font-size: 0.875rem;
}
.contact-label {
font-weight: 600;
color: var(--una-gold);
}
.contact-value {
color: white;
}
/* Contenido principal - CENTRADO */
.login-main {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 30px 20px;
width: 100%;
}
.login-container {
max-width: 500px; /* Ancho fijo para centrar */
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
/* Card de autenticación */
.auth-card {
background: rgb(255, 255, 255);
border-radius: 12px;
padding: 40px;
box-shadow: var(--shadow-heavy);
border: 1px solid var(--medium-gray);
width: 100%;
margin-top: 20px;
}
.card-logo {
text-align: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid var(--medium-gray);
}
.logo-icon-small {
width: 60px;
height: 60px;
border-radius: 10px;
background: var(--white);
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 15px;
border: 2px solid var(--primary-color);
padding: 5px;
}
.logo-icon-small img {
width: 100%;
height: 100%;
object-fit: contain;
}
.card-logo-text h2 {
margin: 0 0 8px;
color: var(--primary-color);
font-size: 1.35rem;
font-weight: 700;
}
.card-logo-text span {
color: var(--secondary-color);
font-size: 0.875rem;
}
/* Tabs de autenticación */
.auth-tabs :deep(.ant-tabs-nav) {
margin-bottom: 30px;
}
.auth-tabs :deep(.ant-tabs-tab) {
font-size: 1rem;
font-weight: 500;
padding: 12px 24px;
}
.auth-tabs :deep(.ant-tabs-tab-active .ant-tabs-tab-btn) {
color: var(--primary-color);
font-weight: 600;
}
.auth-tabs :deep(.ant-tabs-ink-bar) {
background: var(--primary-color);
}
/* Formularios */
.form-section {
background: var(--white);
margin-top: 6px;
border-radius: 8px;
padding: 0;
}
.form-header {
text-align: center;
margin-bottom: 24px;
}
.form-header h3 {
margin: 0 0 8px;
color: var(--primary-color);
font-size: 1.25rem;
font-weight: 600;
}
.form-header p {
margin: 0;
color: var(--secondary-color);
font-size: 0.875rem;
}
.auth-form :deep(.ant-form-item-label) {
font-weight: 600;
color: var(--secondary-color);
font-size: 0.875rem;
}
.auth-form :deep(.ant-input),
.auth-form :deep(.ant-input-password) {
border-radius: 6px;
border: 1px solid var(--medium-gray);
transition: all 0.3s;
font-family: var(--font-family);
}
.auth-form :deep(.ant-input:hover),
.auth-form :deep(.ant-input-password:hover) {
border-color: var(--primary-color);
}
.auth-form :deep(.ant-input:focus),
.auth-form :deep(.ant-input-password:focus) {
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.1);
}
/* Opciones del formulario */
.form-options {
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 20px;
font-size: 0.875rem;
}
.form-options :deep(.ant-checkbox-wrapper) {
color: var(--secondary-color);
}
.form-options :deep(.ant-btn-link) {
padding: 0;
height: auto;
color: var(--primary-color);
text-align: left;
}
.form-options :deep(.ant-btn-link:hover) {
color: #096dd9;
}
/* Switch entre login y registro */
.switch-auth {
text-align: center;
margin-top: 20px;
color: var(--secondary-color);
font-size: 0.875rem;
}
.switch-auth :deep(.ant-btn-link) {
padding: 0;
height: auto;
color: var(--primary-color);
font-weight: 500;
}
.switch-auth :deep(.ant-btn-link:hover) {
color: #096dd9;
}
/* Mensajes de error */
.error-message {
background: #fff2f0;
border: 1px solid #ffccc7;
border-radius: 6px;
padding: 12px 16px;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 8px;
color: #cf1322;
font-size: 0.875rem;
}
.error-message svg {
font-size: 16px;
}
/* BOTÓN DE SUBMIT - AZUL SÓLIDO */
.submit-btn {
height: 48px;
font-weight: 600;
background: var(--primary-color); /* Azul sólido */
border: none;
border-radius: 8px;
box-shadow: var(--shadow-medium);
transition: all 0.3s;
margin-top: 10px;
font-size: 1rem;
}
.submit-btn:hover {
background: #096dd9; /* Azul más oscuro para hover */
transform: translateY(-2px);
box-shadow: var(--shadow-heavy);
}
.submit-btn:active {
background: #0050b3;
transform: translateY(0);
}
/* Términos y condiciones */
.auth-form :deep(.ant-form-item-control-input-content) .ant-checkbox-wrapper a {
color: var(--primary-color);
text-decoration: none;
}
.auth-form :deep(.ant-form-item-control-input-content) .ant-checkbox-wrapper a:hover {
text-decoration: underline;
}
/* Sección de ayuda */
.help-section {
margin-top: 30px;
}
.help-text {
text-align: center;
color: var(--secondary-color);
font-size: 0.875rem;
margin: 0;
line-height: 1.5;
}
.help-text a {
color: var(--primary-color);
text-decoration: none;
font-weight: 500;
}
.help-text a:hover {
text-decoration: underline;
}
/* Footer */
.login-footer {
background: var(--una-blue);
color: white;
padding: 20px 0;
margin-top: auto;
width: 100%;
}
.footer-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
text-align: center;
}
.footer-container p {
margin: 0 0 12px;
color: rgba(255, 255, 255, 0.8);
font-size: 0.75rem;
}
.footer-links {
display: flex;
justify-content: center;
gap: 20px;
flex-wrap: wrap;
}
.footer-links a {
color: rgba(255, 255, 255, 0.8);
font-size: 0.75rem;
text-decoration: none;
transition: color 0.3s;
cursor: pointer;
}
.footer-links a:hover {
color: var(--una-gold);
text-decoration: underline;
}
/* Modales */
.modal-content {
padding: 16px 0;
font-family: var(--font-family);
font-size: 0.938rem;
}
.modal-content h3 {
color: var(--primary-color);
margin-bottom: 16px;
font-size: 1.125rem;
}
.modal-content p {
color: var(--secondary-color);
margin-bottom: 12px;
line-height: 1.6;
}
/* =========================================== */
/* MEDIA QUERIES PARA RESPONSIVIDAD */
/* =========================================== */
/* Tablets pequeñas y móviles grandes (576px - 768px) */
@media (max-width: 768px) {
.header-container {
flex-direction: column;
gap: 12px;
text-align: center;
}
.una-logo {
justify-content: center;
}
.header-contact {
text-align: center;
align-items: center;
}
.contact-item {
justify-content: center;
}
.login-main {
padding: 20px 15px;
}
.auth-card {
padding: 30px 25px;
}
.card-logo-text h2 {
font-size: 1.25rem;
}
.auth-tabs :deep(.ant-tabs-tab) {
padding: 10px 20px;
font-size: 0.938rem;
}
.form-header h3 {
font-size: 1.125rem;
}
.submit-btn {
height: 46px;
font-size: 0.938rem;
}
}
/* Móviles pequeños y medianos (480px - 576px) */
@media (max-width: 576px) {
.login-header {
padding: 10px 0;
}
.una-main-title {
font-size: 1rem;
}
.una-subtitle {
font-size: 0.875rem;
}
.logo-wrapper {
width: 40px;
height: 40px;
}
.contact-item {
font-size: 0.813rem;
}
.login-main {
padding: 15px 10px;
}
.auth-card {
padding: 25px 20px;
border-radius: 10px;
}
.card-logo {
margin-bottom: 25px;
}
.logo-icon-small {
width: 50px;
height: 50px;
}
.card-logo-text h2 {
font-size: 1.125rem;
}
.card-logo-text span {
font-size: 0.813rem;
}
.auth-tabs :deep(.ant-tabs-nav) {
margin-bottom: 25px;
}
.auth-tabs :deep(.ant-tabs-tab) {
padding: 8px 16px;
font-size: 0.875rem;
}
.form-header {
margin-bottom: 20px;
}
.form-header h3 {
font-size: 1rem;
}
.form-header p {
font-size: 0.813rem;
}
.auth-form :deep(.ant-form-item-label) {
font-size: 0.813rem;
}
.auth-form :deep(.ant-input),
.auth-form :deep(.ant-input-password) {
font-size: 0.875rem;
}
.form-options {
font-size: 0.813rem;
}
.switch-auth {
font-size: 0.813rem;
}
.error-message {
padding: 10px 14px;
font-size: 0.813rem;
}
.submit-btn {
height: 44px;
font-size: 0.875rem;
}
.help-section {
margin-top: 25px;
}
.help-text {
font-size: 0.813rem;
}
.login-footer {
padding: 16px 0;
}
.footer-container p {
font-size: 0.688rem;
margin-bottom: 10px;
}
.footer-links {
gap: 15px;
}
.footer-links a {
font-size: 0.688rem;
}
}
/* Móviles muy pequeños (menos de 480px) */
@media (max-width: 480px) {
.una-logo {
flex-direction: column;
text-align: center;
gap: 10px;
}
.header-contact {
width: 100%;
}
.login-main {
padding: 10px 8px;
}
.auth-card {
padding: 20px 16px;
}
.auth-tabs :deep(.ant-tabs-tab) {
padding: 6px 12px;
font-size: 0.813rem;
}
.card-logo-text h2 {
font-size: 1rem;
}
.form-options {
flex-direction: column;
gap: 10px;
}
.form-options :deep(.ant-checkbox-wrapper) {
width: 100%;
}
.form-options :deep(.ant-btn-link) {
align-self: flex-start;
}
.submit-btn {
height: 42px;
}
.modal-content {
font-size: 0.875rem;
}
.modal-content h3 {
font-size: 1rem;
}
}
/* Pantallas muy altas */
@media (min-height: 800px) {
.login-main {
padding-top: 50px;
padding-bottom: 50px;
}
}
</style>