diff --git a/src/app/app.component.html b/src/app/app.component.html index ea8ee18..f5850bd 100755 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -8,12 +8,46 @@ - - - - - {{p.title}} - + + + + {{'menu.home' | translate}} + + + + + + {{'menu.cards' | translate}} + + + + + + {{'menu.postulations' | translate}} + + + + + + {{'menu.contracts' | translate}} + + + + + + {{'menu.faq' | translate}} + + + + + + {{'menu.start' | translate}} + + + + + + {{'menu.hero' | translate}} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 2eda5d1..262dae6 100755 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; import { Platform, NavController, LoadingController } from '@ionic/angular'; import { EventService } from './services/event.service'; import { StatusBar, Style } from '@capacitor/status-bar'; @@ -13,17 +13,17 @@ import { OneSignalService } from './services/onesignal.service'; @Component({ selector: 'app-root', - templateUrl: 'app.component.html' + templateUrl: 'app.component.html', + standalone: false }) -export class AppComponent { +export class AppComponent implements OnInit { private loading: any; - - public appPages: any[] = []; + userRole: number = 0; constructor( private platform: Platform, - private authService: AuthService, + public authService: AuthService, private languageService: LanguageService, private translateService: TranslateService, private navCtrl: NavController, @@ -31,75 +31,20 @@ export class AppComponent { private alertService: AlertService, private loadingCtrl: LoadingController, private oneSignalService: OneSignalService, + private cdr: ChangeDetectorRef, ) { this.initializeApp(); 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); - 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() { await this.platform.ready(); @@ -114,7 +59,6 @@ export class AppComponent { } this.languageService.getDefaultLanguage(); - this.authService.getToken(); } private async setupOneSignalUser(role: number) { diff --git a/src/app/pages/auth/forgot/forgot.page.html b/src/app/pages/auth/forgot/forgot.page.html index 754776c..d3185e3 100755 --- a/src/app/pages/auth/forgot/forgot.page.html +++ b/src/app/pages/auth/forgot/forgot.page.html @@ -8,8 +8,8 @@ {{'auth.forgot' | translate}} - -

{{'auth.forgot_instructions' | translate}}


+ +

{{'auth.forgot_instructions' | translate}}


{{'auth.email' | translate}} diff --git a/src/app/pages/auth/forgot/forgot.page.ts b/src/app/pages/auth/forgot/forgot.page.ts index 65519a9..dd3f7af 100755 --- a/src/app/pages/auth/forgot/forgot.page.ts +++ b/src/app/pages/auth/forgot/forgot.page.ts @@ -1,52 +1,52 @@ import { Component, OnInit } from '@angular/core'; import { NavController, LoadingController } from '@ionic/angular'; import { NgForm } from '@angular/forms'; -import { AuthService } from 'src/app/services/auth.service'; import { AlertService } from 'src/app/services/alert.service'; import { TranslateService } from '@ngx-translate/core'; +import { FirebaseAuthService } from 'src/app/services/firebase-auth.service'; @Component({ selector: 'app-forgot', templateUrl: './forgot.page.html', styleUrls: ['./forgot.page.scss'], + standalone: false }) export class ForgotPage implements OnInit { - private loading; + private loading: HTMLIonLoadingElement | null = null; constructor( - private authService: AuthService, + private firebaseAuthService: FirebaseAuthService, private navCtrl: NavController, private alertService: AlertService, private translateService: TranslateService, private loadingCtrl: LoadingController ) { } - ngOnInit() { - } - // Dismiss Forgot Modal + + ngOnInit() { } + dismissForgot() { this.navCtrl.back(); } - // On Register button tap, dismiss login modal and open register modal - forgot(form: NgForm) { - this.loadingCtrl.create().then((overlay) => { - this.loading = overlay; - this.loading.present(); - }); - this.authService.forgot(form.value.email).subscribe( - data => { - this.loading.dismiss(); + async forgot(form: NgForm) { + this.loading = await this.loadingCtrl.create(); + await this.loading.present(); + + try { + await this.firebaseAuthService.sendPasswordReset(form.value.email); + if (this.loading) 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')); - }, - error => { - this.loading.dismiss(); - this.alertService.presentToast(this.translateService.instant('alerts.error') + error['message']); - console.log(error); - }, - () => { this.dismissForgot(); + } else { + this.alertService.presentToast(this.translateService.instant('alerts.error') + e.message); } - ); + } } } diff --git a/src/app/pages/auth/login/login.page.html b/src/app/pages/auth/login/login.page.html index 79eeb8b..b9cbee8 100755 --- a/src/app/pages/auth/login/login.page.html +++ b/src/app/pages/auth/login/login.page.html @@ -8,7 +8,7 @@ {{'auth.login' | translate}} - + {{'auth.email' | translate}} @@ -22,11 +22,27 @@
{{'auth.login' | translate}} -

{{'auth.forgot' | translate}}

+

{{'auth.forgot' | translate}}



- {{'auth.fb_login' | translate}} - {{'auth.google_login' | translate}} -

{{'auth.signup_ad' | translate}}

+ + + + + +

{{'auth.signup_ad' | translate}}

{{'auth.signup' | translate}} -

{{'auth.terms_1' | translate}}{{'auth.terms_2' | translate}}{{'auth.terms_3' | translate}}

+

{{'auth.terms_1' | translate}}{{'auth.terms_2' | translate}}{{'auth.terms_3' | translate}}

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

-

{{'auth.terms_1' | translate}}{{'auth.terms_2' | translate}}{{'auth.terms_3' | translate}}

+

{{'auth.terms_1' | translate}}{{'auth.terms_2' | translate}}{{'auth.terms_3' | translate}}

@@ -48,6 +48,6 @@
{{'auth.signup' | translate}} -

{{'auth.login_ad' | translate}}

+

{{'auth.login_ad' | translate}}

{{'auth.login' | translate}}
diff --git a/src/app/pages/auth/register/register.page.ts b/src/app/pages/auth/register/register.page.ts index 0841664..1e41c6e 100755 --- a/src/app/pages/auth/register/register.page.ts +++ b/src/app/pages/auth/register/register.page.ts @@ -4,85 +4,93 @@ import { AuthService } from 'src/app/services/auth.service'; import { NgForm } from '@angular/forms'; import { AlertService } from 'src/app/services/alert.service'; import { TranslateService } from '@ngx-translate/core'; +import { FirebaseAuthService } from 'src/app/services/firebase-auth.service'; import { Browser } from '@capacitor/browser'; @Component({ selector: 'app-register', templateUrl: './register.page.html', styleUrls: ['./register.page.scss'], + standalone: false }) export class RegisterPage implements OnInit { - loading: any = null; + loading: HTMLIonLoadingElement | null = null; terms = false; constructor( private authService: AuthService, + private firebaseAuthService: FirebaseAuthService, private translateService: TranslateService, private navCtrl: NavController, private loadingCtrl: LoadingController, private alertService: AlertService ) { } - ngOnInit() { - } - // Dismiss Register Modal + + ngOnInit() { } + dismissRegister() { this.navCtrl.navigateRoot('/landing'); } - // On Login button tap, dismiss Register modal and open login Modal + loginModal() { this.navCtrl.navigateForward('/login'); } - updateChecked () { - if (this.terms == true) { - this.terms = false; - console.log(this.terms); - } else { - this.terms = true; - console.log(this.terms); - } + updateChecked() { + this.terms = !this.terms; } - register(form: NgForm) { - - 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 { + async register(form: NgForm) { + if (!this.terms) { 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() { diff --git a/src/app/pages/landing/landing.page.scss b/src/app/pages/landing/landing.page.scss index 517ab70..44150e3 100755 --- a/src/app/pages/landing/landing.page.scss +++ b/src/app/pages/landing/landing.page.scss @@ -6,6 +6,10 @@ ion-footer { --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 { --min-height: 60px; } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index aea19fb..56770d4 100755 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -14,7 +14,7 @@ "login": "Log in", "signup": "Sign up", "forgot": "Forgot your password?", - "fb_login": "Continue with Facebook", + "apple_login": "Continue with Apple", "google_login": "Continue with Google", "email": "Email", "password": "Password", @@ -142,7 +142,12 @@ "keywords_hint": "Separate keywords with commas", "address": "Address", "discover": "How did you hear about us?", + "select": "Select", "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", "radio": "Radio", "tv": "TV", diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index 66ad6cf..d8e829d 100755 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -14,7 +14,7 @@ "login": "Iniciar sesión", "signup": "Registrate", "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", "email": "Correo electrónico", "password": "Contraseña", @@ -142,7 +142,12 @@ "keywords_hint": "Separa las palabras clave con comas", "address": "Dirección", "discover": "¿Cómo supiste de nosotros?", + "select": "Seleccionar", "discover_details": "Específica como supiste de nosotros", + "bank": "Banco", + "bank_account": "CLABE Interbancaria", + "fee": "Cuota mínima", + "required": "Campo requerido", "signup": "Registrarse", "radio": "Radio", "tv": "TV",