diff --git a/btc-UI/src/app/login/login.component.ts b/btc-UI/src/app/login/login.component.ts index cdbe822..56a72d5 100755 --- a/btc-UI/src/app/login/login.component.ts +++ b/btc-UI/src/app/login/login.component.ts @@ -15,7 +15,8 @@ import { import { CommonModule } from '@angular/common'; import { Router } from '@angular/router'; import { BtcService } from '../service/btc.service'; -import { HttpClientModule, HttpResponse } from '@angular/common/http'; +import { HttpClientModule, HttpClient } from '@angular/common/http'; +import { lastValueFrom } from 'rxjs'; @Component({ selector: 'app-login', @@ -44,7 +45,8 @@ export class LoginComponent implements OnInit, OnDestroy { private fb: FormBuilder, private cdRef: ChangeDetectorRef, private router: Router, - private btcService: BtcService + private btcService: BtcService, + private http: HttpClient ) { this.loginForm = this.fb.group({ email: ['', Validators.required], @@ -108,10 +110,10 @@ export class LoginComponent implements OnInit, OnDestroy { private toPrintableChar(ev: KeyboardEvent): string { if (ev.key.length === 1) return ev.key; if (ev.key === 'Process') { - if (ev.keyCode >= 48 && ev.keyCode <= 57) - return String.fromCharCode(ev.keyCode); - if (ev.keyCode >= 65 && ev.keyCode <= 90) - return String.fromCharCode(ev.keyCode); + // @ts-ignore - keyCode legacy + const kc = ev.keyCode; + if (kc >= 48 && kc <= 57) return String.fromCharCode(kc); + if (kc >= 65 && kc <= 90) return String.fromCharCode(kc); } return ''; } @@ -212,101 +214,149 @@ export class LoginComponent implements OnInit, OnDestroy { resetUserPassMessage() { this.passwordStatus = true; } -//--------------------------------------NEW LOGIN LOGIC ----------------------------------------// -//--------------------------------------NEW LOGIN LOGIC ----------------------------------------// -async onSubmit(): Promise { - if (this.loginForm.invalid) { - this.loginForm.markAllAsTouched(); - return; - } - const { email, password } = this.loginForm.value; + /** + * COMPLETE LOGIN LOGIC - NO FALLBACKS + * Will fail if BTID cannot be fetched from Electron API + */ + async onSubmit(): Promise { + // validate form + if (this.loginForm.invalid) { + this.loginForm.markAllAsTouched(); + return; + } - // βœ… Await service (since it’s async) - (await this.btcService.userLogin(email, password)).subscribe({ - next: (response) => { - const employee = response.body; - console.log('🧠 Raw employee response:', employee); + this.loginError = null; + this.passwordStatus = true; - const userName = employee?.userName || 'Unknown User'; - const employeeId = employee?.userIdNumber || email; + const email = (this.loginForm.get('email')!.value || '').toString().trim(); + const password = (this.loginForm.get('password')!.value || '').toString(); - console.log('βœ… Parsed name:', userName); - console.log('βœ… Parsed ID:', employeeId); + // βœ… CRITICAL: Load BTID FIRST - This will throw if fails + let fetchedBtid: string; + try { + console.log('πŸ”„ Fetching BTID from Electron API...'); + await this.btcService.loadBtid(); + + fetchedBtid = this.btcService.btid!; + if (!fetchedBtid) { + throw new Error('BTID is null after fetch'); + } + + console.log('βœ… BTID successfully fetched:', fetchedBtid); + } catch (btidError: any) { + console.error('❌ BTID fetch failed:', btidError); + this.loginError = `Failed to load BTID: ${btidError.message}. Please check Electron connection and try again.`; + this.passwordStatus = false; + return; // STOP LOGIN - No fallback allowed + } - this.passwordStatus = true; + // βœ… Build payload with REAL BTID - NO FALLBACK + const payload = { + opCard: email, + password: password, + btId: fetchedBtid, // Real BTID only + usrId: '', + btMake: 0, + }; - // βœ… Get the BTID the service fetched - const btid = this.btcService.btid; - console.log("πŸ“¦ BTID from file (via service):", btid); + console.log('πŸ“¦ Login payload being sent:', payload); + try { + // 1) POST to login endpoint + const loginUrl = 'http://localhost:8080/login'; + const loginResp: any = await lastValueFrom( + this.http.post(loginUrl, payload, { observe: 'body' }) + ); + console.log('🧠 Login response:', loginResp); + + // Store response safely + try { + localStorage.setItem('loginRes', JSON.stringify(loginResp)); + console.log('βœ… Full login response saved'); + } catch (e) { + console.error('❌ Failed to stringify login response:', e); + localStorage.setItem('loginRes', JSON.stringify(loginResp, Object.getOwnPropertyNames(loginResp))); + } + + // Parse username from response + const rawLoginRes = localStorage.getItem('loginRes'); + if (rawLoginRes) { + try { + const parsed = JSON.parse(rawLoginRes); + const username = parsed?.log?.cUsrNm || 'Unknown User'; + console.log('πŸ§‘ Username from loginRes:', username); + localStorage.setItem('userName', username); + } catch (e) { + console.error('❌ Failed to parse loginRes from localStorage', e); + } + } + + // βœ… Save REAL BTID to localStorage for second screen + localStorage.setItem('btid', fetchedBtid); + console.log('πŸ’Ύ BTID saved to localStorage:', fetchedBtid); + + // Prepare print payload const printData = { - name: userName, - employeeId: employeeId, + name: localStorage.getItem('userName') || 'Unknown User', + employeeId: email, // Use email as employee ID action: 'login', - type: 'login' + type: 'login', }; - // βœ… Store in localStorage - localStorage.setItem('userName', userName); - localStorage.setItem('employeeId', employeeId); - localStorage.setItem('password', password); - localStorage.setItem('btid', btid || "unknown"); + // 2) Print (commented out - uncomment if needed) + /* + const printRes = await fetch('http://localhost:9100/print', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(printData), + }); - // βœ… Fetch race card after login success - this.btcService - .fetchRaceCard("021804111066", password, btid || "0483") // pass correct payload here - .subscribe({ - next: (rpinfo) => { - console.log("πŸ“¦ Race Card:", rpinfo); + if (!printRes.ok) { + throw new Error(`Print failed (${printRes.status})`); + } + console.log('πŸ–¨οΈ Print successful'); + */ - // Save in localStorage for later use - localStorage.setItem('rpinfo', JSON.stringify(rpinfo)); + // 3) Fetch race card with DYNAMIC values - NO FALLBACKS + console.log('πŸ”„ Fetching race card with:', { opCard: email, password, btId: fetchedBtid }); + const rpInfo = await lastValueFrom( + this.btcService.fetchRaceCard(email, password, fetchedBtid) // Dynamic opCard, real BTID + ); - // βœ… Navigate once race card stored - (window as any).electronAPI?.openSecondScreen?.(); - this.router.navigate(['/home']); - }, - error: (err) => { - console.error("‼️ Failed to fetch race card", err); - this.loginError = "Could not load race card."; - } - }); - // βœ… Print first β€” login only if printing succeeds - fetch('http://localhost:9100/print', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(printData), - }) - .then((res) => { - if (!res.ok) throw new Error('Print failed'); - console.log('πŸ–¨οΈ Print successful'); + console.log('πŸ“¦ Race Card fetched successfully:', rpInfo); + localStorage.setItem('rpinfo', JSON.stringify(rpInfo)); - // βœ… Only here we allow login - (window as any).electronAPI?.openSecondScreen?.(); - this.router.navigate(['/home']); - }) - .catch((err) => { - console.error('‼️ Print failed', err); - this.loginError = 'Login failed: printing service unavailable.'; - this.passwordStatus = false; // reset status - }); - - }, - error: () => { - this.loginError = 'Invalid login credentials'; + // 4) Navigate to second screen + console.log('πŸš€ Navigating to home with BTID:', fetchedBtid); + (window as any).electronAPI?.openSecondScreen?.(); + this.router.navigate(['/home']); + + } catch (err: any) { + console.error('❌ Login flow failed:', err); + + // Specific error handling + if (err?.message?.includes('Print failed')) { + this.loginError = 'Login failed: printing service unavailable.'; + } else if (err?.status === 401 || err?.status === 400) { + this.loginError = 'Invalid login credentials'; + } else if (err?.message?.includes('BTID')) { + this.loginError = `BTID Error: ${err.message}`; + } else { + this.loginError = err?.message || 'Login failed. Please try again.'; + } + + this.passwordStatus = false; } - }); -} + } - showConfirmModal : boolean = false; + showConfirmModal: boolean = false; - confirmShutdown() : void{ - this.showConfirmModal = false; - this.shutdownSystem(); - - } + confirmShutdown(): void { + this.showConfirmModal = false; + this.shutdownSystem(); + } shutdownSystem(): void { fetch('http://localhost:3000/shutdown', { @@ -320,4 +370,4 @@ async onSubmit(): Promise { console.error('‼️ Failed to shutdown', err); }); } -} +} \ No newline at end of file diff --git a/btc-UI/src/app/service/btc.service.ts b/btc-UI/src/app/service/btc.service.ts index ff98076..2457aa6 100755 --- a/btc-UI/src/app/service/btc.service.ts +++ b/btc-UI/src/app/service/btc.service.ts @@ -1,109 +1,7 @@ -// import { HttpClient, HttpHeaders } from '@angular/common/http'; -// import { Injectable } from '@angular/core'; -// import { ApplicationHttpRouts } from '../constants/http-routs'; - -// @Injectable({ -// providedIn: 'root', -// }) - -// // export class BtcService { -// // constructor(private http: HttpClient) {} -// // btid: string | null = null; -// // //user login -// // public userLogin(employeeId: string, password: string) { -// // console.log('Login route ' + ApplicationHttpRouts.LOG_IN); -// // this.btid = localStorage.getItem('btid'); -// // if (this.btid) { -// // employeeId += "," + this.btid; -// // } else { -// // employeeId += ",null"; // or just "," if you don’t want "null" -// // } -// // return this.http.get(ApplicationHttpRouts.LOG_IN, { -// // headers: this.basicAuthCredentialsBuilder(employeeId, password), -// // withCredentials: true, -// // observe: 'response', -// // }); -// // } - -// export class BtcService { -// constructor(private http: HttpClient) {} -// btid: string | null = null; - -// // user login -// public async userLogin(employeeId: string, password: string) { -// console.log('Login route ' + ApplicationHttpRouts.LOG_IN); - -// try { -// if ((window as any).electronAPI?.getBtid) { -// this.btid = await (window as any).electronAPI.getBtid(); -// } else { -// console.warn('Electron API not available β€” fallback to null'); -// this.btid = null; -// } -// } catch (err) { -// console.error('Error fetching BTID:', err); -// this.btid = null; -// } - -// // Append BTID after comma -// if (this.btid) { -// employeeId += ',' + this.btid; -// } else { -// employeeId += ',null'; -// } - -// return this.http.get(ApplicationHttpRouts.LOG_IN, { -// headers: this.basicAuthCredentialsBuilder(employeeId, password), -// withCredentials: true, -// observe: 'response', -// }); -// } - -// // what goes to the backend for auth ... is the same as postman's basic auth -// private basicAuthCredentialsBuilder( -// employeeOrUserId: string, -// password: string -// ): HttpHeaders { -// console.log(`username and password${employeeOrUserId} p = ${password}`); - -// return new HttpHeaders().set( -// 'Authorization', -// 'basic ' + window.btoa(employeeOrUserId + ':' + password) -// ); -// } - -// public pingLiveStatus() { -// return this.http.get(ApplicationHttpRouts.PING, { -// withCredentials: true, -// observe: 'response', -// responseType: 'text' as 'json', -// }); -// } - -// // Fetch all race events today - -// public getAllRaceEventsToday() { -// return this.http.get(ApplicationHttpRouts.RACE_EVENTS_TODAY, { -// withCredentials: true, -// observe: 'response', -// responseType: 'json', -// }); -// } - -// public getRaceCard(){ -// return this.http.get(ApplicationHttpRouts.RACE_CARD, { -// withCredentials: true, -// observe: 'response', -// responseType: 'json', -// }) -// } -// } - - import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { ApplicationHttpRouts } from '../constants/http-routs'; -import { of,Observable, tap } from 'rxjs'; +import { of, Observable, tap } from 'rxjs'; @Injectable({ providedIn: 'root', @@ -111,25 +9,51 @@ import { of,Observable, tap } from 'rxjs'; export class BtcService { constructor(private http: HttpClient) {} btid: string | null = null; - private raceCard: any = null; // cache race card in memory - - // user login - public async userLogin(employeeId: string, password: string) { - console.log('Login route ' + ApplicationHttpRouts.LOG_IN); + private raceCard: any = null; + /** + * Load BTID from Electron API (no fallback) + */ + public async loadBtid(): Promise { + console.log('Loading BTID...'); try { if ((window as any).electronAPI?.getBtid) { this.btid = await (window as any).electronAPI.getBtid(); + if (!this.btid) { + throw new Error('BTID is empty or null from Electron API'); + } + console.log('βœ… BTID loaded:', this.btid); } else { - console.warn('Electron API not available β€” fallback to null'); - this.btid = null; + console.warn('Electron API not available'); + throw new Error('Electron API not available'); } } catch (err) { console.error('Error fetching BTID:', err); this.btid = null; + throw err; // Propagate error instead of setting fallback } + } + + /** + * Get BTID from memory or localStorage (for second screen) + */ + public getBtid(): string | null { + if (this.btid) { + return this.btid; + } + const storedBtid = localStorage.getItem('btid'); + if (storedBtid) { + this.btid = storedBtid; + return this.btid; + } + return null; + } + + public async userLogin(employeeId: string, password: string) { + console.log('Login route ' + ApplicationHttpRouts.LOG_IN); + + await this.loadBtid(); // Will throw if BTID fetch fails - // Append BTID after comma if (this.btid) { employeeId += ',' + this.btid; } else { @@ -143,20 +67,17 @@ export class BtcService { }); } - // basic auth header private basicAuthCredentialsBuilder( employeeOrUserId: string, password: string ): HttpHeaders { console.log(`username and password = ${employeeOrUserId} p = ${password}`); - return new HttpHeaders().set( 'Authorization', 'basic ' + window.btoa(employeeOrUserId + ':' + password) ); } - // Ping backend public pingLiveStatus() { return this.http.get(ApplicationHttpRouts.PING, { withCredentials: true, @@ -165,7 +86,6 @@ export class BtcService { }); } - // Fetch all race events today public getAllRaceEventsToday() { return this.http.get(ApplicationHttpRouts.RACE_EVENTS_TODAY, { withCredentials: true, @@ -174,37 +94,6 @@ export class BtcService { }); } - /** - * Fetch Race Card from backend (POST /download/rpinfo) - * Stores result in memory for reuse - // */ - // public fetchRaceCard( - // opCard: string, - // password: string, - // btId: string, - // usrId: string = '', - // btMake: number = 0 - // ): Observable { - // const payload = { opCard, password, btId, usrId, btMake }; - - // return this.http - // .post('http://localhost:8082/download/rpinfo', payload) - // .pipe( - // tap((data) => { - // console.log('πŸ“¦ Race Card fetched:', data); - // this.raceCard = data; // cache it - // }) - // ); - // } - - // /** - // * Return cached race card (if available) - // */ - // public getRaceCard(): any { - // return this.raceCard; - // } - - public fetchRaceCard( opCard: string, password: string, @@ -213,36 +102,26 @@ export class BtcService { btMake: number = 0 ): Observable { const payload = { opCard, password, btId, usrId, btMake }; - return this.http .post('http://localhost:8082/download/rpinfo', payload) .pipe( tap((data) => { console.log('πŸ“¦ Race Card fetched:', data); - this.raceCard = data; // store in memory - localStorage.setItem('rpinfo', JSON.stringify(data)); // store in localStorage + this.raceCard = data; + localStorage.setItem('rpinfo', JSON.stringify(data)); }) ); } - /** - * Return cached race card: - * - from memory (fastest) - * - fallback to localStorage (if reloaded) - */ public getRaceCard(): Observable { if (this.raceCard) { return of(this.raceCard); } - const cached = localStorage.getItem('rpinfo'); if (cached) { this.raceCard = JSON.parse(cached); return of(this.raceCard); } - - // Nothing available, return empty object return of(null); } -} - +} \ No newline at end of file