btc_horse/btc-UI/src/app/components/navbar/navbar.component.ts

564 lines
16 KiB
TypeScript
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Component, OnInit, HostListener, OnDestroy, NgZone } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BtcService } from '../../service/btc.service';
import { Router } from '@angular/router';
import { catchError, interval, of, Subscription, switchMap } from 'rxjs';
import { SharedStateService } from '../../service/shared-state.service';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css'],
standalone: true,
imports: [CommonModule],
})
export class NavbarComponent implements OnInit, OnDestroy {
dateTime: string = '';
isMenuOpen: boolean = false;
screenWidth: number = window.innerWidth;
private subscription!: Subscription;
userName: string = '';
btid: string | null = null;
liveStatusOk: boolean = true;
showVenueModal = false;
showRaceModal = false;
selectedVenue = 'Select Venue';
selectedRace: number = 1;
currentLegRaceDisplay: string = ''; // Display current leg's race or pool start
showWalletModal = false;
showResultModal = false;
showMessagesModal = false;
showLogModal = false;
raceCardData: any = {};
raceData: any[] = [];
objectKeys = Object.keys;
selectedRaceId: number = 0;
enabledHorseNumbers: number[] = [];
multiLegBaseRaceIdx: number = 0;
currentPool: string | null = null;
private prevEnabledKey = ''; // For memoization
wallet = {
withdraw: 0,
deposit: 0,
payout: 0,
cancel: 0,
ticketing: 0,
balance: 0,
};
logs = [{
description: '',
venue: '',
ticketNumber: '',
poolName: '',
totalAmount: '',
}];
messages: string[] = [
'System ready.',
'Please select a venue.',
'Races updated.',
'Live status stable.',
];
constructor(
private btcService: BtcService,
private router: Router,
private sharedStateService: SharedStateService,
private zone: NgZone // <-- Add NgZone
) {}
ngOnInit() {
this.userName = localStorage.getItem('userName') || '';
this.btid = localStorage.getItem('btid');
// Use NgZone to run setInterval outside Angular's change detection
this.zone.runOutsideAngular(() => {
setInterval(() => {
this.zone.run(() => this.updateDateTime());
}, 1000);
});
this.subscription = interval(5000)
.pipe(
switchMap(() =>
this.btcService.pingLiveStatus().pipe(
catchError((error) => {
console.error('[LIVE STATUS] Ping failed:', error);
this.liveStatusOk = false;
return of(null);
})
))
).subscribe((response) => {
if (response !== null) {
console.log('[LIVE STATUS] OK');
this.liveStatusOk = true;
}
});
this.sharedStateService.sharedData$.subscribe(data => {
if (data.type === 'currentLegRace') {
this.selectedRace = data.value;
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);
} else {
this.currentLegRaceDisplay = '';
this.updateEnabledHorseNumbers();
}
}
if (data.type === 'multiLegPoolStart') {
const { label, baseRaceIdx } = data.value;
this.currentPool = label;
this.multiLegBaseRaceIdx = baseRaceIdx;
this.currentLegRaceDisplay = `Starting at Race ${baseRaceIdx} for ${label}`;
this.updateEnabledHorseNumbersForMultiLeg(baseRaceIdx);
console.log(`[Multi-leg Pool] Selected: ${label}, Base Race: ${baseRaceIdx}`);
}
if (data.type === 'multiLegPoolEnd') {
this.currentPool = null;
this.multiLegBaseRaceIdx = 0;
this.currentLegRaceDisplay = '';
this.updateEnabledHorseNumbers();
}
if (data.type === 'selectedRace') {
this.currentPool = null;
this.multiLegBaseRaceIdx = 0;
this.currentLegRaceDisplay = '';
this.selectedRace = data.value;
this.updateEnabledHorseNumbers();
}
});
}
private getLegIndexForRace(poolName: string, race: 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]?.indexOf(race) ?? 0;
}
private updateEnabledHorseNumbersForMultiLeg(baseRaceIdx: number) {
const raceCardData = this.raceCardData?.raceVenueRaces?.races || [];
const legCount = this.getLegCountForLabel();
const key = `${this.currentPool}-${baseRaceIdx}-${legCount}`;
if (this.prevEnabledKey === key) return; // 🧠 Memoization
this.prevEnabledKey = key;
let combinedHorseNumbers: number[] = [];
const raceIndices = Array.from({ length: legCount }, (_, i) =>
this.getRaceForLeg(this.currentPool || '', i) - 1
);
for (const raceIdx of raceIndices) {
const race = raceCardData[raceIdx] || [];
if (Array.isArray(race)) {
const horses = race
.map((runner: any) => runner?.horseNumber)
.filter((n: number) => typeof n === 'number' && n >= 1 && n <= 30);
combinedHorseNumbers.push(...horses);
}
}
this.enabledHorseNumbers = Array.from(new Set(combinedHorseNumbers));
this.sharedStateService.updateSharedData({
type: 'enabledHorseNumbers',
value: this.enabledHorseNumbers,
});
console.log('[Multi-leg Pool] Updated enabled horse numbers:', 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);
}
updateDateTime() {
const now = new Date();
this.dateTime = now.toLocaleString();
}
@HostListener('window:resize', ['$event'])
onResize(event: any) {
this.screenWidth = event.target.innerWidth;
if (this.screenWidth > 800) {
this.isMenuOpen = false;
}
}
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
}
openVenueModal() {
const cachedData = localStorage.getItem('raceCardData');
if (cachedData) {
this.raceCardData = JSON.parse(cachedData);
// console.log('📦 Loaded race card from localStorage:', this.raceCardData); // comment out for perf
this.selectedVenue = this.raceCardData?.Venue || 'Select Venue';
this.updateEnabledHorseNumbers();
} else {
this.raceCardData = { raceVenueRaces: { races: [] }, pools: {} };
// console.warn('⚠️ No race card data found in localStorage.'); // comment out for perf
}
console.log('[MODAL] Opening venue modal');
this.showVenueModal = true;
}
openRaceModal() {
console.log('[MODAL] Opening race modal');
this.showRaceModal = true;
const venueIndex = Object.keys(this.raceCardData?.raceVenueRaces?.races || [])
.findIndex((_, idx) => idx === this.selectedRaceId);
if (venueIndex !== -1) {
this.raceData = this.raceCardData.raceVenueRaces.races[venueIndex] || [];
}
}
closeModals() {
console.log('[MODAL] Closing all modals');
this.showVenueModal = false;
this.showRaceModal = false;
this.showWalletModal = false;
this.showResultModal = false;
this.showMessagesModal = false;
this.showLogModal = false;
}
selectVenue(index: number) {
const venue = this.raceCardData?.Venue || 'Unknown Venue';
this.selectedVenue = venue;
this.selectedRaceId = index;
this.sharedStateService.updateSharedData({
type: 'selectedVenue',
value: this.selectedVenue,
});
console.log('[VENUE] Venue resolved to:', this.selectedVenue);
this.closeModals();
}
selectRace(race: number) {
this.selectedRace = race;
this.currentPool = null;
this.multiLegBaseRaceIdx = 0;
this.currentLegRaceDisplay = '';
this.sharedStateService.updateSharedData({
type: 'selectedRace',
value: this.selectedRace,
});
const raceCard = JSON.parse(localStorage.getItem('raceCardData') || '{}');
const raceList = raceCard?.raceVenueRaces?.races || [];
const selectedRaceData = raceList[race - 1] || [];
const runnerCount = selectedRaceData.length || 12;
this.sharedStateService.setRunnerCount(runnerCount);
this.updateEnabledHorseNumbers();
console.log('[RACE] Race selected:', this.selectedRace, '| Runner count:', runnerCount);
this.closeModals();
}
updateEnabledHorseNumbers() {
const raceIndex = this.selectedRace - 1;
const race = this.raceCardData?.raceVenueRaces?.races?.[raceIndex];
if (Array.isArray(race)) {
this.enabledHorseNumbers = race
.map((runner: any) => runner?.horseNumber)
.filter((n: any) => typeof n === 'number' && n >= 1 && n <= 30);
} else {
this.enabledHorseNumbers = [];
}
this.sharedStateService.updateSharedData({
type: 'enabledHorseNumbers',
value: this.enabledHorseNumbers,
});
console.log('[HORSE NUMBERS] Enabled horse numbers:', this.enabledHorseNumbers);
}
selectHorseNumber(number: number) {
console.log('[HORSE] Selected horse number:', number);
}
openWalletModal() {
console.log('[MODAL] Opening wallet modal');
this.showWalletModal = true;
}
openResultModal() {
console.log('[MODAL] Opening result modal');
this.showResultModal = true;
}
openMessagesModal() {
console.log('[MODAL] Opening messages modal');
this.showMessagesModal = true;
}
openLogModal() {
console.log('[MODAL] Opening log modal');
this.showLogModal = true;
}
// formattedTicketLogs: {
// label: string;
// numbers: number[];
// count: number;
// amount: number;
// }[] = [];
formattedTicketLogs: {
pool: string;
horses: string;
ticketCountLabel: string;
price: string;
numbers: number[];
count: number;
amount: number;
}[] = [];
// openViewLog() {
// const storedTickets = localStorage.getItem('localTicketsViewlog');
// //console.log('Stored Tickets (raw JSON):', storedTickets);
// if (storedTickets) {
// const tickets = JSON.parse(storedTickets);
// // console.log('Parsed Tickets:', tickets);
// this.formattedTicketLogs = tickets.map((ticket: any, index: number) => {
// const rawLabel = ticket.winLabels || '';
// const numbers = ticket.numbers || [];
// const count = ticket.ticketCount || 0;
// const amount = ticket.totalAmount || 0;
// let pool = '', horses = '', ticketCountLabel = '', price = '';
// // 🧠 Extract parts from rawLabel using RegExp
// const labelMatch = rawLabel.match(/^(\w+)\s+([\d,]+)\s+(\*\d+)\s+(Rs\s*\d+)/);
// if (labelMatch) {
// pool = labelMatch[1];
// horses = labelMatch[2];
// ticketCountLabel = labelMatch[3];
// price = labelMatch[4];
// }
// console.log(`--- Ticket ${index + 1} ---`);
// console.log('Pool:', pool);
// console.log('Horses:', horses);
// console.log('Ticket Label:', ticketCountLabel);
// console.log('Price:', price);
// console.log('Numbers:', numbers);
// console.log('Count:', count);
// console.log('Amount:', amount);
// return {
// pool,
// horses,
// ticketCountLabel,
// price,
// numbers,
// count,
// amount
// };
// });
// } else {
// console.log('No tickets found in localStorage.');
// this.formattedTicketLogs = [];
// }
// this.showLogModal = true;
// console.log('Log modal opened. Final formattedTicketLogs:', this.formattedTicketLogs);
// }
openViewLog() {
const storedTickets = localStorage.getItem('localTicketsViewlog');
if (storedTickets) {
const tickets = JSON.parse(storedTickets);
this.formattedTicketLogs = tickets.map((ticket: any, index: number) => {
const rawLabel = ticket.winLabels?.trim() || '';
const numbers = ticket.numbers || [];
const count = ticket.ticketCount || 0;
const amount = ticket.totalAmount || 0;
let pool = '', horses = '', horsesArray: string[][] = [], ticketCountLabel = '', price = '';
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 {
horsesArray = [horses.split(',').map(h => h.trim())];
}
}
}
// console.log(`--- Ticket ${index + 1} ---`);
// console.log('Pool:', pool);
// console.log('Horses (raw):', horses);
// console.log('Horses (array):', horsesArray);
// console.log('Ticket Label:', ticketCountLabel);
// console.log('Price:', price);
// console.log('Numbers:', numbers);
// console.log('Count:', count);
// console.log('Amount:', amount);
return {
pool,
horses,
horsesArray,
ticketCountLabel,
price,
numbers,
count,
amount
};
});
} else {
console.log('No tickets found in localStorage.');
this.formattedTicketLogs = [];
}
this.showLogModal = true;
console.log('Log modal opened. Final formattedTicketLogs:', this.formattedTicketLogs);
}
logout(): void {
const name = localStorage.getItem('userName') || 'Unknown User';
const employeeId = localStorage.getItem('employeeId') || '000000';
const printData = {
name,
employeeId,
action: 'logout',
type: 'logout' // 👈 This is the missing piece
};
console.log('[LOGOUT] Initiating logout with printData:', printData);
fetch('http://localhost:9100/print', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(printData),
})
.then((res) => {
if (!res.ok) throw new Error('Logout print failed');
console.log('[LOGOUT] Print successful');
(window as any).electronAPI?.closeSecondScreen?.();
localStorage.clear();
this.router.navigate(['/logout']);
})
.catch((err) => {
console.error('[LOGOUT] Error printing:', err);
(window as any).electronAPI?.closeSecondScreen?.();
localStorage.clear();
this.router.navigate(['/logout']);
});
}
// logout(): void {
// const name = localStorage.getItem('userName') || 'Unknown User';
// const employeeId = localStorage.getItem('employeeId') || '000000';
// const printData = { name, employeeId, action: 'logout' };
// console.log('[LOGOUT] Initiating logout with printData:', printData);
// fetch('http://localhost:9100/print', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(printData),
// })
// .then((res) => {
// if (!res.ok) throw new Error('Logout print failed');
// console.log('[LOGOUT] Print successful');
// (window as any).electronAPI?.closeSecondScreen?.();
// localStorage.clear();
// this.router.navigate(['/logout']);
// })
// .catch((err) => {
// console.error('[LOGOUT] Error printing:', err);
// (window as any).electronAPI?.closeSecondScreen?.();
// localStorage.clear();
// this.router.navigate(['/logout']);
// });
// }
ngOnDestroy(): void {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
// Add trackByHorse for use in *ngFor
trackByHorse(index: number, item: number): number {
return item;
}
}