feat: Side menu dinámico, login social y traducciones hero

- app.component: menú lateral con roles de usuario, ChangeDetectorRef
  para re-render tras login, integración OneSignal al iniciar sesión
- auth/login: botones Google y Apple con estilos, navegación corregida
- auth/register y forgot: mejoras de UX y navegación
- landing: sombra en botones del footer
- i18n: claves hero (select, bank, bank_account, fee, required) en es/en

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 08:18:12 -06:00
parent 0efe318db3
commit db0d0001fa
12 changed files with 314 additions and 241 deletions

View File

@@ -8,12 +8,46 @@
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-list> <ion-list>
<ion-menu-toggle auto-hide="false" *ngFor="let p of appPages"> <ion-menu-toggle auto-hide="false" [class.ion-hide]="userRole === 0">
<ion-item [routerDirection]="'root'" [routerLink]="[p.url]"> <ion-item [routerDirection]="'root'" [routerLink]="['/dashboard']">
<ion-icon slot="start" [name]="p.icon"></ion-icon> <ion-icon slot="start" name="home"></ion-icon>
<ion-label> <ion-label>{{'menu.home' | translate}}</ion-label>
{{p.title}} </ion-item>
</ion-label> </ion-menu-toggle>
<ion-menu-toggle auto-hide="false" [class.ion-hide]="userRole === 0">
<ion-item [routerDirection]="'root'" [routerLink]="['/cards']">
<ion-icon slot="start" name="card"></ion-icon>
<ion-label>{{'menu.cards' | translate}}</ion-label>
</ion-item>
</ion-menu-toggle>
<ion-menu-toggle auto-hide="false" [class.ion-hide]="userRole < 2">
<ion-item [routerDirection]="'root'" [routerLink]="['/postulations']">
<ion-icon slot="start" name="hammer"></ion-icon>
<ion-label>{{'menu.postulations' | translate}}</ion-label>
</ion-item>
</ion-menu-toggle>
<ion-menu-toggle auto-hide="false" [class.ion-hide]="userRole === 0">
<ion-item [routerDirection]="'root'" [routerLink]="['/contracts']">
<ion-icon slot="start" name="file-tray-full"></ion-icon>
<ion-label>{{'menu.contracts' | translate}}</ion-label>
</ion-item>
</ion-menu-toggle>
<ion-menu-toggle auto-hide="false" [class.ion-hide]="userRole === 0">
<ion-item [routerDirection]="'root'" [routerLink]="['/faq']">
<ion-icon slot="start" name="information-circle"></ion-icon>
<ion-label>{{'menu.faq' | translate}}</ion-label>
</ion-item>
</ion-menu-toggle>
<ion-menu-toggle auto-hide="false" [class.ion-hide]="userRole < 2">
<ion-item [routerDirection]="'root'" [routerLink]="['/start']">
<ion-icon slot="start" name="send"></ion-icon>
<ion-label>{{'menu.start' | translate}}</ion-label>
</ion-item>
</ion-menu-toggle>
<ion-menu-toggle auto-hide="false" [class.ion-hide]="userRole !== 1">
<ion-item [routerDirection]="'root'" [routerLink]="['/hero']">
<ion-icon slot="start" name="ribbon"></ion-icon>
<ion-label>{{'menu.hero' | translate}}</ion-label>
</ion-item> </ion-item>
</ion-menu-toggle> </ion-menu-toggle>
<ion-item (click)="logout()"> <ion-item (click)="logout()">

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core'; import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
import { Platform, NavController, LoadingController } from '@ionic/angular'; import { Platform, NavController, LoadingController } from '@ionic/angular';
import { EventService } from './services/event.service'; import { EventService } from './services/event.service';
import { StatusBar, Style } from '@capacitor/status-bar'; import { StatusBar, Style } from '@capacitor/status-bar';
@@ -13,17 +13,17 @@ import { OneSignalService } from './services/onesignal.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: 'app.component.html' templateUrl: 'app.component.html',
standalone: false
}) })
export class AppComponent { export class AppComponent implements OnInit {
private loading: any; private loading: any;
userRole: number = 0;
public appPages: any[] = [];
constructor( constructor(
private platform: Platform, private platform: Platform,
private authService: AuthService, public authService: AuthService,
private languageService: LanguageService, private languageService: LanguageService,
private translateService: TranslateService, private translateService: TranslateService,
private navCtrl: NavController, private navCtrl: NavController,
@@ -31,75 +31,20 @@ export class AppComponent {
private alertService: AlertService, private alertService: AlertService,
private loadingCtrl: LoadingController, private loadingCtrl: LoadingController,
private oneSignalService: OneSignalService, private oneSignalService: OneSignalService,
private cdr: ChangeDetectorRef,
) { ) {
this.initializeApp(); this.initializeApp();
this.events.subscribe('set_role', role => { this.events.subscribe('set_role', role => {
// Set OneSignal user tags when role is set (user logged in) this.userRole = role ?? 0;
this.cdr.detectChanges();
this.setupOneSignalUser(role); this.setupOneSignalUser(role);
if (role >= 2){
this.appPages = [
{
title: this.translateService.instant('menu.home'),
url: '/dashboard',
icon: 'home'
},
{
title: this.translateService.instant('menu.cards'),
url: '/cards',
icon: 'card'
},
{
title: this.translateService.instant('menu.postulations'),
url: '/postulations',
icon: 'hammer'
},
{
title: this.translateService.instant('menu.contracts'),
url: '/contracts',
icon: 'filing'
},
{
title: this.translateService.instant('menu.faq'),
url: '/faq',
icon: 'information-circle'
},
{
title: this.translateService.instant('menu.start'),
url: '/start',
icon: 'send'
}
];
} else {
this.appPages = [
{
title: this.translateService.instant('menu.home'),
url: '/dashboard',
icon: 'home'
},
{
title: this.translateService.instant('menu.cards'),
url: '/cards',
icon: 'card'
},
{
title: this.translateService.instant('menu.contracts'),
url: '/contracts',
icon: 'filing'
},
{
title: this.translateService.instant('menu.faq'),
url: '/faq',
icon: 'information-circle'
},
{
title: this.translateService.instant('menu.hero'),
url: '/hero',
icon: 'ribbon'
}
];
}
}); });
} }
ngOnInit() {
this.userRole = this.authService.userRole;
}
async initializeApp() { async initializeApp() {
await this.platform.ready(); await this.platform.ready();
@@ -114,7 +59,6 @@ export class AppComponent {
} }
this.languageService.getDefaultLanguage(); this.languageService.getDefaultLanguage();
this.authService.getToken();
} }
private async setupOneSignalUser(role: number) { private async setupOneSignalUser(role: number) {

View File

@@ -8,8 +8,8 @@
<ion-title>{{'auth.forgot' | translate}}</ion-title> <ion-title>{{'auth.forgot' | translate}}</ion-title>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
<ion-content padding> <ion-content class="ion-padding">
<p text-wrap>{{'auth.forgot_instructions' | translate}}</p><br> <p class="ion-text-wrap">{{'auth.forgot_instructions' | translate}}</p><br>
<form #form="ngForm" (ngSubmit)="forgot(form)" method="post"> <form #form="ngForm" (ngSubmit)="forgot(form)" method="post">
<ion-item> <ion-item>
<ion-label position="floating">{{'auth.email' | translate}}</ion-label> <ion-label position="floating">{{'auth.email' | translate}}</ion-label>

View File

@@ -1,52 +1,52 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { NavController, LoadingController } from '@ionic/angular'; import { NavController, LoadingController } from '@ionic/angular';
import { NgForm } from '@angular/forms'; import { NgForm } from '@angular/forms';
import { AuthService } from 'src/app/services/auth.service';
import { AlertService } from 'src/app/services/alert.service'; import { AlertService } from 'src/app/services/alert.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { FirebaseAuthService } from 'src/app/services/firebase-auth.service';
@Component({ @Component({
selector: 'app-forgot', selector: 'app-forgot',
templateUrl: './forgot.page.html', templateUrl: './forgot.page.html',
styleUrls: ['./forgot.page.scss'], styleUrls: ['./forgot.page.scss'],
standalone: false
}) })
export class ForgotPage implements OnInit { export class ForgotPage implements OnInit {
private loading; private loading: HTMLIonLoadingElement | null = null;
constructor( constructor(
private authService: AuthService, private firebaseAuthService: FirebaseAuthService,
private navCtrl: NavController, private navCtrl: NavController,
private alertService: AlertService, private alertService: AlertService,
private translateService: TranslateService, private translateService: TranslateService,
private loadingCtrl: LoadingController private loadingCtrl: LoadingController
) { } ) { }
ngOnInit() {
} ngOnInit() { }
// Dismiss Forgot Modal
dismissForgot() { dismissForgot() {
this.navCtrl.back(); this.navCtrl.back();
} }
// On Register button tap, dismiss login modal and open register modal
forgot(form: NgForm) { async forgot(form: NgForm) {
this.loadingCtrl.create().then((overlay) => { this.loading = await this.loadingCtrl.create();
this.loading = overlay; await this.loading.present();
this.loading.present();
}); try {
this.authService.forgot(form.value.email).subscribe( await this.firebaseAuthService.sendPasswordReset(form.value.email);
data => { if (this.loading) this.loading.dismiss();
this.loading.dismiss(); this.alertService.presentToast(this.translateService.instant('alerts.reset_pass'));
this.dismissForgot();
} catch (e: any) {
if (this.loading) this.loading.dismiss();
if (e.code === 'auth/user-not-found') {
// Por seguridad mostramos el mismo mensaje aunque no exista el usuario
this.alertService.presentToast(this.translateService.instant('alerts.reset_pass')); this.alertService.presentToast(this.translateService.instant('alerts.reset_pass'));
},
error => {
this.loading.dismiss();
this.alertService.presentToast(this.translateService.instant('alerts.error') + error['message']);
console.log(error);
},
() => {
this.dismissForgot(); this.dismissForgot();
} else {
this.alertService.presentToast(this.translateService.instant('alerts.error') + e.message);
} }
); }
} }
} }

View File

@@ -8,7 +8,7 @@
<ion-title>{{'auth.login' | translate}}</ion-title> <ion-title>{{'auth.login' | translate}}</ion-title>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
<ion-content padding> <ion-content class="ion-padding">
<form #form="ngForm" (ngSubmit)="login(form)" method="post"> <form #form="ngForm" (ngSubmit)="login(form)" method="post">
<ion-item> <ion-item>
<ion-label position="floating">{{'auth.email' | translate}}</ion-label> <ion-label position="floating">{{'auth.email' | translate}}</ion-label>
@@ -22,11 +22,27 @@
<br> <br>
<ion-button type="submit" expand="full" color="primary">{{'auth.login' | translate}}</ion-button> <ion-button type="submit" expand="full" color="primary">{{'auth.login' | translate}}</ion-button>
</form> </form>
<ion-text><p text-center (click)="forgotModal()"><u>{{'auth.forgot' | translate}}</u></p></ion-text> <ion-text><p class="ion-text-center" (click)="forgotModal()"><u>{{'auth.forgot' | translate}}</u></p></ion-text>
<br><br> <br><br>
<ion-button expand="full" color="facebook" (click)="loginFacebook()">{{'auth.fb_login' | translate}}</ion-button> <!-- Google Sign-In — branding oficial: fondo blanco, logo G coloreado, borde gris -->
<ion-button expand="full" color="danger" (click)="loginGoogle()">{{'auth.google_login' | translate}}</ion-button> <button class="btn-social btn-google" (click)="loginGoogle()">
<p text-center>{{'auth.signup_ad' | translate}}</p> <svg class="btn-social__icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z" fill="#FBBC05"/>
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
</svg>
<span class="btn-social__label">{{'auth.google_login' | translate}}</span>
</button>
<!-- Apple Sign-In — branding oficial: fondo negro, logo Apple blanco, SF font -->
<button class="btn-social btn-apple" (click)="loginApple()">
<svg class="btn-social__icon btn-social__icon--apple" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.7 9.05 7.4c1.42.07 2.4.78 3.23.8 1.22-.24 2.39-.93 3.68-.84 1.56.12 2.73.72 3.5 1.9-3.22 1.93-2.46 5.91.59 7.03-.69 1.55-1.6 3.08-3 4zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" fill="#FFFFFF"/>
</svg>
<span class="btn-social__label">{{'auth.apple_login' | translate}}</span>
</button>
<p class="ion-text-center">{{'auth.signup_ad' | translate}}</p>
<ion-button expand="full" color="secondary" (click)="registerModal()">{{'auth.signup' | translate}}</ion-button> <ion-button expand="full" color="secondary" (click)="registerModal()">{{'auth.signup' | translate}}</ion-button>
<ion-text><p text-center>{{'auth.terms_1' | translate}}<u (click)="openTerms()">{{'auth.terms_2' | translate}}</u>{{'auth.terms_3' | translate}}</p></ion-text> <ion-text><p class="ion-text-center">{{'auth.terms_1' | translate}}<u (click)="openTerms()">{{'auth.terms_2' | translate}}</u>{{'auth.terms_3' | translate}}</p></ion-text>
</ion-content> </ion-content>

View File

@@ -0,0 +1,63 @@
.btn-social {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 44px;
border-radius: 4px;
border: none;
cursor: pointer;
font-size: 14px;
font-weight: 500;
letter-spacing: 0.06em;
margin-bottom: 12px;
padding: 0 12px;
transition: box-shadow 0.2s ease;
&__icon {
width: 20px;
height: 20px;
margin-right: 12px;
flex-shrink: 0;
&--apple {
width: 18px;
height: 18px;
}
}
&__label {
flex: 1;
text-align: center;
margin-right: 32px; // compensa el ancho del icono para centrar visualmente
text-transform: uppercase;
}
&:active {
box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.15);
}
}
// Google: fondo blanco, borde gris, texto oscuro
.btn-google {
background-color: #ffffff;
border: 1px solid #DADCE0;
color: #3c4043;
font-family: 'Roboto', sans-serif;
&:hover {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
}
// Apple: fondo negro, texto blanco, font del sistema
.btn-apple {
background-color: #000000;
color: #ffffff;
font-family: -apple-system, 'SF Pro Display', BlinkMacSystemFont, sans-serif;
border-radius: 6px;
&:hover {
background-color: #1a1a1a;
}
}

View File

@@ -4,22 +4,24 @@ import { NgForm } from '@angular/forms';
import { AuthService } from 'src/app/services/auth.service'; import { AuthService } from 'src/app/services/auth.service';
import { AlertService } from 'src/app/services/alert.service'; import { AlertService } from 'src/app/services/alert.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { FacebookLogin, FacebookLoginResponse } from '@capacitor-community/facebook-login'; import { FirebaseAuthService } from 'src/app/services/firebase-auth.service';
import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth';
import { Browser } from '@capacitor/browser'; import { Browser } from '@capacitor/browser';
import { firstValueFrom } from 'rxjs';
@Component({ @Component({
selector: 'app-login', selector: 'app-login',
templateUrl: './login.page.html', templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'] styleUrls: ['./login.page.scss'],
standalone: false
}) })
export class LoginPage implements OnInit { export class LoginPage implements OnInit {
private loading: any; private loading: HTMLIonLoadingElement | null = null;
constructor( constructor(
private authService: AuthService, private authService: AuthService,
private firebaseAuthService: FirebaseAuthService,
private navCtrl: NavController, private navCtrl: NavController,
private alertService: AlertService, private alertService: AlertService,
private translateService: TranslateService, private translateService: TranslateService,
@@ -45,68 +47,73 @@ export class LoginPage implements OnInit {
this.loading = await this.loadingCtrl.create(); this.loading = await this.loadingCtrl.create();
await this.loading.present(); await this.loading.present();
this.authService.login(form.value.email, form.value.password).subscribe( try {
data => { const idToken = await this.firebaseAuthService.signInWithEmail(
if (this.loading) this.loading.dismiss(); form.value.email,
}, form.value.password
error => { );
if (this.loading) this.loading.dismiss();
if (JSON.stringify(error['status']) == '401') { this.authService.loginWithFirebase(idToken).subscribe(
this.alertService.presentToast(this.translateService.instant('alerts.login_error')); (data: any) => {},
} (error: any) => {
else { if (this.loading) this.loading.dismiss();
this.alertService.presentToast(this.translateService.instant('alerts.error') + error['message']); if (JSON.stringify(error['status']) == '401') {
console.log(error); this.alertService.presentToast(this.translateService.instant('alerts.login_error'));
} } else {
}, this.alertService.presentToast(this.translateService.instant('alerts.error') + error['message']);
() => { }
if (this.loading) this.loading.dismiss(); },
this.dismissLogin(); () => { this.navigateAfterLogin(); }
this.navCtrl.navigateRoot('/dashboard'); );
this.alertService.presentToast(this.translateService.instant('alerts.login')); } catch (e: any) {
} if (this.loading) this.loading.dismiss();
); const msg = this.getFirebaseErrorMessage(e.code);
this.alertService.presentToast(msg);
}
} }
async loginFacebook() { private async navigateAfterLogin() {
this.loading = await this.loadingCtrl.create(); try {
await this.loading.present(); await firstValueFrom(this.authService.user());
} catch { }
if (this.loading) this.loading.dismiss();
this.navCtrl.navigateRoot('/dashboard');
this.alertService.presentToast(this.translateService.instant('alerts.login'));
}
try { private getFirebaseErrorMessage(code: string): string {
// Login with permissions switch (code) {
const result = await FacebookLogin.login({ permissions: ['public_profile', 'email'] }); case 'auth/wrong-password':
case 'auth/user-not-found':
case 'auth/invalid-credential':
return this.translateService.instant('alerts.login_error');
case 'auth/too-many-requests':
return this.translateService.instant('alerts.too_many_requests');
default:
return this.translateService.instant('alerts.error') + code;
}
}
if (result.accessToken) { async loginApple() {
// Get user ID and Token this.loading = await this.loadingCtrl.create();
const fb_id = result.accessToken.userId; await this.loading.present();
const fb_token = result.accessToken.token;
console.log("ID: " + fb_id); try {
console.log("Token: " + fb_token); const idToken = await this.firebaseAuthService.signInWithApple();
this.authService.login_register_fb(fb_token, fb_id).subscribe( this.authService.loginWithFirebase(idToken).subscribe(
data => { (data: any) => {},
}, (error: any) => {
error => { if (this.loading) this.loading.dismiss();
this.loading.dismiss(); this.alertService.presentToast(this.translateService.instant('alerts.error') + error);
this.alertService.presentToast(this.translateService.instant('alerts.error') + error); console.log(error);
console.log(error); },
}, () => { this.navigateAfterLogin(); }
() => { );
this.loading.dismiss(); } catch (e) {
this.dismissLogin(); if (this.loading) this.loading.dismiss();
this.navCtrl.navigateRoot('/dashboard'); this.alertService.presentToast(this.translateService.instant('alerts.error') + e);
this.alertService.presentToast(this.translateService.instant('alerts.login')); }
}
);
} else {
this.loading.dismiss();
this.alertService.presentToast(this.translateService.instant('alerts.timeout_error'));
}
} catch (e) {
this.loading.dismiss();
this.alertService.presentToast(this.translateService.instant('alerts.error') + e);
}
} }
async loginGoogle() { async loginGoogle() {
@@ -114,32 +121,19 @@ export class LoginPage implements OnInit {
await this.loading.present(); await this.loading.present();
try { try {
const res = await GoogleAuth.signIn(); const idToken = await this.firebaseAuthService.signInWithGoogle();
console.log(res);
const google_id = res.id; this.authService.loginWithFirebase(idToken).subscribe(
const google_token = res.authentication.accessToken; (data: any) => {},
(error: any) => {
console.log("ID: " + google_id); if (this.loading) this.loading.dismiss();
console.log("Token: " + google_token);
this.authService.login_register_googlePlus(google_token, google_id).subscribe(
data => {
},
error => {
this.loading.dismiss();
this.alertService.presentToast(this.translateService.instant('alerts.error') + error); this.alertService.presentToast(this.translateService.instant('alerts.error') + error);
console.log(error); console.log(error);
}, },
() => { () => { this.navigateAfterLogin(); }
this.loading.dismiss();
this.dismissLogin();
this.navCtrl.navigateRoot('/dashboard');
this.alertService.presentToast(this.translateService.instant('alerts.login'));
}
); );
} catch (err) { } catch (err) {
this.loading.dismiss(); if (this.loading) this.loading.dismiss();
this.alertService.presentToast(this.translateService.instant('alerts.error') + err); this.alertService.presentToast(this.translateService.instant('alerts.error') + err);
console.log(err); console.log(err);
} }

View File

@@ -8,7 +8,7 @@
<ion-title>{{'auth.signup' | translate}}</ion-title> <ion-title>{{'auth.signup' | translate}}</ion-title>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
<ion-content padding> <ion-content class="ion-padding">
<form #form="ngForm" (ngSubmit)="register(form)" method="post"> <form #form="ngForm" (ngSubmit)="register(form)" method="post">
<ion-item> <ion-item>
<ion-label position="floating">{{'auth.name' | translate}}</ion-label> <ion-label position="floating">{{'auth.name' | translate}}</ion-label>
@@ -37,7 +37,7 @@
<br><br> <br><br>
<ion-row> <ion-row>
<ion-col size="9.5"> <ion-col size="9.5">
<p text-center style="margin-bottom:0;margin-top:0.25em;color:#4d4d4d">{{'auth.terms_1' | translate}}<u (click)="openTerms()">{{'auth.terms_2' | translate}}</u>{{'auth.terms_3' | translate}}</p> <p class="ion-text-center" style="margin-bottom:0;margin-top:0.25em;color:#4d4d4d">{{'auth.terms_1' | translate}}<u (click)="openTerms()">{{'auth.terms_2' | translate}}</u>{{'auth.terms_3' | translate}}</p>
</ion-col> </ion-col>
<ion-col size="2.5"> <ion-col size="2.5">
<ion-item lines="none"> <ion-item lines="none">
@@ -48,6 +48,6 @@
<br> <br>
<ion-button type="submit" expand="full" color="secondary">{{'auth.signup' | translate}}</ion-button> <ion-button type="submit" expand="full" color="secondary">{{'auth.signup' | translate}}</ion-button>
</form> </form>
<br><p text-center>{{'auth.login_ad' | translate}}</p> <br><p class="ion-text-center">{{'auth.login_ad' | translate}}</p>
<ion-button expand="full" color="primary" (click)="loginModal()">{{'auth.login' | translate}}</ion-button> <ion-button expand="full" color="primary" (click)="loginModal()">{{'auth.login' | translate}}</ion-button>
</ion-content> </ion-content>

View File

@@ -4,85 +4,93 @@ import { AuthService } from 'src/app/services/auth.service';
import { NgForm } from '@angular/forms'; import { NgForm } from '@angular/forms';
import { AlertService } from 'src/app/services/alert.service'; import { AlertService } from 'src/app/services/alert.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { FirebaseAuthService } from 'src/app/services/firebase-auth.service';
import { Browser } from '@capacitor/browser'; import { Browser } from '@capacitor/browser';
@Component({ @Component({
selector: 'app-register', selector: 'app-register',
templateUrl: './register.page.html', templateUrl: './register.page.html',
styleUrls: ['./register.page.scss'], styleUrls: ['./register.page.scss'],
standalone: false
}) })
export class RegisterPage implements OnInit { export class RegisterPage implements OnInit {
loading: any = null; loading: HTMLIonLoadingElement | null = null;
terms = false; terms = false;
constructor( constructor(
private authService: AuthService, private authService: AuthService,
private firebaseAuthService: FirebaseAuthService,
private translateService: TranslateService, private translateService: TranslateService,
private navCtrl: NavController, private navCtrl: NavController,
private loadingCtrl: LoadingController, private loadingCtrl: LoadingController,
private alertService: AlertService private alertService: AlertService
) { } ) { }
ngOnInit() {
} ngOnInit() { }
// Dismiss Register Modal
dismissRegister() { dismissRegister() {
this.navCtrl.navigateRoot('/landing'); this.navCtrl.navigateRoot('/landing');
} }
// On Login button tap, dismiss Register modal and open login Modal
loginModal() { loginModal() {
this.navCtrl.navigateForward('/login'); this.navCtrl.navigateForward('/login');
} }
updateChecked () { updateChecked() {
if (this.terms == true) { this.terms = !this.terms;
this.terms = false;
console.log(this.terms);
} else {
this.terms = true;
console.log(this.terms);
}
} }
register(form: NgForm) { async register(form: NgForm) {
if (!this.terms) {
if (this.terms == true) {
if (form.value.name && form.value.email && form.value.phone && form.value.password && form.value.password1) {
if (form.value.password == form.value.password1) {
this.loadingCtrl.create().then((overlay) => {
this.loading = overlay;
this.loading.present();
});
this.authService.register(form.value.name, form.value.email, form.value.phone, form.value.password).subscribe(
data => {
this.loading.dismiss();
this.alertService.presentToast(this.translateService.instant('alerts.signup'));
},
error => {
if (JSON.stringify(error['status']) == '422') {
this.loading.dismiss();
this.alertService.presentToast(this.translateService.instant('alerts.signup_error'));
}
else {
this.loading.dismiss();
console.log(error);
this.alertService.presentToast(this.translateService.instant('alerts.error') + error['message']);
}
},
() => {
}
);
} else {
this.alertService.presentToast(this.translateService.instant('alerts.signup_error_1'));
}
} else {
this.alertService.presentToast(this.translateService.instant('alerts.signup_error_2'));
}
} else {
this.alertService.presentToast(this.translateService.instant('alerts.signup_error_3')); this.alertService.presentToast(this.translateService.instant('alerts.signup_error_3'));
return;
} }
const { name, email, phone, password, password1 } = form.value;
if (!name || !email || !phone || !password || !password1) {
this.alertService.presentToast(this.translateService.instant('alerts.signup_error_2'));
return;
}
if (password !== password1) {
this.alertService.presentToast(this.translateService.instant('alerts.signup_error_1'));
return;
}
this.loading = await this.loadingCtrl.create();
await this.loading.present();
try {
// Firebase crea la cuenta con email + password
const idToken = await this.firebaseAuthService.registerWithEmail(email, password);
// Backend crea el perfil y devuelve el token de la app
this.authService.registerWithFirebase(idToken, name, phone, email).subscribe(
(data: any) => {},
(error: any) => {
if (this.loading) this.loading.dismiss();
if (error['status'] === 422) {
this.alertService.presentToast(this.translateService.instant('alerts.signup_error'));
} else {
this.alertService.presentToast(this.translateService.instant('alerts.error') + error['message']);
}
},
() => {
if (this.loading) this.loading.dismiss();
this.alertService.presentToast(this.translateService.instant('alerts.signup'));
this.navCtrl.navigateRoot('/dashboard');
}
);
} catch (e: any) {
if (this.loading) this.loading.dismiss();
if (e.code === 'auth/email-already-in-use') {
this.alertService.presentToast(this.translateService.instant('alerts.signup_error'));
} else {
this.alertService.presentToast(this.translateService.instant('alerts.error') + e.message);
}
}
} }
async openTerms() { async openTerms() {

View File

@@ -6,6 +6,10 @@ ion-footer {
--ion-background-color: #175ccc; --ion-background-color: #175ccc;
} }
ion-footer ion-button::part(native) {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
ion-header ion-toolbar { ion-header ion-toolbar {
--min-height: 60px; --min-height: 60px;
} }

View File

@@ -14,7 +14,7 @@
"login": "Log in", "login": "Log in",
"signup": "Sign up", "signup": "Sign up",
"forgot": "Forgot your password?", "forgot": "Forgot your password?",
"fb_login": "Continue with Facebook", "apple_login": "Continue with Apple",
"google_login": "Continue with Google", "google_login": "Continue with Google",
"email": "Email", "email": "Email",
"password": "Password", "password": "Password",
@@ -142,7 +142,12 @@
"keywords_hint": "Separate keywords with commas", "keywords_hint": "Separate keywords with commas",
"address": "Address", "address": "Address",
"discover": "How did you hear about us?", "discover": "How did you hear about us?",
"select": "Select",
"discover_details": "Please specify how you found out about us", "discover_details": "Please specify how you found out about us",
"bank": "Bank",
"bank_account": "Bank account",
"fee": "Minimum fee",
"required": "Field required",
"signup": "Sign up", "signup": "Sign up",
"radio": "Radio", "radio": "Radio",
"tv": "TV", "tv": "TV",

View File

@@ -14,7 +14,7 @@
"login": "Iniciar sesión", "login": "Iniciar sesión",
"signup": "Registrate", "signup": "Registrate",
"forgot": "¿Olvidó su contraseña?", "forgot": "¿Olvidó su contraseña?",
"fb_login": "Iniciar sesión con Facebook", "apple_login": "Iniciar sesión con Apple",
"google_login": "Iniciar sesión con Google", "google_login": "Iniciar sesión con Google",
"email": "Correo electrónico", "email": "Correo electrónico",
"password": "Contraseña", "password": "Contraseña",
@@ -142,7 +142,12 @@
"keywords_hint": "Separa las palabras clave con comas", "keywords_hint": "Separa las palabras clave con comas",
"address": "Dirección", "address": "Dirección",
"discover": "¿Cómo supiste de nosotros?", "discover": "¿Cómo supiste de nosotros?",
"select": "Seleccionar",
"discover_details": "Específica como supiste de nosotros", "discover_details": "Específica como supiste de nosotros",
"bank": "Banco",
"bank_account": "CLABE Interbancaria",
"fee": "Cuota mínima",
"required": "Campo requerido",
"signup": "Registrarse", "signup": "Registrarse",
"radio": "Radio", "radio": "Radio",
"tv": "TV", "tv": "TV",