recuperar contraseña

This commit is contained in:
luis cespedes 2025-05-06 13:16:49 -04:00
parent 0288e80b9d
commit 31f0ce7292
3 changed files with 224 additions and 390 deletions

View File

@ -1,5 +1,4 @@
<!-- Header container -->
<div class="header-container">
<div class="header-image">
<div class="water-drop-container">
@ -16,124 +15,100 @@
<p-toast></p-toast>
<!-- MAIN CONTENT -->
<main class="main-content">
<main class="main-content flex align-items-center justify-content-center">
<div class="login-container">
<div class="login-box">
<div class="flip-container" [ngClass]="{'flip': showRecovery}">
<div class="flipper">
<!-- LADO FRONTAL: LOGIN -->
<div class="front">
<div class="login-card">
<div class="login-header">
<h2>Iniciar Sesión</h2>
<!-- Contenedor principal con posición relativa -->
<div class="position-relative overflow-hidden">
<!-- PANEL DE LOGIN -->
<div class="panel-container w-full"
[ngClass]="{'animate__animated animate__fadeOut d-none': showRecovery,
'animate__animated animate__fadeIn': !showRecovery && !isInitialLoad}">
<div class="login-card shadow-2 border-round">
<div class="login-header">
<h2>Iniciar Sesión</h2>
</div>
<form (ngSubmit)="onLogin()" class="p-3">
<!-- Email -->
<div class="field mb-3">
<input type="email" pInputText [(ngModel)]="email" name="email" placeholder="Email"
class="input-with-icon w-full" required />
</div>
<form (ngSubmit)="onLogin()">
<!-- Email -->
<input
type="email"
pInputText
[(ngModel)]="email"
name="email"
placeholder="Email"
class="input-with-icon w-full mb-3"
style="background-color: white; color: black;"
required
/>
<!-- Password -->
<input
type="password"
pInputText
[(ngModel)]="password"
name="password"
placeholder="Password"
class="input-with-lock w-full"
style="background-color: white; color: black;"
required
/>
<!-- Mensaje de error -->
<div *ngIf="errorMessage" class="error-message mt-2">
<p-message severity="error" [text]="errorMessage"></p-message>
</div>
<!-- Botón -->
<div class="login-actions">
<button
pButton
type="submit"
[label]="loading ? 'Autenticando...' : 'Autenticar'"
class="p-button-primary w-full"
style="color: white"
[disabled]="loading || !email || !password">
<i *ngIf="loading" class="pi pi-spin pi-spinner mr-2"></i>
</button>
</div>
</form>
<!-- Recuperar contraseña -->
<div class="password-recovery">
<a href="#" (click)="toggleRecovery($event)">Recuperar Contraseña</a>
<!-- Password -->
<div class="field mb-3">
<input type="password" pInputText [(ngModel)]="password" name="password" placeholder="Password"
class="input-with-lock w-full" required />
</div>
<!-- Credenciales de prueba -->
<div class="test-credentials mt-3 p-2" style="background-color: #f8f9fa; border-radius: 4px;">
<p class="mb-1"><strong>Credenciales de prueba:</strong></p>
<p class="mb-1">Email: admin&#64;example.com</p>
<p>Password: admin123</p>
<!-- Mensaje de error -->
<div *ngIf="errorMessage" class="error-message my-2">
<p-message severity="error" [text]="errorMessage"></p-message>
</div>
<!-- Botón -->
<div class="login-actions">
<button pButton type="submit" [label]="loading ? 'Autenticando...' : 'Autenticar'"
class="p-button-primary w-full" [disabled]="loading || !email || !password">
<i *ngIf="loading" class="pi pi-spin pi-spinner mr-2"></i>
</button>
</div>
</form>
<!-- Recuperar contraseña -->
<div class="password-recovery px-3 pb-3">
<a href="#" (click)="toggleRecovery($event)">Recuperar Contraseña</a>
</div>
<!-- Credenciales de prueba -->
<div class="test-credentials mx-3 mb-3 p-2 border-round bg-gray-100">
<p class="mb-1 font-bold">Credenciales de prueba:</p>
<p class="mb-1">Email: admin&#64;example.com</p>
<p>Password: admin123</p>
</div>
</div>
</div>
<!-- PANEL DE RECUPERACIÓN -->
<div class="panel-container w-full"
[ngClass]="{'animate__animated animate__fadeIn': showRecovery,
'animate__animated animate__fadeOut d-none': !showRecovery && !isInitialLoad}">
<div class="login-card shadow-2 border-round">
<div class="login-header">
<h2>Recuperar Contraseña</h2>
</div>
<!-- LADO TRASERO: RECUPERACIÓN DE CONTRASEÑA -->
<div class="back">
<div class="login-card">
<div class="login-header">
<h2>Recuperar Contraseña</h2>
<form (ngSubmit)="onRecoverPassword()" class="p-3">
<!-- Email para recuperación -->
<div class="field mb-3">
<input type="email" pInputText [(ngModel)]="recoveryEmail" name="recoveryEmail"
placeholder="Ingresa tu email" class="input-with-icon w-full" required />
</div>
<form (ngSubmit)="onRecoverPassword()">
<!-- Email para recuperación -->
<input
type="email"
pInputText
[(ngModel)]="recoveryEmail"
name="recoveryEmail"
placeholder="Ingresa tu email"
class="input-with-icon w-full mb-3"
style="background-color: white; color: black;"
required
/>
<!-- Mensaje informativo -->
<div class="info-message mb-3">
<p class="text-sm">Te enviaremos un enlace para restablecer tu contraseña.</p>
</div>
<!-- Mensaje de estado de recuperación -->
<div *ngIf="recoveryMessage" class="recovery-message mt-2 mb-3">
<p-message [severity]="recoveryStatus" [text]="recoveryMessage"></p-message>
</div>
<!-- Botón de enviar -->
<div class="login-actions">
<button
pButton
type="submit"
[label]="recoveryLoading ? 'Enviando...' : 'Enviar Enlace'"
class="p-button-primary w-full"
style="color: white"
[disabled]="recoveryLoading || !recoveryEmail">
<i *ngIf="recoveryLoading" class="pi pi-spin pi-spinner mr-2"></i>
</button>
</div>
</form>
<!-- Volver al login -->
<div class="password-recovery">
<a href="#" (click)="toggleRecovery($event)">Volver al Login</a>
<!-- Mensaje informativo -->
<div class="info-message mb-3">
<p class="text-sm">Te enviaremos un enlace para restablecer tu contraseña.</p>
</div>
<!-- Mensaje de estado de recuperación -->
<div *ngIf="recoveryMessage" class="recovery-message my-2">
<p-message [severity]="recoveryStatus" [text]="recoveryMessage"></p-message>
</div>
<!-- Botón de enviar -->
<div class="login-actions">
<button pButton type="submit" [label]="recoveryLoading ? 'Enviando...' : 'Enviar Enlace'"
class="p-button-primary w-full" [disabled]="recoveryLoading || !recoveryEmail">
<i *ngIf="recoveryLoading" class="pi pi-spin pi-spinner mr-2"></i>
</button>
</div>
</form>
<!-- Volver al login -->
<div class="password-recovery px-3 pb-3">
<a href="#" (click)="toggleRecovery($event)">Volver al Login</a>
</div>
</div>
</div>
@ -143,4 +118,4 @@
</main>
<!-- FOOTER -->
<app-footer></app-footer>
<app-footer></app-footer>

