feat : race number changes automatically according to the stopbet✅
This commit is contained in:
parent
ff263099c7
commit
3486b342d7
@ -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,11 +674,13 @@ liveStatusOk = true;
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Add trackByHorse for use in *ngFor
|
||||
trackByHorse(index: number, item: number): number {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user