fix : stop bet (local storage)
This commit is contained in:
parent
bd4f1e21d0
commit
89009d28c4
@ -1,4 +1,3 @@
|
||||
// navbar.component.ts
|
||||
import { Component, OnInit, HostListener, OnDestroy, NgZone, Inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { BtcService } from '../../service/btc.service';
|
||||
@ -24,7 +23,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
userName: string = '';
|
||||
btid: string | null = null;
|
||||
|
||||
// component properties - add these at top-level in the component class
|
||||
// Component properties
|
||||
consecTimeouts = 0;
|
||||
maxConsecTimeouts = 2;
|
||||
liveStatusOk = true;
|
||||
@ -33,7 +32,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
showRaceModal = false;
|
||||
selectedVenue = 'Select Venue';
|
||||
selectedRace: number = 1;
|
||||
currentLegRaceDisplay: string = ''; // Display current leg's race or pool start
|
||||
currentLegRaceDisplay: string = '';
|
||||
|
||||
showWalletModal = false;
|
||||
showResultModal = false;
|
||||
@ -43,13 +42,12 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
raceData: any[] = [];
|
||||
objectKeys = Object.keys;
|
||||
|
||||
selectedRaceId: number = 0; // index into races array
|
||||
selectedRaceId: number = 0;
|
||||
enabledHorseNumbers: number[] = [];
|
||||
multiLegBaseRaceIdx: number = 0;
|
||||
currentPool: string | null = null;
|
||||
|
||||
private prevEnabledKey = ''; // For memoization
|
||||
|
||||
private prevEnabledKey = '';
|
||||
wallet = {
|
||||
withdraw: 0,
|
||||
deposit: 0,
|
||||
@ -71,11 +69,11 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
|
||||
messages: string[] = ['System ready.', 'Please select a venue.', 'Races updated.', 'Live status stable.'];
|
||||
|
||||
// Stopbet-related properties (merged approach)
|
||||
private stopbetStatuses: Map<number, string> = new Map(); // raceNum => 'Y'|'N'|'S'
|
||||
// Stopbet-related properties
|
||||
private stopbetStatuses: Map<number, string> = new Map();
|
||||
private currentVenue: string = '';
|
||||
private currentDate: string = '';
|
||||
private eventSource?: EventSource;
|
||||
private eventSource: EventSource | undefined;
|
||||
|
||||
formattedTicketLogs: {
|
||||
pool: string;
|
||||
@ -139,51 +137,37 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
// Set current venue and date
|
||||
this.currentVenue = (this.raceCardData?.venue ?? '').toUpperCase();
|
||||
this.currentVenue = (this.raceCardData?.venue ?? '').toUpperCase() || localStorage.getItem('selectedVenue') || '';
|
||||
this.currentDate = this.getTodayDate();
|
||||
console.log('[INIT] Current venue:', this.currentVenue, 'date:', this.currentDate);
|
||||
|
||||
// Setup stopbet if venue is available:
|
||||
// 1) initialize StopbetService (if it uses BroadcastChannel or another approach)
|
||||
if (this.currentVenue) {
|
||||
try {
|
||||
this.stopbetService.initialize(this.currentVenue, this.currentDate);
|
||||
} catch (e) {
|
||||
// initialization may not be necessary or supported by the service; ignore if fails
|
||||
console.warn('[STOPBET] stopbetService.initialize failed or not required:', e);
|
||||
console.warn('[STOPBET] stopbetService.initialize failed:', e);
|
||||
}
|
||||
|
||||
// 2) fetch initial stopbets (HTTP fallback) and setup SSE stream
|
||||
this.fetchInitialStopbets();
|
||||
this.setupSSE();
|
||||
} else {
|
||||
console.warn('[INIT] No venue set, skipping stopbetService.initialize');
|
||||
}
|
||||
|
||||
// 3) subscribe to StopbetService (if it provides an observable of statuses)
|
||||
try {
|
||||
const statuses$ = (this.stopbetService as any).getStopbetStatuses?.();
|
||||
const statuses$ = this.stopbetService.getStopbetStatuses();
|
||||
if (statuses$ && typeof statuses$.subscribe === 'function') {
|
||||
this.stopbetSubscription = statuses$.subscribe((statuses: any) => {
|
||||
// if the service gives you a map/object, merge into local stopbetStatuses
|
||||
if (statuses && typeof statuses === 'object') {
|
||||
try {
|
||||
// expected structure: { raceNum: status, ... } or Map-like
|
||||
Object.entries(statuses).forEach(([k, v]) => {
|
||||
const rn = Number(k);
|
||||
if (!Number.isNaN(rn)) this.stopbetStatuses.set(rn, String(v));
|
||||
});
|
||||
} catch {
|
||||
// ignore malformed
|
||||
}
|
||||
}
|
||||
// Trigger check/switch if current race is stopped
|
||||
this.stopbetSubscription = statuses$.subscribe((statuses: Map<number, string>) => {
|
||||
this.stopbetStatuses.clear();
|
||||
statuses.forEach((value, key) => {
|
||||
this.stopbetStatuses.set(key, value);
|
||||
});
|
||||
console.log('[NAVBAR] Updated stopbetStatuses:', Object.fromEntries(this.stopbetStatuses));
|
||||
this.checkAndSwitchIfStopped();
|
||||
this.updateEnabledHorseNumbers();
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[STOPBET] subscribe to service failed:', e);
|
||||
console.warn('[STOPBET] Subscribe to service failed:', e);
|
||||
}
|
||||
|
||||
// Periodic ABS/latest polling (unchanged)
|
||||
this.subscription = interval(5000)
|
||||
.pipe(
|
||||
switchMap(() => {
|
||||
@ -198,15 +182,21 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
.subscribe((res: any) => {
|
||||
if (res) {
|
||||
this.liveStatusOk = res.success === true;
|
||||
|
||||
if (res.raceCardData) {
|
||||
this.raceCardData = res.raceCardData;
|
||||
localStorage.setItem('raceCardData', JSON.stringify(res.raceCardData));
|
||||
const newVenue = (this.raceCardData?.venue ?? '').toUpperCase();
|
||||
if (newVenue && newVenue !== this.currentVenue) {
|
||||
console.log('[NAVBAR] Venue changed to', newVenue, 'reinitializing stopbetService');
|
||||
this.currentVenue = newVenue;
|
||||
this.stopbetService.initialize(this.currentVenue, this.currentDate);
|
||||
}
|
||||
}
|
||||
if (res.wallet) {
|
||||
this.wallet = res.wallet;
|
||||
}
|
||||
}
|
||||
this.updateEnabledHorseNumbers();
|
||||
});
|
||||
|
||||
// Subscribe to shared state updates
|
||||
@ -216,7 +206,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
if (this.currentPool) {
|
||||
const leg = this.getLegIndexForRace(this.currentPool, data.value);
|
||||
this.currentLegRaceDisplay = `Leg ${leg + 1} (Race ${data.value}) for ${this.currentPool}`;
|
||||
this.updateEnabledHorseNumbersForMultiLeg(this.multiLegBaseRaceIdx);
|
||||
this.updateEnabledHorseNumbers();
|
||||
} else {
|
||||
this.currentLegRaceDisplay = '';
|
||||
this.updateEnabledHorseNumbers();
|
||||
@ -226,8 +216,9 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
const { label, baseRaceIdx } = data.value;
|
||||
this.currentPool = label;
|
||||
this.multiLegBaseRaceIdx = baseRaceIdx;
|
||||
this.selectedRace = this.getValidRaceForLeg(label, 0);
|
||||
this.currentLegRaceDisplay = `Starting at Race ${baseRaceIdx} for ${label}`;
|
||||
this.updateEnabledHorseNumbersForMultiLeg(baseRaceIdx);
|
||||
this.updateEnabledHorseNumbers();
|
||||
}
|
||||
if (data.type === 'multiLegPoolEnd') {
|
||||
this.currentPool = null;
|
||||
@ -242,6 +233,15 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
this.selectedRace = data.value;
|
||||
this.updateEnabledHorseNumbers();
|
||||
}
|
||||
if (data.type === 'selectedVenue') {
|
||||
const newVenue = data.value.toUpperCase();
|
||||
if (newVenue !== this.currentVenue) {
|
||||
console.log('[NAVBAR] Selected venue changed to', newVenue);
|
||||
this.currentVenue = newVenue;
|
||||
localStorage.setItem('selectedVenue', newVenue);
|
||||
this.stopbetService.initialize(this.currentVenue, this.currentDate);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -253,70 +253,8 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
return `${year}/${month}/${day}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP-based initial fetch (fallback).
|
||||
* Keeps internal stopbetStatuses Map updated with server data if available.
|
||||
*/
|
||||
private fetchInitialStopbets() {
|
||||
if (!this.currentVenue || !this.currentDate) return;
|
||||
this.http.get(`http://localhost:8080/stopbet/raw?venue=${this.currentVenue}&date=${this.currentDate}`).subscribe({
|
||||
next: (res: any) => {
|
||||
if (res && 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]);
|
||||
}
|
||||
// After loading, ensure current selected race is valid
|
||||
this.checkAndSwitchIfStopped();
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('[STOPBET] Failed to fetch initial statuses:', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* SSE stream setup for real-time stopbet updates.
|
||||
*/
|
||||
private setupSSE() {
|
||||
try {
|
||||
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);
|
||||
// optionally attempt reconnect logic here
|
||||
};
|
||||
} catch (err) {
|
||||
console.warn('[STOPBET] SSE initialization failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
private isOpen(race: number): boolean {
|
||||
// Prefer explicit map; if unknown assume open
|
||||
const status = this.stopbetStatuses.get(race);
|
||||
return status === 'N' || status === undefined;
|
||||
return this.stopbetService.isOpen(race);
|
||||
}
|
||||
|
||||
private getMaxRaces(): number {
|
||||
@ -325,54 +263,136 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
|
||||
private getOpenRaceStartingFrom(start: number) {
|
||||
const max = this.getMaxRaces();
|
||||
for (let r = start; r <= max; r++) {
|
||||
if (this.isOpen(r)) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
for (let r = 1; r < start; r++) {
|
||||
if (this.isOpen(r)) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return start;
|
||||
return this.stopbetService.getOpenRaceStartingFrom(start, max);
|
||||
}
|
||||
|
||||
private checkAndSwitchIfStopped() {
|
||||
if (!this.isOpen(this.selectedRace)) {
|
||||
const nextOpen = this.getOpenRaceStartingFrom(this.selectedRace + 1);
|
||||
if (nextOpen !== this.selectedRace) {
|
||||
console.log('[NAVBAR] Switching from stopped race', this.selectedRace, 'to', nextOpen);
|
||||
this.selectRace(nextOpen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getLegIndexForRace(poolName: string, race: number): number {
|
||||
private getLegIndexForRace(poolName: string | null, race: number): number {
|
||||
if (!poolName) return 0;
|
||||
const raceMap: { [key: string]: number[] } = {
|
||||
mjp1: [1, 2, 3, 4],
|
||||
jkp1: [3, 4, 5, 6, 7],
|
||||
trb1: [2, 3, 4],
|
||||
trb2: [5, 6, 7],
|
||||
};
|
||||
return raceMap[poolName]?.indexOf(race) ?? 0;
|
||||
return raceMap[poolName.toLowerCase()]?.indexOf(race) ?? 0;
|
||||
}
|
||||
|
||||
private updateEnabledHorseNumbersForMultiLeg(baseRaceIdx: number) {
|
||||
// Defensive read of races array (structured or legacy)
|
||||
const racesArr = this.raceCardData?.raceVenueRaces?.races ?? [];
|
||||
const legCount = this.getLegCountForLabel();
|
||||
const key = `${this.currentPool}-${baseRaceIdx}-${legCount}`;
|
||||
private getRaceForLeg(poolName: string | null, leg: number): number {
|
||||
if (!poolName) return this.multiLegBaseRaceIdx + leg;
|
||||
const raceMap: { [key: string]: number[] } = {
|
||||
mjp1: [1, 2, 3, 4],
|
||||
jkp1: [3, 4, 5, 6, 7],
|
||||
trb1: [2, 3, 4],
|
||||
trb2: [5, 6, 7],
|
||||
};
|
||||
return raceMap[poolName.toLowerCase()]?.[leg] ?? this.multiLegBaseRaceIdx + leg;
|
||||
}
|
||||
|
||||
if (this.prevEnabledKey === key) return; // memoization
|
||||
private getValidRaceForLeg(poolName: string, leg: number): number {
|
||||
const races = this.getValidRacesForPool(poolName);
|
||||
return races[leg] || this.multiLegBaseRaceIdx + leg;
|
||||
}
|
||||
|
||||
private getValidRacesForPool(poolName: string): number[] {
|
||||
const raceMap: { [key: string]: number[] } = {
|
||||
mjp1: [1, 2, 3, 4],
|
||||
jkp1: [3, 4, 5, 6, 7],
|
||||
trb1: [2, 3, 4],
|
||||
trb2: [5, 6, 7],
|
||||
};
|
||||
const defaultRaces = raceMap[poolName.toLowerCase()] || [];
|
||||
const maxRaces = this.getMaxRaces();
|
||||
|
||||
let earliestStoppedRace: number | null = null;
|
||||
this.stopbetStatuses.forEach((status, raceNum) => {
|
||||
if (status !== 'N' && status !== undefined && (earliestStoppedRace === null || raceNum < earliestStoppedRace)) {
|
||||
earliestStoppedRace = raceNum;
|
||||
}
|
||||
});
|
||||
|
||||
const validRaces = defaultRaces.filter((race) =>
|
||||
earliestStoppedRace === null
|
||||
? this.isOpen(race) && race <= maxRaces
|
||||
: race > earliestStoppedRace && this.isOpen(race) && race <= maxRaces
|
||||
);
|
||||
console.log('[NAVBAR] Valid races for pool', poolName, ':', validRaces);
|
||||
return validRaces;
|
||||
}
|
||||
|
||||
private getLegCountForLabel(): number {
|
||||
if (!this.currentPool) return 1;
|
||||
switch (this.currentPool.toLowerCase()) {
|
||||
case 'mjp1':
|
||||
return 4;
|
||||
case 'jkp1':
|
||||
return 5;
|
||||
case 'trb1':
|
||||
case 'trb2':
|
||||
return 3;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
updateEnabledHorseNumbers() {
|
||||
const key = `${this.currentPool || 'single'}-${this.selectedRace}-${this.multiLegBaseRaceIdx}`;
|
||||
if (this.prevEnabledKey === key) return;
|
||||
this.prevEnabledKey = key;
|
||||
|
||||
let combinedHorseNumbers: number[] = [];
|
||||
if (!this.isOpen(this.selectedRace)) {
|
||||
this.enabledHorseNumbers = [];
|
||||
this.sharedStateService.updateSharedData({
|
||||
type: 'enabledHorseNumbers',
|
||||
value: this.enabledHorseNumbers,
|
||||
});
|
||||
console.log('[NAVBAR] Race', this.selectedRace, 'is stopped, enabledHorseNumbers:', this.enabledHorseNumbers);
|
||||
return;
|
||||
}
|
||||
|
||||
const raceIndices = Array.from({ length: legCount }, (_, i) => this.getRaceForLeg(this.currentPool || '', i) - 1);
|
||||
const racesArr = this.raceCardData?.raceVenueRaces?.races ?? [];
|
||||
let runners: any[] = [];
|
||||
|
||||
for (const raceIdx of raceIndices) {
|
||||
const rawRace = racesArr[raceIdx] ?? [];
|
||||
let runners: any[] = [];
|
||||
if (this.currentPool) {
|
||||
const legCount = this.getLegCountForLabel();
|
||||
const validRaces = this.getValidRacesForPool(this.currentPool);
|
||||
let combinedHorseNumbers: number[] = [];
|
||||
|
||||
for (let leg = 0; leg < legCount; leg++) {
|
||||
const raceNum = validRaces[leg];
|
||||
if (!raceNum || !this.isOpen(raceNum)) continue;
|
||||
|
||||
const raceIdx = raceNum - 1;
|
||||
const rawRace = racesArr[raceIdx] ?? [];
|
||||
|
||||
if (Array.isArray(rawRace)) {
|
||||
runners = rawRace;
|
||||
} else if (rawRace && Array.isArray(rawRace.runners)) {
|
||||
runners = rawRace.runners;
|
||||
} else {
|
||||
runners = [];
|
||||
}
|
||||
|
||||
const horses = runners
|
||||
.map((runner: any) => runner?.horseNumber ?? runner?.number ?? runner?.horse_no)
|
||||
.filter((n: any) => typeof n === 'number' && n >= 1 && n <= 30);
|
||||
|
||||
combinedHorseNumbers.push(...horses);
|
||||
}
|
||||
|
||||
this.enabledHorseNumbers = Array.from(new Set(combinedHorseNumbers));
|
||||
} else {
|
||||
const raceIndex = this.selectedRace - 1;
|
||||
const rawRace = racesArr[raceIndex] ?? [];
|
||||
|
||||
if (Array.isArray(rawRace)) {
|
||||
runners = rawRace;
|
||||
@ -382,43 +402,16 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
runners = [];
|
||||
}
|
||||
|
||||
const horses = runners
|
||||
.map((runner: any) => runner?.horseNumber ?? runner?.number ?? runner?.horse_no)
|
||||
.filter((n: number) => typeof n === 'number' && n >= 1 && n <= 30);
|
||||
|
||||
combinedHorseNumbers.push(...horses);
|
||||
this.enabledHorseNumbers = runners
|
||||
.map((r: any) => r?.horseNumber ?? r?.number ?? r?.horse_no)
|
||||
.filter((n: any) => typeof n === 'number' && n >= 1 && n <= 30);
|
||||
}
|
||||
|
||||
this.enabledHorseNumbers = Array.from(new Set(combinedHorseNumbers));
|
||||
|
||||
this.sharedStateService.updateSharedData({
|
||||
type: 'enabledHorseNumbers',
|
||||
value: this.enabledHorseNumbers,
|
||||
});
|
||||
}
|
||||
|
||||
private getLegCountForLabel(): number {
|
||||
switch (this.currentPool) {
|
||||
case 'mjp1':
|
||||
return 4;
|
||||
case 'jkp1':
|
||||
return 5;
|
||||
case 'trb1':
|
||||
case 'trb2':
|
||||
return 3;
|
||||
default:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
private getRaceForLeg(poolName: string, leg: number): number {
|
||||
const raceMap: { [key: string]: number[] } = {
|
||||
mjp1: [1, 2, 3, 4],
|
||||
jkp1: [3, 4, 5, 6, 7],
|
||||
trb1: [2, 3, 4],
|
||||
trb2: [5, 6, 7],
|
||||
};
|
||||
return raceMap[poolName]?.[leg] ?? this.multiLegBaseRaceIdx + leg;
|
||||
console.log('[NAVBAR] Updated enabledHorseNumbers:', this.enabledHorseNumbers);
|
||||
}
|
||||
|
||||
updateDateTime() {
|
||||
@ -496,9 +489,8 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
this.currentVenue = newVenue;
|
||||
this.stopbetStatuses.clear();
|
||||
if (this.currentVenue) {
|
||||
this.fetchInitialStopbets();
|
||||
this.setupSSE();
|
||||
try {
|
||||
console.log('[NAVBAR] Initializing stopbetService for venue:', newVenue);
|
||||
this.stopbetService.initialize(this.currentVenue, this.currentDate);
|
||||
} catch {}
|
||||
}
|
||||
@ -540,35 +532,6 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
this.closeModals();
|
||||
}
|
||||
|
||||
updateEnabledHorseNumbers() {
|
||||
const raceIndex = this.selectedRace - 1;
|
||||
const racesArr = this.raceCardData?.raceVenueRaces?.races ?? [];
|
||||
const rawRace = racesArr?.[raceIndex];
|
||||
|
||||
let runners: any[] = [];
|
||||
|
||||
if (Array.isArray(rawRace)) {
|
||||
runners = rawRace;
|
||||
} else if (rawRace && Array.isArray(rawRace.runners)) {
|
||||
runners = rawRace.runners;
|
||||
} else {
|
||||
runners = [];
|
||||
}
|
||||
|
||||
if (Array.isArray(runners)) {
|
||||
this.enabledHorseNumbers = runners
|
||||
.map((r: any) => r?.horseNumber ?? r?.number ?? r?.horse_no)
|
||||
.filter((n: any) => typeof n === 'number' && n >= 1 && n <= 30);
|
||||
} else {
|
||||
this.enabledHorseNumbers = [];
|
||||
}
|
||||
|
||||
this.sharedStateService.updateSharedData({
|
||||
type: 'enabledHorseNumbers',
|
||||
value: this.enabledHorseNumbers,
|
||||
});
|
||||
}
|
||||
|
||||
selectHorseNumber(number: number) {
|
||||
// Intentionally left no-op (UI hook)
|
||||
}
|
||||
@ -625,7 +588,6 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
const horsesPartMatch = rawLabel.match(/^\w+\s+(.+?)\s+\*\d+/);
|
||||
if (horsesPartMatch) {
|
||||
horses = horsesPartMatch[1].trim();
|
||||
|
||||
// Special pools split into races
|
||||
if (['MJP', 'JKP', 'TRE'].includes(pool)) {
|
||||
horsesArray = horses.split('/').map((r) => r.trim().split(',').map((h) => h.trim()));
|
||||
@ -665,6 +627,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
logout(): void {
|
||||
const name = localStorage.getItem('userName') || 'Unknown User';
|
||||
const employeeId = localStorage.getItem('employeeId') || '000000';
|
||||
const stopbetData = localStorage.getItem('stopbetStatuses'); // Save stopbetStatuses
|
||||
|
||||
const printData = {
|
||||
name,
|
||||
@ -682,11 +645,20 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
if (!res.ok) throw new Error('Logout print failed');
|
||||
(window as any).electronAPI?.closeSecondScreen?.();
|
||||
localStorage.clear();
|
||||
if (stopbetData) {
|
||||
localStorage.setItem('stopbetStatuses', stopbetData);
|
||||
console.log('[NAVBAR] Preserved stopbetStatuses in localStorage:', stopbetData);
|
||||
}
|
||||
this.router.navigate(['/logout']);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('[NAVBAR] Logout error:', err);
|
||||
(window as any).electronAPI?.closeSecondScreen?.();
|
||||
localStorage.clear();
|
||||
if (stopbetData) {
|
||||
localStorage.setItem('stopbetStatuses', stopbetData);
|
||||
console.log('[NAVBAR] Preserved stopbetStatuses in localStorage:', stopbetData);
|
||||
}
|
||||
this.router.navigate(['/logout']);
|
||||
});
|
||||
}
|
||||
@ -709,4 +681,4 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
trackByHorse(index: number, item: number): number {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
// stopbet.service.ts
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
@ -11,6 +10,7 @@ export class StopbetService {
|
||||
private currentVenue: string = '';
|
||||
private currentDate: string = '';
|
||||
private eventSource?: EventSource;
|
||||
private readonly STORAGE_KEY = 'stopbetStatuses';
|
||||
|
||||
constructor(private http: HttpClient, private zone: NgZone) {}
|
||||
|
||||
@ -21,30 +21,130 @@ export class StopbetService {
|
||||
|
||||
// Initialize stopbet for a venue and date
|
||||
initialize(venue: string, date: string) {
|
||||
this.currentVenue = venue.toUpperCase();
|
||||
this.currentDate = date;
|
||||
const newVenue = venue ? venue.toUpperCase() : '';
|
||||
const newDate = date || this.getTodayDate();
|
||||
|
||||
// Only clear if venue and date are valid and different
|
||||
if (newVenue && newDate && (this.currentVenue !== newVenue || this.currentDate !== newDate)) {
|
||||
console.log(
|
||||
`[STOPBET] Venue or date changed (from ${this.currentVenue}:${this.currentDate} to ${newVenue}:${newDate}), clearing stopbetStatuses`
|
||||
);
|
||||
this.stopbetStatuses.clear();
|
||||
localStorage.removeItem(this.STORAGE_KEY);
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
} else {
|
||||
console.log(
|
||||
`[STOPBET] No clear needed: newVenue=${newVenue}, newDate=${newDate}, currentVenue=${this.currentVenue}, currentDate=${this.currentDate}`
|
||||
);
|
||||
}
|
||||
|
||||
this.currentVenue = newVenue;
|
||||
this.currentDate = newDate;
|
||||
|
||||
// Load from localStorage first
|
||||
this.loadFromLocalStorage();
|
||||
|
||||
// Fetch from server and setup SSE
|
||||
this.fetchInitialStopbets();
|
||||
this.setupSSE();
|
||||
}
|
||||
|
||||
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] || {};
|
||||
this.stopbetStatuses.clear();
|
||||
for (const race in meeting) {
|
||||
const raceNum = parseInt(race, 10);
|
||||
this.stopbetStatuses.set(raceNum, meeting[race]);
|
||||
}
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
// Public method to set race status
|
||||
setStatus(raceNum: number, status: string) {
|
||||
const oldStatus = this.stopbetStatuses.get(raceNum);
|
||||
if (oldStatus === status) {
|
||||
console.log(`[STOPBET] No change for race ${raceNum}: status remains ${status}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the specified race status
|
||||
this.stopbetStatuses.set(raceNum, status);
|
||||
|
||||
// If stopping this race, auto-disable previous races
|
||||
if (status === 'Y' || status === 'S') {
|
||||
for (let prevRace = 1; prevRace < raceNum; prevRace++) {
|
||||
const prevStatus = this.stopbetStatuses.get(prevRace);
|
||||
if (prevStatus === 'N' || prevStatus === undefined) {
|
||||
console.log(`[STOPBET] Auto-stopping race ${prevRace} due to race ${raceNum} being stopped`);
|
||||
this.stopbetStatuses.set(prevRace, 'Y');
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('[STOPBET] Failed to fetch initial statuses:', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Emit updated statuses and save to localStorage
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
this.saveToLocalStorage();
|
||||
}
|
||||
|
||||
private loadFromLocalStorage() {
|
||||
const cached = localStorage.getItem(this.STORAGE_KEY);
|
||||
if (cached) {
|
||||
try {
|
||||
const parsed = JSON.parse(cached);
|
||||
if (parsed.venue === this.currentVenue && parsed.date === this.currentDate) {
|
||||
this.stopbetStatuses.clear();
|
||||
for (const [race, status] of Object.entries(parsed.statuses)) {
|
||||
const raceNum = parseInt(race, 10);
|
||||
if (!isNaN(raceNum) && ['Y', 'N', 'S'].includes(status as string)) {
|
||||
this.stopbetStatuses.set(raceNum, status as string);
|
||||
}
|
||||
}
|
||||
console.log('[STOPBET] Loaded statuses from localStorage:', this.stopbetStatuses);
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
} else {
|
||||
console.log(
|
||||
`[STOPBET] localStorage data invalid for venue=${this.currentVenue}, date=${this.currentDate}:`,
|
||||
parsed
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[STOPBET] Failed to parse localStorage:', err);
|
||||
localStorage.removeItem(this.STORAGE_KEY);
|
||||
}
|
||||
} else {
|
||||
console.log('[STOPBET] No stopbetStatuses found in localStorage');
|
||||
}
|
||||
}
|
||||
|
||||
private saveToLocalStorage() {
|
||||
try {
|
||||
const data = {
|
||||
venue: this.currentVenue,
|
||||
date: this.currentDate,
|
||||
statuses: Object.fromEntries(this.stopbetStatuses),
|
||||
};
|
||||
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(data));
|
||||
console.log('[STOPBET] Saved statuses to localStorage:', data);
|
||||
} catch (err) {
|
||||
console.error('[STOPBET] Failed to save to localStorage:', err);
|
||||
}
|
||||
}
|
||||
|
||||
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] || {};
|
||||
this.stopbetStatuses.clear();
|
||||
for (const race in meeting) {
|
||||
const raceNum = parseInt(race, 10);
|
||||
const status = meeting[race];
|
||||
this.setStatus(raceNum, status);
|
||||
}
|
||||
console.log('[STOPBET] Fetched initial statuses:', this.stopbetStatuses);
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
this.saveToLocalStorage();
|
||||
} else {
|
||||
console.warn('[STOPBET] Invalid response from fetchInitialStopbets:', res);
|
||||
}
|
||||
},
|
||||
error: (err) => {
|
||||
console.error('[STOPBET] Failed to fetch initial statuses:', err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private setupSSE() {
|
||||
@ -58,8 +158,8 @@ export class StopbetService {
|
||||
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);
|
||||
this.stopbetStatusesSubject.next(new Map(this.stopbetStatuses));
|
||||
this.setStatus(raceNum, data.status);
|
||||
console.log('[STOPBET] SSE updated race', raceNum, 'to', data.status);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[STOPBET] SSE parse error:', err);
|
||||
@ -86,6 +186,14 @@ export class StopbetService {
|
||||
return start;
|
||||
}
|
||||
|
||||
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}`;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user