View File

@ -5,218 +5,6 @@
}
/* Header Styles */
.header {
background-color: #d3e9f7;
padding: 1rem 3rem;
display: flex;
align-items: center;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.header-content {
display: flex;
align-items: center;
gap: 2rem;
width: 100%;
max-width: 1200px;
margin: 0 auto;
}
.siss-logo {
height: 60px;
}
.sacg-container {
display: flex;
flex-direction: column;
}
.sacg-title {
display: flex;
align-items: center;
font-size: 2.5rem;
font-weight: bold;
color: #0a2847;
position: relative;
}
.version-badge {
background-color: #0088cc;
color: white;
border-radius: 50%;
padding: 0.1rem 0.4rem;
font-size: 0.8rem;
position: absolute;
top: 0;
right: -1.5rem;
}
.sacg-subtitle {
font-size: 1.2rem;
color: #0a2847;
}
/* Main Content Styles */
.main-content {
flex: 1;
display: flex;
justify-content: center;
align-items: flex-start; /* Cambiado de center a flex-start */
padding-top: 40px; /* Añadido padding superior */
background-color: #f0f0f0;
background-size: cover;
background-position: center;
position: relative;
}
.main-content::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(240, 240, 240, 0.7);
}
.login-container {
position: relative;
z-index: 10;
}
.login-box {
width: 360px;
max-width: 100%;
margin-top: 2rem;
margin-bottom: 2rem;
}
.login-card {
background-color: white;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
width: 100%;
}
/* Estilos para el efecto de flip de tarjeta */
.flip-container {
perspective: 1000px;
width: 100%;
min-height: 410px; /* Aumentamos altura para evitar saltos */
position: relative;
}
.flip-container.flip .flipper {
transform: rotateY(180deg);
}
.flipper {
transition: 0.6s;
transform-style: preserve-3d;
position: relative;
width: 100%;
height: 100%;
}
.front, .back {
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden; /* Asegura compatibilidad con Safari */
backface-visibility: hidden;
}
.front {
z-index: 2;
transform: rotateY(0deg);
}
.back {
transform: rotateY(180deg);
}
/* Ajuste para los contenedores de recuperación */
.info-message {
padding: 0 0.5rem;
}
.recovery-message {
min-height: 30px;
}
.login-header {
padding: 1rem;
text-align: center;
border-bottom: 1px solid #e0e0e0;
}
.login-header h2 {
margin: 0;
font-size: 1.2rem;
font-weight: normal;
color: #666;
}
.login-card form {
padding: 1.5rem;
}
.p-inputgroup-addon {
background-color: #f0f0f0;
border-color: #ced4da;
}
.login-actions {
display: flex;
justify-content: flex-end;
margin-top: 1rem;
}
.p-button-primary {
background-color: #0088cc;
border-color: #0088cc;
}
.password-recovery {
padding: 0 1.5rem 1.5rem;
text-align: left;
}
.password-recovery a {
color: #0088cc;
text-decoration: none;
font-size: 0.9rem;
}
.password-recovery a:hover {
text-decoration: underline;
}
/* Responsive styles */
@media screen and (max-width: 768px) {
.header-content {
flex-direction: column;
gap: 1rem;
}
}
.login-page-custom {
-ms-flex-align: center !important;
align-items: center !important;
background-color: #e9ecef !important;
display: -ms-flexbox !important;
display: flex !important;
-ms-flex-direction: column !important;
flex-direction: column !important;
height: calc(100vh - 250px) !important;
-ms-flex-pack: center !important;
justify-content: center !important;
}
.header-container {
position: relative;
width: 100%;
@ -224,58 +12,6 @@
overflow: hidden;
}
.water-drop {
position: absolute;
height: 45px;
width: auto;
top: 30%;
left: 47%;
transform: translateY(-50%);
z-index: 2;
}
.text-drop {
color: #fff;
position: absolute;
width: auto;
top: 33%;
left: 47.3%;
transform: translateY(-50%);
z-index: 2;
}
.header-text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #002147;
text-align: center;
padding: 0px;
box-sizing: border-box;
}
.main-title {
margin-top: 0px;
margin-right: 250px;
font-size: 4rem;
font-weight: bold;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
.sub-title {
font-size: 3.0rem;
margin-bottom: 0px;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
transform: translate(-4%);
}
.header-image {
position: relative;
width: 100%;
@ -318,16 +54,124 @@
.header-text {
position: absolute;
bottom: 20px;
left: 20px;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #002147;
text-align: center;
padding: 0px;
box-sizing: border-box;
}
.sub-title {
font-size: 3.0rem;
margin-bottom: 0px;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5);
transform: translate(-4%);
}
/* Main Content Styles */
.main-content {
flex: 1;
background-color: #f0f0f0;
position: relative;
min-height: 60vh; /* Aumentado para mayor espacio vertical */
}
.main-content::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(240, 240, 240, 0.7);
}
.login-container {
position: relative;
z-index: 10;
width: 100%;
max-width: 360px;
}
.login-box {
width: 100%;
}
.login-card {
background-color: white;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
width: 100%;
}
/* Estilo para los paneles y animaciones */
.position-relative {
min-height: 450px;
position: relative;
}
.panel-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
transition: opacity 0.3s ease-out, visibility 0.3s ease-out;
}
.d-none {
visibility: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
position: absolute !important;
z-index: -1 !important;
}
/* Configuración para animate.css */
.animate__animated {
--animate-duration: 0.8s; /* Aumentado para una transición más suave */
}
.login-header {
padding: 1rem;
text-align: center;
border-bottom: 1px solid #e0e0e0;
}
.login-header h2 {
margin: 0;
font-size: 1.2rem;
color: #0088cc;
}
.login-actions {
margin-top: 1rem;
}
.password-recovery a {
color: #0088cc;
text-decoration: none;
font-size: 0.9rem;
}
.password-recovery a:hover {
text-decoration: underline;
}
/* Estilos para los inputs con iconos */
.input-with-icon {
background-image: url("data:image/svg+xml,%3Csvg fill='gray' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 4H4C2.897 4 2 4.897 2 6v12c0 1.103.897 2 2 2h16c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2zM4 6h16l-8 5-8-5zm0 12V8l8 5 8-5v10H4z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 20px 20px;
padding-right: 35px; /* espacio para que el texto no choque con el ícono */
padding-right: 35px;
}
.input-with-lock {
@ -335,5 +179,12 @@
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 20px 20px;
padding-right: 35px; /* espacio para que el texto no se superponga al ícono */
padding-right: 35px;
}
/* Responsive styles */
@media screen and (max-width: 768px) {
.login-container {
padding: 0 1rem;
}
}

View File

@ -1,8 +1,7 @@
import { Component } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { CardModule } from 'primeng/card';
import { InputTextModule } from 'primeng/inputtext';
import { ButtonModule } from 'primeng/button';
@ -34,7 +33,7 @@ import { AuthService } from '../../services/auth.service';
templateUrl: './login.component.html',
styleUrl: './login.component.scss'
})
export class LoginComponent {
export class LoginComponent implements OnInit {
// Login form
email: string = '';
password: string = '';
@ -49,13 +48,18 @@ export class LoginComponent {
// Control de formularios
showRecovery: boolean = false;
isInitialLoad: boolean = false;
constructor(
private router: Router,
private router: Router,
private authService: AuthService,
private messageService: MessageService
) { }
ngOnInit() {
}
/**
* Cambia entre el formulario de login y recuperación
*/
@ -69,8 +73,13 @@ export class LoginComponent {
if (this.showRecovery && this.email) {
this.recoveryEmail = this.email;
}
// Forzar actualización del DOM con un pequeño retraso
setTimeout(() => {
// Este timeout ayuda a que Angular aplique los cambios de clase completamente
}, 50); // Aumentado para asegurar la transición suave
}
/**
* Proceso de login
*/
@ -90,15 +99,15 @@ export class LoginComponent {
console.error('Error en login:', error);
this.loading = false;
this.errorMessage = 'Credenciales incorrectas';
this.messageService.add({
severity: 'error',
summary: 'Error',
detail: 'Credenciales incorrectas'
this.messageService.add({
severity: 'error',
summary: 'Error',
detail: 'Credenciales incorrectas'
});
}
});
}
/**
* Proceso de recuperación de contraseña
*/
@ -108,7 +117,7 @@ export class LoginComponent {
this.recoveryStatus = 'error';
return;
}
this.recoveryLoading = true;
this.recoveryMessage = '';
@ -117,7 +126,6 @@ export class LoginComponent {
this.recoveryLoading = false;
this.recoveryMessage = 'Hemos enviado un enlace de recuperación a tu email';
this.recoveryStatus = 'success';
this.messageService.add({
severity: 'success',
summary: 'Email enviado',
@ -125,4 +133,4 @@ export class LoginComponent {
});
}, 1500);
}
}
}