diff --git a/btc-UI/src/app/components/navbar/navbar.component.ts b/btc-UI/src/app/components/navbar/navbar.component.ts
index 5efc621..af0fb59 100755
--- a/btc-UI/src/app/components/navbar/navbar.component.ts
+++ b/btc-UI/src/app/components/navbar/navbar.component.ts
@@ -44,7 +44,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
selectedRaceId: number = 0;
enabledHorseNumbers: number[] = [];
- multiLegBaseRaceIdx: number = 0;
+ multiLegBaseRaceIdx = 0;
currentPool: string | null = null;
private prevEnabledKey = '';
@@ -75,6 +75,9 @@ export class NavbarComponent implements OnInit, OnDestroy {
private currentDate: string = '';
private eventSource: EventSource | undefined;
+ stopMessage: string = '';
+ private stopMessageTimeout: number | null = null;
+
formattedTicketLogs: {
pool: string;
horses: string;
@@ -108,7 +111,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
}, 1000);
});
- // === Load structuredRaceCard from rpinfo if available (prefer structured) ===
+ // Load structuredRaceCard from rpinfo if available (prefer structured)
const rpCached = localStorage.getItem('rpinfo');
if (rpCached) {
try {
@@ -120,7 +123,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
this.raceCardData = {};
}
} else {
- // fallback to older raceCardData key if present (non-structured)
+ // Fallback to older raceCardData key if present (non-structured)
const fallback = localStorage.getItem('raceCardData');
if (fallback) {
try {
@@ -155,10 +158,51 @@ export class NavbarComponent implements OnInit, OnDestroy {
const statuses$ = this.stopbetService.getStopbetStatuses();
if (statuses$ && typeof statuses$.subscribe === 'function') {
this.stopbetSubscription = statuses$.subscribe((statuses: Map) => {
- this.stopbetStatuses.clear();
- statuses.forEach((value, key) => {
- this.stopbetStatuses.set(key, value);
+ const newStatuses = new Map(statuses);
+ const newStops: number[] = [];
+
+ // Detect newly stopped races
+ newStatuses.forEach((value, key) => {
+ const prev = this.stopbetStatuses.get(key);
+ if (prev === 'N' || prev === undefined) {
+ if (value === 'Y' || value === 'S') {
+ newStops.push(key);
+ }
+ }
});
+
+ this.stopbetStatuses = newStatuses;
+
+ // If there are new stops, display the message for 30 seconds
+ if (newStops.length > 0) {
+ newStops.sort((a, b) => a - b);
+ this.stopMessage = newStops.map(r => `Race ${r} Stopped`).join(' - ');
+ // Sync new stop message to electron main if available
+ if ((window as any).electronAPI) {
+ try {
+ (window as any).electronAPI.syncStopMessage(this.stopMessage);
+ } catch (e) {
+ console.warn('[NAVBAR] electronAPI.syncStopMessage failed:', e);
+ }
+ }
+
+ if (this.stopMessageTimeout) {
+ clearTimeout(this.stopMessageTimeout);
+ }
+ this.stopMessageTimeout = window.setTimeout(() => {
+ this.stopMessage = '';
+ this.stopMessageTimeout = null;
+ // Sync cleared message to electron main if available
+ if ((window as any).electronAPI) {
+ try {
+ (window as any).electronAPI.syncStopMessage(this.stopMessage);
+ } catch (e) {
+ console.warn('[NAVBAR] electronAPI.syncStopMessage (clear) failed:', e);
+ }
+ }
+ }, 50000);
+ }
+
console.log('[NAVBAR] Updated stopbetStatuses:', Object.fromEntries(this.stopbetStatuses));
this.checkAndSwitchIfStopped();
this.updateEnabledHorseNumbers();
@@ -432,7 +476,6 @@ export class NavbarComponent implements OnInit, OnDestroy {
}
openVenueModal() {
- // Prefer structuredRaceCard loaded at init (rpinfo). If empty, try legacy key.
if (!this.raceCardData || Object.keys(this.raceCardData).length === 0) {
const cachedData = localStorage.getItem('raceCardData');
if (cachedData) {
@@ -447,7 +490,6 @@ export class NavbarComponent implements OnInit, OnDestroy {
}
}
- // Use lowercase 'venue' as structuredRaceCard uses .venue
this.selectedVenue = this.raceCardData?.venue ?? this.raceCardData?.Venue ?? 'Select Venue';
this.updateEnabledHorseNumbers();
this.showVenueModal = true;
@@ -500,7 +542,6 @@ export class NavbarComponent implements OnInit, OnDestroy {
}
selectRace(race: number) {
- // Adjust race if the attempted one is stopped
const adjustedRace = this.isOpen(race) ? race : this.getOpenRaceStartingFrom(race);
this.selectedRace = adjustedRace;
@@ -513,11 +554,9 @@ export class NavbarComponent implements OnInit, OnDestroy {
value: this.selectedRace,
});
- // Use this.raceCardData (structured) rather than re-parsing localStorage
const raceList = this.raceCardData?.raceVenueRaces?.races ?? [];
const selectedRaceEntry = raceList[this.selectedRace - 1] ?? [];
- // Determine runnerCount defensively based on possible shapes
let runnerCount = 12;
if (Array.isArray(selectedRaceEntry)) {
runnerCount = selectedRaceEntry.length || 12;
@@ -573,22 +612,18 @@ export class NavbarComponent implements OnInit, OnDestroy {
displayBarcode = '';
if (rawLabel) {
- // 1️⃣ Extract pool (first word)
const parts = rawLabel.split(/\s+/);
pool = parts[0];
- // 2️⃣ Extract ticket count (*n) & price if exists
const countMatch = rawLabel.match(/\*(\d+)(?:\s+(Rs\s*\d+))?/);
if (countMatch) {
ticketCountLabel = `*${countMatch[1]}`;
price = countMatch[2] || '';
}
- // 3️⃣ Extract horses part (between pool name & ticket count)
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()));
} else {
@@ -627,7 +662,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 stopbetData = localStorage.getItem('stopbetStatuses');
const printData = {
name,
@@ -675,10 +710,12 @@ export class NavbarComponent implements OnInit, OnDestroy {
this.eventSource.close();
} catch {}
}
+ if (this.stopMessageTimeout) {
+ clearTimeout(this.stopMessageTimeout);
+ }
}
- // Add trackByHorse for use in *ngFor
trackByHorse(index: number, item: number): number {
return item;
}
-}
\ 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 9b57ce9..cd16ba7 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
@@ -98,14 +98,14 @@ div[style*="background-color: black"] .custom-cell {
/* === Footer Always at Bottom === */
.footer {
height: 10vh;
- background-color: #1c2c46a8;
- color: white;
+ background-color: #fafbfca2;
+ color: rgb(126, 0, 0);
display: flex;
align-items: center;
justify-content: center;
margin-top: 15%;
font-weight: bold;
- font-size: 1rem;
+ font-size: 1.5rem;
border-radius: 0.5rem;
}
@@ -160,3 +160,21 @@ div[style*="background-color: black"] .custom-cell {
margin-bottom: 1rem;
}
}
+
+/* Add these new styles for scrolling */
+.live-data-text.scrolling {
+ white-space: nowrap;
+ overflow: hidden;
+ box-sizing: border-box;
+ animation: scroll 20s linear infinite; /* Adjust duration for speed */
+ width: 55%; /* Ensure it takes full width */
+}
+
+@keyframes scroll {
+ 0% {
+ transform: translateX(100%);
+ }
+ 100% {
+ transform: translateX(-100%);
+ }
+}
\ No newline at end of file
diff --git a/btc-UI/src/app/components/shared-table/shared-table.component.html b/btc-UI/src/app/components/shared-table/shared-table.component.html
index 5b3f69a..1354164 100644
--- a/btc-UI/src/app/components/shared-table/shared-table.component.html
+++ b/btc-UI/src/app/components/shared-table/shared-table.component.html
@@ -44,9 +44,10 @@