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;
// component properties - add these at top-level in the component class
consecTimeouts = 0;
maxConsecTimeouts = 2;
liveStatusOk = true;
consecTimeouts = 0;
maxConsecTimeouts = 2;
liveStatusOk = true;
showVenueModal = false;
showRaceModal = false;
@ -77,6 +76,12 @@ liveStatusOk = true;
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() {
this.userName = localStorage.getItem('userName') || '';
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
this.subscription = interval(5000)
.pipe(
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(
catchError((err) => {
// console.error('[ANGULAR] ABS latest fetch failed ❌', err);
// console.error('[ANGULAR] ABS latest fetch failed ❌', err);
this.liveStatusOk = false;
return of(null);
})
@ -133,7 +148,7 @@ liveStatusOk = true;
)
.subscribe((res: any) => {
if (res) {
// console.log('[ANGULAR] ABS latest response ✅', res);
// console.log('[ANGULAR] ABS latest response ✅', res);
this.liveStatusOk = res.success === true;
// 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 {
const raceMap: { [key: string]: number[] } = {
mjp1: [1, 2, 3, 4],
@ -234,7 +340,7 @@ liveStatusOk = true;
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 {
@ -297,12 +403,12 @@ liveStatusOk = true;
// Use lowercase 'venue' as structuredRaceCard uses .venue
this.selectedVenue = this.raceCardData?.venue ?? this.raceCardData?.Venue ?? 'Select Venue';
this.updateEnabledHorseNumbers();
// console.log('[MODAL] Opening venue modal (structured):', this.selectedVenue);
// console.log('[MODAL] Opening venue modal (structured):', this.selectedVenue);
this.showVenueModal = true;
}
openRaceModal() {
// console.log('[MODAL] Opening race modal');
// console.log('[MODAL] Opening race modal');
this.showRaceModal = true;
const racesArr = this.raceCardData?.raceVenueRaces?.races ?? [];
@ -315,7 +421,7 @@ liveStatusOk = true;
}
closeModals() {
// console.log('[MODAL] Closing all modals');
// console.log('[MODAL] Closing all modals');
this.showVenueModal = false;
this.showRaceModal = false;
this.showWalletModal = false;
@ -335,12 +441,26 @@ liveStatusOk = true;
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();
}
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.multiLegBaseRaceIdx = 0;
this.currentLegRaceDisplay = '';
@ -352,7 +472,7 @@ liveStatusOk = true;
// Use this.raceCardData (structured) rather than re-parsing localStorage
const raceList = this.raceCardData?.raceVenueRaces?.races ?? [];
const selectedRaceEntry = raceList[race - 1] ?? [];
const selectedRaceEntry = raceList[this.selectedRace - 1] ?? [];
// Determine runnerCount defensively based on possible shapes
let runnerCount = 12;
@ -367,7 +487,7 @@ liveStatusOk = true;
this.sharedStateService.setRunnerCount(runnerCount);
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();
}
@ -399,30 +519,30 @@ liveStatusOk = true;
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) {
// console.log('[HORSE] Selected horse number:', number);
// console.log('[HORSE] Selected horse number:', number);
}
openWalletModal() {
// console.log('[MODAL] Opening wallet modal');
// console.log('[MODAL] Opening wallet modal');
this.showWalletModal = true;
}
openResultModal() {
// console.log('[MODAL] Opening result modal');
// console.log('[MODAL] Opening result modal');
this.showResultModal = true;
}
openMessagesModal() {
// console.log('[MODAL] Opening messages modal');
// console.log('[MODAL] Opening messages modal');
this.showMessagesModal = true;
}
openLogModal() {
// console.log('[MODAL] Opening log modal');
// console.log('[MODAL] Opening log modal');
this.showLogModal = true;
}
@ -492,8 +612,8 @@ liveStatusOk = true;
displayBarcode = '********' + last4;
}
// console.log(maskedBarcode);
// console.log('Decoded:', atob(maskedBarcode));
// console.log(maskedBarcode);
// console.log('Decoded:', atob(maskedBarcode));
return {
pool,
@ -509,12 +629,12 @@ liveStatusOk = true;
};
});
} else {
// console.log('No tickets found in localStorage.');
// console.log('No tickets found in localStorage.');
this.formattedTicketLogs = [];
}
this.showLogModal = true;
// console.log('Log modal opened. Final formattedTicketLogs:', this.formattedTicketLogs);
// console.log('Log modal opened. Final formattedTicketLogs:', this.formattedTicketLogs);
}
logout(): void {
@ -528,7 +648,7 @@ liveStatusOk = true;
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', {
method: 'POST',
@ -537,13 +657,13 @@ liveStatusOk = true;
})
.then((res) => {
if (!res.ok) throw new Error('Logout print failed');
// console.log('[LOGOUT] Print successful');
// console.log('[LOGOUT] Print successful');
(window as any).electronAPI?.closeSecondScreen?.();
localStorage.clear();
this.router.navigate(['/logout']);
})
.catch((err) => {
// console.error('[LOGOUT] Error printing:', err);
// console.error('[LOGOUT] Error printing:', err);
(window as any).electronAPI?.closeSecondScreen?.();
localStorage.clear();
this.router.navigate(['/logout']);
@ -554,6 +674,9 @@ liveStatusOk = true;
if (this.subscription) {
this.subscription.unsubscribe();
}
if (this.eventSource) {
this.eventSource.close();
}
}
// Add trackByHorse for use in *ngFor
@ -561,4 +684,3 @@ liveStatusOk = true;
return item;
}
}