feat : race number changes automatically according to the stopbet

This commit is contained in:
karthik 2025-09-15 15:05:00 +05:30
parent ff263099c7
commit 3486b342d7

View File

@ -22,10 +22,9 @@ export class NavbarComponent implements OnInit, OnDestroy {
btid: string | null = null; btid: string | null = null;
// component properties - add these at top-level in the component class // component properties - add these at top-level in the component class
consecTimeouts = 0; consecTimeouts = 0;
maxConsecTimeouts = 2; maxConsecTimeouts = 2;
liveStatusOk = true; liveStatusOk = true;
showVenueModal = false; showVenueModal = false;
showRaceModal = false; showRaceModal = false;
@ -77,6 +76,12 @@ liveStatusOk = true;
private http: HttpClient private http: HttpClient
) {} ) {}
// Stopbet-related properties
private stopbetStatuses: Map<number, string> = new Map(); // raceNum => 'Y'|'N'|'S'
private currentVenue: string = '';
private currentDate: string = '';
private eventSource?: EventSource;
ngOnInit() { ngOnInit() {
this.userName = localStorage.getItem('userName') || ''; this.userName = localStorage.getItem('userName') || '';
this.btid = localStorage.getItem('btid'); this.btid = localStorage.getItem('btid');
@ -117,14 +122,24 @@ liveStatusOk = true;
} }
} }
// Set current venue and date
this.currentVenue = (this.raceCardData?.venue ?? '').toUpperCase();
this.currentDate = this.getTodayDate();
// Setup stopbet if venue is available
if (this.currentVenue) {
this.fetchInitialStopbets();
this.setupSSE();
}
// Periodic ABS/latest polling // Periodic ABS/latest polling
this.subscription = interval(5000) this.subscription = interval(5000)
.pipe( .pipe(
switchMap(() => { switchMap(() => {
// console.log(`[ANGULAR] Fetching latest ABS status at ${new Date().toISOString()}`); // console.log(`[ANGULAR] Fetching latest ABS status at ${new Date().toISOString()}`);
return this.http.get('http://localhost:8080/abs/latest').pipe( return this.http.get('http://localhost:8080/abs/latest').pipe(
catchError((err) => { catchError((err) => {
// console.error('[ANGULAR] ABS latest fetch failed ❌', err); // console.error('[ANGULAR] ABS latest fetch failed ❌', err);
this.liveStatusOk = false; this.liveStatusOk = false;
return of(null); return of(null);
}) })
@ -133,7 +148,7 @@ liveStatusOk = true;
) )
.subscribe((res: any) => { .subscribe((res: any) => {
if (res) { if (res) {
// console.log('[ANGULAR] ABS latest response ✅', res); // console.log('[ANGULAR] ABS latest response ✅', res);
this.liveStatusOk = res.success === true; this.liveStatusOk = res.success === true;
// If backend eventually returns structured data: // If backend eventually returns structured data:
@ -185,6 +200,97 @@ liveStatusOk = true;
}); });
} }
private getTodayDate(): string {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${year}/${month}/${day}`;
}
private fetchInitialStopbets() {
this.http.get(`http://localhost:8080/stopbet/raw?venue=${this.currentVenue}&date=${this.currentDate}`).subscribe({
next: (res: any) => {
if (res.ok && res.data) {
const key = `${this.currentVenue}:${this.currentDate}`;
const meeting = res.data[key] || {};
for (const race in meeting) {
const raceNum = parseInt(race, 10);
this.stopbetStatuses.set(raceNum, meeting[race]);
}
// Check and switch if current race is stopped
this.checkAndSwitchIfStopped();
}
},
error: (err) => {
console.error('[STOPBET] Failed to fetch initial statuses:', err);
}
});
}
private setupSSE() {
if (this.eventSource) {
this.eventSource.close();
}
this.eventSource = new EventSource('http://localhost:8080/stopbet/stream');
this.eventSource.onmessage = (event) => {
this.zone.run(() => {
try {
const data = JSON.parse(event.data);
if (data.type === 'stopbet' && data.venue === this.currentVenue && data.date === this.currentDate) {
const raceNum = parseInt(data.race, 10);
this.stopbetStatuses.set(raceNum, data.status);
// If current selected race is affected and now stopped, switch
if (this.selectedRace === raceNum && !this.isOpen(raceNum)) {
this.checkAndSwitchIfStopped();
}
}
} catch (err) {
console.error('[STOPBET] SSE parse error:', err);
}
});
};
this.eventSource.onerror = (err) => {
console.error('[STOPBET] SSE error:', err);
};
}
private isOpen(race: number): boolean {
const status = this.stopbetStatuses.get(race);
return status === 'N' || status === undefined; // Assume open if unknown
}
private getMaxRaces(): number {
return this.raceCardData?.raceVenueRaces?.races?.length || 10;
}
private getOpenRaceStartingFrom(start: number): number {
const max = this.getMaxRaces();
// Try from start to max
for (let r = start; r <= max; r++) {
if (this.isOpen(r)) {
return r;
}
}
// If none after, try from 1 to start-1
for (let r = 1; r < start; r++) {
if (this.isOpen(r)) {
return r;
}
}
// All stopped, return original start
return start;
}
private checkAndSwitchIfStopped() {
if (!this.isOpen(this.selectedRace)) {
const nextOpen = this.getOpenRaceStartingFrom(this.selectedRace + 1);
if (nextOpen !== this.selectedRace) {
this.selectRace(nextOpen);
}
}
}
private getLegIndexForRace(poolName: string, race: number): number { private getLegIndexForRace(poolName: string, race: number): number {
const raceMap: { [key: string]: number[] } = { const raceMap: { [key: string]: number[] } = {
mjp1: [1, 2, 3, 4], mjp1: [1, 2, 3, 4],
@ -234,7 +340,7 @@ liveStatusOk = true;
value: this.enabledHorseNumbers, value: this.enabledHorseNumbers,
}); });
// console.log('[Multi-leg Pool] Updated enabled horse numbers:', this.enabledHorseNumbers); // console.log('[Multi-leg Pool] Updated enabled horse numbers:', this.enabledHorseNumbers);
} }
private getLegCountForLabel(): number { private getLegCountForLabel(): number {
@ -297,12 +403,12 @@ liveStatusOk = true;
// Use lowercase 'venue' as structuredRaceCard uses .venue // Use lowercase 'venue' as structuredRaceCard uses .venue
this.selectedVenue = this.raceCardData?.venue ?? this.raceCardData?.Venue ?? 'Select Venue'; this.selectedVenue = this.raceCardData?.venue ?? this.raceCardData?.Venue ?? 'Select Venue';
this.updateEnabledHorseNumbers(); this.updateEnabledHorseNumbers();
// console.log('[MODAL] Opening venue modal (structured):', this.selectedVenue); // console.log('[MODAL] Opening venue modal (structured):', this.selectedVenue);
this.showVenueModal = true; this.showVenueModal = true;
} }
openRaceModal() { openRaceModal() {
// console.log('[MODAL] Opening race modal'); // console.log('[MODAL] Opening race modal');
this.showRaceModal = true; this.showRaceModal = true;
const racesArr = this.raceCardData?.raceVenueRaces?.races ?? []; const racesArr = this.raceCardData?.raceVenueRaces?.races ?? [];
@ -315,7 +421,7 @@ liveStatusOk = true;
} }
closeModals() { closeModals() {
// console.log('[MODAL] Closing all modals'); // console.log('[MODAL] Closing all modals');
this.showVenueModal = false; this.showVenueModal = false;
this.showRaceModal = false; this.showRaceModal = false;
this.showWalletModal = false; this.showWalletModal = false;
@ -335,12 +441,26 @@ liveStatusOk = true;
value: this.selectedVenue, value: this.selectedVenue,
}); });
// console.log('[VENUE] Venue resolved to (structured):', this.selectedVenue, '| index:', index); // Update currentVenue if changed
const newVenue = venue.toUpperCase();
if (newVenue !== this.currentVenue) {
this.currentVenue = newVenue;
this.stopbetStatuses.clear();
if (this.currentVenue) {
this.fetchInitialStopbets();
this.setupSSE();
}
}
// console.log('[VENUE] Venue resolved to (structured):', this.selectedVenue, '| index:', index);
this.closeModals(); this.closeModals();
} }
selectRace(race: number) { selectRace(race: number) {
this.selectedRace = race; // Adjust race if the attempted one is stopped
const adjustedRace = this.isOpen(race) ? race : this.getOpenRaceStartingFrom(race);
this.selectedRace = adjustedRace;
this.currentPool = null; this.currentPool = null;
this.multiLegBaseRaceIdx = 0; this.multiLegBaseRaceIdx = 0;
this.currentLegRaceDisplay = ''; this.currentLegRaceDisplay = '';
@ -352,7 +472,7 @@ liveStatusOk = true;
// Use this.raceCardData (structured) rather than re-parsing localStorage // Use this.raceCardData (structured) rather than re-parsing localStorage
const raceList = this.raceCardData?.raceVenueRaces?.races ?? []; const raceList = this.raceCardData?.raceVenueRaces?.races ?? [];
const selectedRaceEntry = raceList[race - 1] ?? []; const selectedRaceEntry = raceList[this.selectedRace - 1] ?? [];
// Determine runnerCount defensively based on possible shapes // Determine runnerCount defensively based on possible shapes
let runnerCount = 12; let runnerCount = 12;
@ -367,7 +487,7 @@ liveStatusOk = true;
this.sharedStateService.setRunnerCount(runnerCount); this.sharedStateService.setRunnerCount(runnerCount);
this.updateEnabledHorseNumbers(); this.updateEnabledHorseNumbers();
// console.log('[RACE] Race selected (structured):', this.selectedRace, '| Runner count:', runnerCount); // console.log('[RACE] Race selected (structured):', this.selectedRace, '| Runner count:', runnerCount);
this.closeModals(); this.closeModals();
} }
@ -399,30 +519,30 @@ liveStatusOk = true;
value: this.enabledHorseNumbers, value: this.enabledHorseNumbers,
}); });
// console.log('[HORSE NUMBERS] Enabled horse numbers (structured):', this.enabledHorseNumbers); // console.log('[HORSE NUMBERS] Enabled horse numbers (structured):', this.enabledHorseNumbers);
} }
selectHorseNumber(number: number) { selectHorseNumber(number: number) {
// console.log('[HORSE] Selected horse number:', number); // console.log('[HORSE] Selected horse number:', number);
} }
openWalletModal() { openWalletModal() {
// console.log('[MODAL] Opening wallet modal'); // console.log('[MODAL] Opening wallet modal');
this.showWalletModal = true; this.showWalletModal = true;
} }
openResultModal() { openResultModal() {
// console.log('[MODAL] Opening result modal'); // console.log('[MODAL] Opening result modal');
this.showResultModal = true; this.showResultModal = true;
} }
openMessagesModal() { openMessagesModal() {
// console.log('[MODAL] Opening messages modal'); // console.log('[MODAL] Opening messages modal');
this.showMessagesModal = true; this.showMessagesModal = true;
} }
openLogModal() { openLogModal() {
// console.log('[MODAL] Opening log modal'); // console.log('[MODAL] Opening log modal');
this.showLogModal = true; this.showLogModal = true;
} }
@ -492,8 +612,8 @@ liveStatusOk = true;
displayBarcode = '********' + last4; displayBarcode = '********' + last4;
} }
// console.log(maskedBarcode); // console.log(maskedBarcode);
// console.log('Decoded:', atob(maskedBarcode)); // console.log('Decoded:', atob(maskedBarcode));
return { return {
pool, pool,
@ -509,12 +629,12 @@ liveStatusOk = true;
}; };
}); });
} else { } else {
// console.log('No tickets found in localStorage.'); // console.log('No tickets found in localStorage.');
this.formattedTicketLogs = []; this.formattedTicketLogs = [];
} }
this.showLogModal = true; this.showLogModal = true;
// console.log('Log modal opened. Final formattedTicketLogs:', this.formattedTicketLogs); // console.log('Log modal opened. Final formattedTicketLogs:', this.formattedTicketLogs);
} }
logout(): void { logout(): void {
@ -528,7 +648,7 @@ liveStatusOk = true;
type: 'logout', // This is the missing piece type: 'logout', // This is the missing piece
}; };
// console.log('[LOGOUT] Initiating logout with printData:', printData); // console.log('[LOGOUT] Initiating logout with printData:', printData);
fetch('http://localhost:9100/print', { fetch('http://localhost:9100/print', {
method: 'POST', method: 'POST',
@ -537,13 +657,13 @@ liveStatusOk = true;
}) })
.then((res) => { .then((res) => {
if (!res.ok) throw new Error('Logout print failed'); if (!res.ok) throw new Error('Logout print failed');
// console.log('[LOGOUT] Print successful'); // console.log('[LOGOUT] Print successful');
(window as any).electronAPI?.closeSecondScreen?.(); (window as any).electronAPI?.closeSecondScreen?.();
localStorage.clear(); localStorage.clear();
this.router.navigate(['/logout']); this.router.navigate(['/logout']);
}) })
.catch((err) => { .catch((err) => {
// console.error('[LOGOUT] Error printing:', err); // console.error('[LOGOUT] Error printing:', err);
(window as any).electronAPI?.closeSecondScreen?.(); (window as any).electronAPI?.closeSecondScreen?.();
localStorage.clear(); localStorage.clear();
this.router.navigate(['/logout']); this.router.navigate(['/logout']);
@ -554,6 +674,9 @@ liveStatusOk = true;
if (this.subscription) { if (this.subscription) {
this.subscription.unsubscribe(); this.subscription.unsubscribe();
} }
if (this.eventSource) {
this.eventSource.close();
}
} }
// Add trackByHorse for use in *ngFor // Add trackByHorse for use in *ngFor
@ -561,4 +684,3 @@ liveStatusOk = true;
return item; return item;
} }
} }