diff --git a/btc-UI/electron/main.js b/btc-UI/electron/main.js index 35c3692..a0e5178 100644 --- a/btc-UI/electron/main.js +++ b/btc-UI/electron/main.js @@ -8,6 +8,7 @@ let currentSharedData = null; // Store latest data for initial sync let currentSelectedVenue = 'Select Venue'; let currentSelectedRace = 1; let currentStopMessage = ''; +let currentBtid = null; // Store in-memory BTID function createWindows() { const displays = screen.getAllDisplays(); @@ -42,6 +43,9 @@ function createWindows() { screenWindow.loadURL('http://10.150.40.124:4200/shared-display'); + // Debugging: Open DevTools for second screen to verify logs + // screenWindow.webContents.openDevTools({ mode: 'detach' }); + // Handle opening second screen and send initial data ipcMain.on('open-second-screen', () => { screenWindow.show(); @@ -52,6 +56,7 @@ function createWindows() { screenWindow.webContents.send('update-selected-venue', currentSelectedVenue); screenWindow.webContents.send('update-selected-race', currentSelectedRace); screenWindow.webContents.send('update-stop-message', currentStopMessage); + screenWindow.webContents.send('update-btid', currentBtid); } }); @@ -81,7 +86,7 @@ function createWindows() { } }); - // Add this new handler + // Handle stop message sync ipcMain.on('sync-stop-message', (event, message) => { currentStopMessage = message; if (screenWindow && screenWindow.webContents) { @@ -89,7 +94,16 @@ function createWindows() { } }); - // Handle BTID request + // Handle BTID sync + ipcMain.on('sync-btid', (event, btid) => { + console.log('[MAIN] sync-btid received:', btid); + currentBtid = btid; // Store in-memory BTID + if (screenWindow && screenWindow.webContents) { + screenWindow.webContents.send('update-btid', btid); + } + }); + + // Handle BTID request from file ipcMain.handle('get-btid', () => { try { const filePath = path.join(process.env.HOME || process.env.USERPROFILE, 'BTID', 'betting.txt'); @@ -97,10 +111,16 @@ function createWindows() { const match = content.match(/Btid\s*=\s*(\d+)/i); return match ? match[1] : null; } catch (err) { - console.error('Error reading betting.txt:', err); + console.error('[MAIN] Error reading betting.txt:', err); return null; } }); + + // Allow renderers to request current in-memory BTID on demand + ipcMain.handle('request-current-btid', async () => { + console.log('[MAIN] request-current-btid =>', currentBtid); + return currentBtid || null; + }); } app.whenReady().then(createWindows); diff --git a/btc-UI/electron/preload.js b/btc-UI/electron/preload.js index afc5373..2a4fbc0 100644 --- a/btc-UI/electron/preload.js +++ b/btc-UI/electron/preload.js @@ -4,13 +4,15 @@ contextBridge.exposeInMainWorld('electronAPI', { openSecondScreen: () => ipcRenderer.send('open-second-screen'), closeSecondScreen: () => ipcRenderer.send('close-second-screen'), getBtid: () => ipcRenderer.invoke('get-btid'), + getCurrentBtid: () => ipcRenderer.invoke('request-current-btid'), // NEW: Request in-memory BTID syncSharedData: (data) => ipcRenderer.send('sync-shared-data', data), onUpdateSharedData: (callback) => ipcRenderer.on('update-shared-data', (event, data) => callback(data)), - // Add these new lines syncStopMessage: (message) => ipcRenderer.send('sync-stop-message', message), onUpdateStopMessage: (callback) => ipcRenderer.on('update-stop-message', (event, message) => callback(message)), syncSelectedVenue: (venue) => ipcRenderer.send('sync-selected-venue', venue), onUpdateSelectedVenue: (callback) => ipcRenderer.on('update-selected-venue', (event, venue) => callback(venue)), syncSelectedRace: (race) => ipcRenderer.send('sync-selected-race', race), onUpdateSelectedRace: (callback) => ipcRenderer.on('update-selected-race', (event, race) => callback(race)), + syncBtid: (btid) => ipcRenderer.send('sync-btid', btid), // NEW: Send BTID to main + onUpdateBtid: (callback) => ipcRenderer.on('update-btid', (event, btid) => callback(btid)), // NEW: Listen for BTID updates }); \ No newline at end of file diff --git a/btc-UI/src/app/components/shared-display/shared-display.component.ts b/btc-UI/src/app/components/shared-display/shared-display.component.ts index 99c083f..4029add 100644 --- a/btc-UI/src/app/components/shared-display/shared-display.component.ts +++ b/btc-UI/src/app/components/shared-display/shared-display.component.ts @@ -1,4 +1,4 @@ -// shared-display.component.ts +// shared-display.component.ts (updated ngOnInit to read initial values from localStorage) import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedStateService } from '../../service/shared-state.service'; @@ -13,7 +13,6 @@ interface SharedData { salesTotal: number; receiveTotal: number; totalClicks: number; - } @Component({ @@ -51,11 +50,66 @@ export class SharedDisplayComponent implements OnInit { ) {} ngOnInit(): void { - console.log('[SHARED DISPLAY] Initializing, electronAPI available:', !!window.electronAPI); - this.btid = localStorage.getItem('btid'); + console.log('[SHARED DISPLAY] Initializing, electronAPI available:', !!(window as any).electronAPI); + + // NEW: Read initial values from localStorage (set by login) + this.btid = localStorage.getItem('btid') || null; + this.selectedVenue = localStorage.getItem('selectedVenue') || this.selectedVenue; + this.selectedRace = localStorage.getItem('selectedRace') || this.selectedRace; + console.log('[SHARED DISPLAY] Initial values from localStorage:', { btid: this.btid, selectedVenue: this.selectedVenue, selectedRace: this.selectedRace }); const electronAPI = (window as any).electronAPI; - if (electronAPI) { + if (!electronAPI) { + console.error('[SHARED DISPLAY] electronAPI not available'); + return; + } + + // 1) Ask main process for the current in-memory BTID (avoid race) + if (electronAPI.getCurrentBtid) { + try { + electronAPI.getCurrentBtid().then((btidFromMain: string | null) => { + console.log('[SHARED DISPLAY] getCurrentBtid returned:', btidFromMain); + if (btidFromMain) { + this.btid = btidFromMain; + try { localStorage.setItem('btid', btidFromMain); } catch (e) { /* ignore */ } + this.cdRef.detectChanges(); + } + }).catch((err: any) => { + console.warn('[SHARED DISPLAY] getCurrentBtid failed:', err); + }); + } catch (err) { + console.warn('[SHARED DISPLAY] getCurrentBtid exception:', err); + } + } else if (electronAPI.getBtid) { + // Fallback to existing getBtid (reads file via main handler) + electronAPI.getBtid().then((btidFromFile: string | null) => { + console.log('[SHARED DISPLAY] getBtid returned:', btidFromFile); + if (btidFromFile) { + this.btid = btidFromFile; + try { localStorage.setItem('btid', btidFromFile); } catch (e) { /* ignore */ } + this.cdRef.detectChanges(); + } + }).catch((err: any) => { + console.warn('[SHARED DISPLAY] getBtid failed:', err); + }); + } + + // 2) Listen for update-btid events (handles later syncs) + if (electronAPI.onUpdateBtid) { + electronAPI.onUpdateBtid((btid: string) => { + console.log('[SHARED DISPLAY] Received update-btid:', btid); + if (btid) { + this.btid = btid; + try { localStorage.setItem('btid', btid); } catch (e) { /* ignore */ } + this.cdRef.detectChanges(); + } + }); + } else { + console.warn('[SHARED DISPLAY] onUpdateBtid not available'); + } + + // Existing shared-data / venue / race listeners + if (electronAPI.onUpdateSharedData) { electronAPI.onUpdateSharedData((data: SharedData) => { console.log('[SHARED DISPLAY] Received IPC data:', data); if (!data) return; @@ -68,20 +122,32 @@ export class SharedDisplayComponent implements OnInit { console.log('[SHARED DISPLAY] Updated filledRows:', this.filledRows.map(row => ({ ...row, numbers: JSON.stringify(row.numbers) }))); this.cdRef.detectChanges(); }); + } + if (electronAPI.onUpdateSelectedVenue) { electronAPI.onUpdateSelectedVenue((venue: string) => { this.selectedVenue = venue; console.log('[SHARED DISPLAY] Venue updated via IPC to:', this.selectedVenue); + try { localStorage.setItem('selectedVenue', venue); } catch (e) { /* ignore */ } this.cdRef.detectChanges(); }); + } + if (electronAPI.onUpdateSelectedRace) { electronAPI.onUpdateSelectedRace((race: number) => { this.selectedRace = race.toString(); console.log('[SHARED DISPLAY] Race updated via IPC to:', this.selectedRace); + try { localStorage.setItem('selectedRace', race.toString()); } catch (e) { /* ignore */ } + this.cdRef.detectChanges(); + }); + } + + if (electronAPI.onUpdateStopMessage) { + electronAPI.onUpdateStopMessage((msg: string) => { + console.log('[SHARED DISPLAY] Stop message received:', msg); + // Optionally handle display of stop message this.cdRef.detectChanges(); }); - } else { - console.error('[SHARED DISPLAY] electronAPI not available'); } } } \ No newline at end of file diff --git a/btc-UI/src/app/components/shared-table/shared-table.component.css b/btc-UI/src/app/components/shared-table/shared-table.component.css index 5fc5a90..e865f9e 100644 --- a/btc-UI/src/app/components/shared-table/shared-table.component.css +++ b/btc-UI/src/app/components/shared-table/shared-table.component.css @@ -168,7 +168,7 @@ div[style*="background-color: black"] .custom-cell { overflow: hidden; box-sizing: border-box; animation: scroll 20s linear infinite; /* Adjust duration for speed */ - width: 55%; /* Ensure it takes full width */ + width: 100%; /* Ensure it takes full width */ } @keyframes scroll { diff --git a/btc-UI/src/app/login/login.component.ts b/btc-UI/src/app/login/login.component.ts index 56a72d5..3191635 100755 --- a/btc-UI/src/app/login/login.component.ts +++ b/btc-UI/src/app/login/login.component.ts @@ -220,137 +220,156 @@ export class LoginComponent implements OnInit, OnDestroy { * Will fail if BTID cannot be fetched from Electron API */ async onSubmit(): Promise { - // validate form - if (this.loginForm.invalid) { - this.loginForm.markAllAsTouched(); - return; - } - - this.loginError = null; - this.passwordStatus = true; - - const email = (this.loginForm.get('email')!.value || '').toString().trim(); - const password = (this.loginForm.get('password')!.value || '').toString(); - - // ✅ 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 - } - - // ✅ Build payload with REAL BTID - NO FALLBACK - const payload = { - opCard: email, - password: password, - btId: fetchedBtid, // Real BTID only - usrId: '', - btMake: 0, - }; - - 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: localStorage.getItem('userName') || 'Unknown User', - employeeId: email, // Use email as employee ID - action: 'login', - type: 'login', - }; - - // 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), - }); - - if (!printRes.ok) { - throw new Error(`Print failed (${printRes.status})`); - } - console.log('🖨️ Print successful'); - */ - - // 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 - ); - - console.log('📦 Race Card fetched successfully:', rpInfo); - localStorage.setItem('rpinfo', JSON.stringify(rpInfo)); - - // 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; - } + // validate form + if (this.loginForm.invalid) { + this.loginForm.markAllAsTouched(); + return; } + this.loginError = null; + this.passwordStatus = true; + + const email = (this.loginForm.get('email')!.value || '').toString().trim(); + const password = (this.loginForm.get('password')!.value || '').toString(); + + // ✅ 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 + } + + // ✅ Build payload with REAL BTID - NO FALLBACK + const payload = { + opCard: email, + password: password, + btId: fetchedBtid, // Real BTID only + usrId: '', + btMake: 0, + }; + + 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); + + // NEW: Sync BTID to main process for immediate second screen access via getCurrentBtid + if ((window as any).electronAPI?.syncBtid) { + (window as any).electronAPI.syncBtid(fetchedBtid); + console.log('📡 BTID synced to main process via IPC'); + } + + // Prepare print payload + const printData = { + name: localStorage.getItem('userName') || 'Unknown User', + employeeId: email, // Use email as employee ID + action: 'login', + type: 'login', + }; + + // 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), + }); + + if (!printRes.ok) { + throw new Error(`Print failed (${printRes.status})`); + } + console.log('🖨️ Print successful'); + */ + + // 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 + ); + + console.log('📦 Race Card fetched successfully:', rpInfo); + localStorage.setItem('rpinfo', JSON.stringify(rpInfo)); + + // NEW: Set initial race from rpInfo to localStorage for second screen initial display + // Adjust these extractions based on the actual structure of rpInfo (e.g., rpInfo.currentRace) + const initialRace = rpInfo.race || rpInfo.currentRace || 1; // Example: Adjust path to your rpInfo structure + localStorage.setItem('selectedRace', initialRace.toString()); + console.log('💾 Initial race saved to localStorage:', { initialRace }); + + // NEW: Sync initial race to main process (for currentSelectedRace in main.js) + if ((window as any).electronAPI) { + if ((window as any).electronAPI.syncSelectedRace) { + (window as any).electronAPI.syncSelectedRace(initialRace); + console.log('📡 Initial race synced to main process via IPC'); + } + } + + // 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; confirmShutdown(): void { diff --git a/btc-UI/src/app/service/stopbet.service.ts b/btc-UI/src/app/service/stopbet.service.ts index 5cec3cf..e157b34 100644 --- a/btc-UI/src/app/service/stopbet.service.ts +++ b/btc-UI/src/app/service/stopbet.service.ts @@ -169,7 +169,7 @@ export class StopbetService implements OnDestroy { } this.http - .get(`http://localhost:8080/stopbet/raw?venue=${this.currentVenue}&date=${this.currentDate}`) + .get(`http://localhost:8089/stopbet/raw?venue=${this.currentVenue}&date=${this.currentDate}`) .subscribe({ next: (res: any) => { if (res && res.ok && res.data) { @@ -205,7 +205,7 @@ export class StopbetService implements OnDestroy { } try { - this.eventSource = new EventSource('http://localhost:8080/stopbet/stream'); + this.eventSource = new EventSource('http://localhost:8089/stopbet/stream'); } catch (err) { console.error('[STOPBET] Failed to create EventSource:', err); return; diff --git a/btc-UI/src/electron.d.ts b/btc-UI/src/electron.d.ts index 698ee3b..bfa3a11 100644 --- a/btc-UI/src/electron.d.ts +++ b/btc-UI/src/electron.d.ts @@ -2,15 +2,17 @@ interface ElectronAPI { openSecondScreen: () => void; closeSecondScreen: () => void; getBtid: () => Promise; + getCurrentBtid: () => Promise; // NEW: Request in-memory BTID syncSharedData: (data: SharedData) => void; onUpdateSharedData: (callback: (data: SharedData) => void) => void; - // Add these new lines syncStopMessage: (message: string) => void; onUpdateStopMessage: (callback: (message: string) => void) => void; syncSelectedVenue: (venue: string) => void; onUpdateSelectedVenue: (callback: (venue: string) => void) => void; syncSelectedRace: (race: number) => void; onUpdateSelectedRace: (callback: (race: number) => void) => void; + syncBtid: (btid: string) => void; // NEW: Send BTID to main + onUpdateBtid: (callback: (btid: string) => void) => void; // NEW: Listen for BTID updates } interface SharedData {