fix : race card fetching from the server and working

This commit is contained in:
karthik 2025-09-08 15:10:12 +05:30
parent 5974bf5535
commit b680150a88
8 changed files with 797 additions and 1019 deletions

View File

@ -19,20 +19,19 @@ export class NavbarComponent implements OnInit, OnDestroy {
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
currentLegRaceDisplay: string = '';
showWalletModal = false;
showResultModal = false;
showMessagesModal = false;
showLogModal = false;
raceCardData: any = {};
raceCardData: any = null;
raceData: any[] = [];
objectKeys = Object.keys;
@ -41,37 +40,19 @@ export class NavbarComponent implements OnInit, OnDestroy {
multiLegBaseRaceIdx: number = 0;
currentPool: string | null = null;
private prevEnabledKey = ''; // For memoization
private prevEnabledKey = '';
wallet = {
withdraw: 0,
deposit: 0,
payout: 0,
cancel: 0,
ticketing: 0,
balance: 0,
};
wallet = { withdraw: 0, deposit: 0, payout: 0, cancel: 0, ticketing: 0, balance: 0 };
logs = [{
description: '',
venue: '',
ticketNumber: '',
poolName: '',
totalAmount: '',
}];
logs = [{ description: '', venue: '', ticketNumber: '', poolName: '', totalAmount: '' }];
messages: string[] = [
'System ready.',
'Please select a venue.',
'Races updated.',
'Live status stable.',
];
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
private zone: NgZone
) {}
ngOnInit() {
@ -79,30 +60,19 @@ export class NavbarComponent implements OnInit, OnDestroy {
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);
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);
})
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;
}
if (response !== null) { this.liveStatusOk = true; console.log('[LIVE STATUS] OK'); }
});
this.sharedStateService.sharedData$.subscribe(data => {
if (data.type === 'currentLegRace') {
this.selectedRace = data.value;
@ -117,11 +87,11 @@ export class NavbarComponent implements OnInit, OnDestroy {
}
if (data.type === 'multiLegPoolStart') {
const { label, baseRaceIdx } = data.value;
this.currentPool = label;
this.currentPool = this.normalizePoolName(label);
this.multiLegBaseRaceIdx = baseRaceIdx;
this.currentLegRaceDisplay = `Starting at Race ${baseRaceIdx} for ${label}`;
this.currentLegRaceDisplay = `Starting at Race ${baseRaceIdx} for ${this.currentPool}`;
this.updateEnabledHorseNumbersForMultiLeg(baseRaceIdx);
console.log(`[Multi-leg Pool] Selected: ${label}, Base Race: ${baseRaceIdx}`);
console.log(`[Multi-leg Pool] Selected: ${this.currentPool}, Base Race: ${baseRaceIdx}`);
}
if (data.type === 'multiLegPoolEnd') {
this.currentPool = null;
@ -137,115 +107,115 @@ export class NavbarComponent implements OnInit, OnDestroy {
this.updateEnabledHorseNumbers();
}
});
this.loadRaceCardData();
}
private safeGetJSON(key: string): any | null {
try {
const raw = localStorage.getItem(key);
if (!raw) return null;
return JSON.parse(raw);
} catch (err) {
console.warn(`[safeGetJSON] failed parse ${key}`, err);
return null;
}
}
private loadRaceCardData() {
const cached = this.safeGetJSON('rpinfo');
if (cached && cached.structuredRaceCard) {
this.raceCardData = cached.structuredRaceCard;
this.selectedVenue = this.raceCardData?.venue || 'Select Venue';
this.updateEnabledHorseNumbers();
return;
}
const rc = this.safeGetJSON('raceCardData');
if (rc) {
this.raceCardData = rc;
this.selectedVenue = this.raceCardData?.venue || 'Select Venue';
this.updateEnabledHorseNumbers();
return;
}
this.raceCardData = { error: 'Race card not available locally' };
this.selectedVenue = 'Select Venue';
}
private normalizePoolName(name: string | null | undefined): string | null {
if (!name) return null;
const n = String(name).toLowerCase();
if (n.startsWith('trb') || n.startsWith('tbp')) return n.includes('2') ? 'trb2' : 'trb1';
if (n.startsWith('mjp')) return n.includes('2') ? 'mjp2' : 'mjp1';
if (n.startsWith('jkp') || n.startsWith('jpp') || n.startsWith('jk')) return n.includes('2') ? 'jkp2' : 'jkp1';
return n;
}
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;
if (!this.raceCardData) return 0;
const normalized = this.normalizePoolName(poolName) || poolName;
const poolMap: { [key: string]: number[] } = this.raceCardData?.pools || {};
const arr = poolMap[normalized] || poolMap[poolName] || [];
return arr.indexOf(race) >= 0 ? arr.indexOf(race) : 0;
}
private updateEnabledHorseNumbersForMultiLeg(baseRaceIdx: number) {
const raceCardData = this.raceCardData?.raceVenueRaces?.races || [];
const racesArr = this.raceCardData?.raceVenueRaces?.races || [];
const legCount = this.getLegCountForLabel();
const key = `${this.currentPool}-${baseRaceIdx}-${legCount}`;
if (this.prevEnabledKey === key) return; // 🧠 Memoization
const poolKey = this.currentPool || '';
const key = `${poolKey}-${baseRaceIdx}-${legCount}`;
if (this.prevEnabledKey === key) return;
this.prevEnabledKey = key;
let combinedHorseNumbers: number[] = [];
const raceIndices = Array.from({ length: legCount }, (_, i) =>
this.getRaceForLeg(this.currentPool || '', i) - 1
);
const raceIndices = Array.from({ length: legCount }, (_, i) => this.getRaceForLeg(poolKey, 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);
const race = racesArr[raceIdx] || {};
if (Array.isArray(race.horses)) {
const horses = race.horses.filter((n: any) => 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,
});
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;
const p = (this.currentPool || '').toLowerCase();
switch (p) {
case 'mjp1': case 'mjp2': return 4;
case 'jkp1': case 'jkp2': 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);
const normalized = this.normalizePoolName(poolName) || poolName;
const poolMap: { [key: string]: number[] } = this.raceCardData?.pools || {};
const arr = poolMap[normalized] || poolMap[poolName] || [];
if (arr.length > leg) return arr[leg];
return (this.multiLegBaseRaceIdx || 1) + leg;
}
updateDateTime() {
const now = new Date();
this.dateTime = now.toLocaleString();
}
updateDateTime() { this.dateTime = new Date().toLocaleString(); }
@HostListener('window:resize', ['$event'])
onResize(event: any) {
this.screenWidth = event.target.innerWidth;
if (this.screenWidth > 800) {
this.isMenuOpen = false;
}
if (this.screenWidth > 800) this.isMenuOpen = false;
}
toggleMenu() {
this.isMenuOpen = !this.isMenuOpen;
}
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;
}
openVenueModal() { this.loadRaceCardData(); 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] || [];
}
const race = this.raceCardData?.raceVenueRaces?.races?.[this.selectedRaceId];
this.raceData = race ? (race.horses || []) : [];
}
closeModals() {
@ -259,16 +229,9 @@ export class NavbarComponent implements OnInit, OnDestroy {
}
selectVenue(index: number) {
const venue = this.raceCardData?.Venue || 'Unknown Venue';
this.selectedVenue = venue;
this.selectedVenue = this.raceCardData?.venue || 'Unknown Venue';
this.selectedRaceId = index;
this.sharedStateService.updateSharedData({
type: 'selectedVenue',
value: this.selectedVenue,
});
console.log('[VENUE] Venue resolved to:', this.selectedVenue);
this.sharedStateService.updateSharedData({ type: 'selectedVenue', value: this.selectedVenue });
this.closeModals();
}
@ -277,124 +240,57 @@ export class NavbarComponent implements OnInit, OnDestroy {
this.currentPool = null;
this.multiLegBaseRaceIdx = 0;
this.currentLegRaceDisplay = '';
this.sharedStateService.updateSharedData({ type: 'selectedRace', value: this.selectedRace });
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;
const raceData = this.raceCardData?.raceVenueRaces?.races?.[race - 1] || {};
const runnerCount = Array.isArray(raceData.horses) ? raceData.horses.length : (raceData.runners?.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];
const race = this.raceCardData?.raceVenueRaces?.races?.[raceIndex] || {};
let horses: any[] = [];
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,
});
if (Array.isArray(race?.horses)) horses = race.horses;
else if (Array.isArray(race?.runners)) horses = race.runners.map((r: any) => r.number ?? r);
else if (Array.isArray(race)) horses = race;
this.enabledHorseNumbers = horses.map((n: any) => Number(n)).filter((n: number) => !Number.isNaN(n) && n >= 1 && n <= 30);
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);
}
selectHorseNumber(number: number) { console.log('[HORSE] Selected horse number:', number); }
openWalletModal() {
console.log('[MODAL] Opening wallet modal');
this.showWalletModal = true;
}
openWalletModal() { this.showWalletModal = true; }
openResultModal() { this.showResultModal = true; }
openMessagesModal() { this.showMessagesModal = true; }
openLogModal() { this.showLogModal = 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;
horsesArray: string[][];
ticketCountLabel: string;
price: string;
numbers: number[];
count: number;
amount: number;
maskedBarcode: string;
displayBarcode: string;
}[] = [];
formattedTicketLogs: any[] = [];
openViewLog() {
const storedTickets = localStorage.getItem('localTicketsViewlog');
if (storedTickets) {
if (!storedTickets) { this.formattedTicketLogs = []; this.showLogModal = true; return; }
const tickets = JSON.parse(storedTickets);
this.formattedTicketLogs = tickets.map((ticket: any, index: number) => {
this.formattedTicketLogs = tickets.map((ticket: any) => {
const rawLabel = ticket.winLabels?.trim() || '';
const numbers = ticket.numbers || [];
const count = ticket.ticketCount || 0;
const amount = ticket.totalAmount || 0;
const barcodeId = ticket.barcodeId || '';
let pool = '', horses = '', horsesArray: string[][] = [], ticketCountLabel = '', price = '', maskedBarcode = '', 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)
if (countMatch) { ticketCountLabel = `*${countMatch[1]}`; price = countMatch[2] || ''; }
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())
);
if (['MJP', 'JKP', 'TRE', 'trb1', 'trb2', 'mjp1', 'jkp1'].includes(pool)) {
horsesArray = horses.split('/').map((r: string) => r.trim().split(',').map(h => h.trim()));
} else {
horsesArray = [horses.split(',').map(h => h.trim())];
}
@ -403,62 +299,25 @@ openViewLog() {
if (barcodeId) {
const last4 = barcodeId.slice(-4);
// Encrypt everything except last4
const encryptedPart = btoa(barcodeId.slice(0, -4));
// Store masked barcode (if you still need encrypted form)
maskedBarcode = encryptedPart;
// For GUI → show ******** + last4
maskedBarcode = btoa(barcodeId.slice(0, -4));
displayBarcode = '********' + last4;
}
console.log(maskedBarcode);
console.log('Decoded:', atob(maskedBarcode));
return {
pool,
horses,
horsesArray,
ticketCountLabel,
price,
numbers,
count,
amount,
maskedBarcode,
displayBarcode
};
return { pool, horses, horsesArray, ticketCountLabel, price, numbers: ticket.numbers || [], count: ticket.ticketCount || 0, amount: ticket.totalAmount || 0, maskedBarcode, displayBarcode };
});
} 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
};
const printData = { name, employeeId, action: 'logout', type: '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),
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(printData),
})
.then((res) => {
if (!res.ok) throw new Error('Logout print failed');
@ -475,46 +334,9 @@ logout(): void {
});
}
// 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();
}
if (this.subscription) this.subscription.unsubscribe();
}
// Add trackByHorse for use in *ngFor
trackByHorse(index: number, item: number): number {
return item;
}
trackByHorse(index: number, item: number): number { return item; }
}

View File

@ -221,7 +221,7 @@
<!-- View RC Modal -->
<div class="viewrc-modal-overlay" *ngIf="showViewRc">
<!-- <div class="viewrc-modal-overlay" *ngIf="showViewRc">
<div class="viewrc-modal-box">
<h3 class="viewrc-modal-title">VIEW RC</h3>
<div class="viewrc-modal-body">
@ -271,6 +271,61 @@
</ng-template>
</div>
<div class="viewrc-modal-footer">
<button class="viewrc-cancel-btn" (click)="closeViewRcPopup()">CANCEL</button>
</div>
</div>
</div> -->
<div class="viewrc-modal-overlay" *ngIf="showViewRc">
<div class="viewrc-modal-box">
<h3 class="viewrc-modal-title">VIEW RC</h3>
<div class="viewrc-modal-body">
<ng-container *ngIf="raceCardData && !raceCardData.error; else errorTpl">
<p class="top"><strong>📍 Venue:</strong> {{ raceCardData.venue }}</p>
<p class="top"><strong>📅 Date:</strong> {{ raceCardData.date }}</p>
<div class="rc-table-container">
<h4>🏇 Race Lists</h4>
<table class="rc-table">
<thead>
<tr>
<th class="new">Races</th>
<th class="new">Race Numbers</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let race of raceCardData.raceVenueRaces.races">
<td class="table_col">Race {{ race.raceNo }}</td>
<td class="table_col1">{{ race.horses.join(', ') }}</td>
</tr>
</tbody>
</table>
</div>
<div class="rc-table-container" *ngIf="raceCardData.pools?.comboRaces">
<h4>🎯Races</h4>
<table class="rc-table">
<thead>
<tr>
<th class="new">Pool</th>
<th class="new">Races</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let pool of objectKeys(raceCardData.pools.comboRaces)">
<td class="table_col">{{ pool }}</td>
<td class="table_col1">{{ raceCardData.pools.comboRaces[pool].join(', ') }}</td>
</tr>
</tbody>
</table>
</div>
</ng-container>
<ng-template #errorTpl>
<p class="error-text">❌ {{ raceCardData?.error }}</p>
</ng-template>
</div>
<div class="viewrc-modal-footer">
<button class="viewrc-cancel-btn" (click)="closeViewRcPopup()">CANCEL</button>
</div>

View File

@ -441,17 +441,52 @@ ${receiptText}
raceCardData: any = null; // ✅ Hold fetched data
constructor(
private btcService: BtcService
) {}
// openViewRcPopup() {
// const cachedData = localStorage.getItem('rpinfo');
// if (cachedData) {
// this.raceCardData = JSON.parse(cachedData);
// console.log('📦 Loaded race card from localStorage:', this.raceCardData);
// } else {
// this.raceCardData = { error: 'Race card not available locally' };
// console.warn('⚠️ No race card data found in localStorage.');
// }
// this.showViewRc = true;
// }
// openViewRcPopup() {
// const cachedData = localStorage.getItem('rpinfo');
// if (cachedData) {
// try {
// this.raceCardData = JSON.parse(cachedData); // now it's an array
// console.log('📦 Loaded race card from localStorage:', this.raceCardData);
// } catch (e) {
// console.error('Error parsing rpinfo:', e);
// this.raceCardData = { error: 'Invalid race card data' };
// }
// } else {
// this.raceCardData = { error: 'Race card not available locally' };
// console.warn('⚠️ No race card data found in localStorage.');
// }
// this.showViewRc = true;
// }
openViewRcPopup() {
const cachedData = localStorage.getItem('raceCardData');
const cachedData = localStorage.getItem('rpinfo');
if (cachedData) {
this.raceCardData = JSON.parse(cachedData);
console.log('📦 Loaded race card from localStorage:', this.raceCardData);
try {
const parsed = JSON.parse(cachedData);
this.raceCardData = parsed.structuredRaceCard; // ✅ only keep structured
console.log('📦 Loaded structured race card:', this.raceCardData);
} catch (e) {
console.error('Error parsing rpinfo:', e);
this.raceCardData = { error: 'Invalid race card data' };
}
} else {
this.raceCardData = { error: 'Race card not available locally' };
console.warn('⚠️ No race card data found in localStorage.');
@ -460,6 +495,8 @@ ${receiptText}
this.showViewRc = true;
}
objectKeys = Object.keys;
closeViewRcPopup() {

View File

@ -49,6 +49,8 @@ export class HomeComponent implements OnInit, OnDestroy {
console.log('🏠 HomeComponent loaded');
this.btcService.getAllRaceEventsToday().subscribe({
next: (response: HttpResponse<HorseRaceModel[]>) => {
const horseRaceData = response.body;
@ -64,13 +66,13 @@ export class HomeComponent implements OnInit, OnDestroy {
},
});
const raceCardCached = localStorage.getItem('raceCardData');
const raceCardCached = localStorage.getItem('rpinfo');
if (!raceCardCached) {
this.btcService.getRaceCard().subscribe({
next: (res) => {
const raceCardData = res.body;
console.log('📦 Race card preloaded:', raceCardData);
localStorage.setItem('raceCardData', JSON.stringify(raceCardData));
localStorage.setItem('rpinfo', JSON.stringify(raceCardData));
this.updateRunnerCount(0);
},
error: (err) => {
@ -118,7 +120,7 @@ export class HomeComponent implements OnInit, OnDestroy {
}
private updateRunnerCount(raceIdx: number) {
const raceCardData = JSON.parse(localStorage.getItem('raceCardData') || '{}');
const raceCardData = JSON.parse(localStorage.getItem('rpinfo') || '{}');
const race = raceCardData?.raceVenueRaces?.races?.[raceIdx] || [];
const runnerCount = Array.isArray(race) ? race.length : 12;
if (!raceCardData?.raceVenueRaces?.races?.[raceIdx]) {

View File

@ -213,6 +213,7 @@ export class LoginComponent implements OnInit, OnDestroy {
this.passwordStatus = true;
}
//--------------------------------------NEW LOGIN LOGIC ----------------------------------------//
//--------------------------------------NEW LOGIN LOGIC ----------------------------------------//
async onSubmit(): Promise<void> {
if (this.loginForm.invalid) {
this.loginForm.markAllAsTouched();
@ -239,103 +240,37 @@ async onSubmit(): Promise<void> {
const btid = this.btcService.btid;
console.log("📦 BTID from file (via service):", btid);
// ✅ Prepare print data
const printData = {
name: userName,
employeeId: employeeId,
// btid: btid || "unknown",
action: 'login',
type: 'login'
};
console.log(printData.name);
console.log(printData.employeeId);
//console.log(printData.btid);
console.log(btid);
// ✅ Store in localStorage
localStorage.setItem('userName', userName);
localStorage.setItem('employeeId', employeeId);
localStorage.setItem('password', password);
localStorage.setItem('btid', btid || "unknown");
// ✅ Print first — login only if printing succeeds
// fetch('http://localhost:9100/print', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(printData),
// })
// .then((res) => {
// if (!res.ok) throw new Error('Print failed');
// console.log('🖨️ Print successful');
// ✅ Fetch race card after login success
this.btcService
.fetchRaceCard("021804111066", password, btid || "0483") // pass correct payload here
.subscribe({
next: (rpinfo) => {
console.log("📦 Race Card:", rpinfo);
// ✅ Only here we allow login
// Save in localStorage for later use
localStorage.setItem('rpinfo', JSON.stringify(rpinfo));
// ✅ Navigate once race card stored
(window as any).electronAPI?.openSecondScreen?.();
this.router.navigate(['/home']);
// })
// .catch((err) => {
// console.error('‼️ Print failed', err);
// this.loginError = 'Login failed: printing service unavailable.';
// this.passwordStatus = false; // reset status
// });
},
error: (err) => {
console.error("‼️ Failed to fetch race card", err);
this.loginError = "Could not load race card.";
}
});
},
error: () => {
this.loginError = 'Invalid login credentials';
}
});
}
//-------------------------NEW LOGIN ENDS HERE -------------------------------------------------//
// async onSubmit(): Promise<void> {
// if (this.loginForm.invalid) {
// this.loginForm.markAllAsTouched();
// return;
// }
// const { email, password } = this.loginForm.value;
// // ✅ Await service (since its async)
// (await this.btcService.userLogin(email, password)).subscribe({
// next: (response) => {
// const employee = response.body;
// console.log('🧠 Raw employee response:', employee);
// const userName = employee?.userName || 'Unknown User';
// const employeeId = employee?.userIdNumber || email;
// console.log('✅ Parsed name:', userName);
// console.log('✅ Parsed ID:', employeeId);
// this.passwordStatus = true;
// // ✅ Get the BTID the service fetched
// const btid = this.btcService.btid;
// console.log("📦 BTID from file (via service):", btid);
// // Prepare print data
// const printData = {
// name: userName,
// employeeId: employeeId,
// action: 'login',
// type: 'login'
// };
// // ✅ Store in localStorage
// localStorage.setItem('userName', userName);
// localStorage.setItem('employeeId', employeeId);
// localStorage.setItem('password', password);
// localStorage.setItem('btid', btid || "unknown");
// // ✅ Open second screen + navigate
// (window as any).electronAPI?.openSecondScreen?.();
// this.router.navigate(['/home']);
// },
// error: () => {
// this.loginError = 'Invalid login credentials';
// }
// });
// }
showConfirmModal : boolean = false;

View File

@ -1,23 +1,57 @@
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ApplicationHttpRouts } from '../constants/http-routs';
// import { HttpClient, HttpHeaders } from '@angular/common/http';
// import { Injectable } from '@angular/core';
// import { ApplicationHttpRouts } from '../constants/http-routs';
@Injectable({
providedIn: 'root',
})
// @Injectable({
// providedIn: 'root',
// })
// // export class BtcService {
// // constructor(private http: HttpClient) {}
// // btid: string | null = null;
// // //user login
// // public userLogin(employeeId: string, password: string) {
// // console.log('Login route ' + ApplicationHttpRouts.LOG_IN);
// // this.btid = localStorage.getItem('btid');
// // if (this.btid) {
// // employeeId += "," + this.btid;
// // } else {
// // employeeId += ",null"; // or just "," if you dont want "null"
// // }
// // return this.http.get<any>(ApplicationHttpRouts.LOG_IN, {
// // headers: this.basicAuthCredentialsBuilder(employeeId, password),
// // withCredentials: true,
// // observe: 'response',
// // });
// // }
// export class BtcService {
// constructor(private http: HttpClient) {}
// btid: string | null = null;
// // user login
// public userLogin(employeeId: string, password: string) {
// public async userLogin(employeeId: string, password: string) {
// console.log('Login route ' + ApplicationHttpRouts.LOG_IN);
// this.btid = localStorage.getItem('btid');
// if (this.btid) {
// employeeId += "," + this.btid;
// try {
// if ((window as any).electronAPI?.getBtid) {
// this.btid = await (window as any).electronAPI.getBtid();
// } else {
// employeeId += ",null"; // or just "," if you dont want "null"
// console.warn('Electron API not available — fallback to null');
// this.btid = null;
// }
// } catch (err) {
// console.error('Error fetching BTID:', err);
// this.btid = null;
// }
// // Append BTID after comma
// if (this.btid) {
// employeeId += ',' + this.btid;
// } else {
// employeeId += ',null';
// }
// return this.http.get<any>(ApplicationHttpRouts.LOG_IN, {
// headers: this.basicAuthCredentialsBuilder(employeeId, password),
// withCredentials: true,
@ -25,9 +59,59 @@ import { ApplicationHttpRouts } from '../constants/http-routs';
// });
// }
// // what goes to the backend for auth ... is the same as postman's basic auth
// private basicAuthCredentialsBuilder(
// employeeOrUserId: string,
// password: string
// ): HttpHeaders {
// console.log(`username and password${employeeOrUserId} p = ${password}`);
// return new HttpHeaders().set(
// 'Authorization',
// 'basic ' + window.btoa(employeeOrUserId + ':' + password)
// );
// }
// public pingLiveStatus() {
// return this.http.get<any>(ApplicationHttpRouts.PING, {
// withCredentials: true,
// observe: 'response',
// responseType: 'text' as 'json',
// });
// }
// // Fetch all race events today
// public getAllRaceEventsToday() {
// return this.http.get<any>(ApplicationHttpRouts.RACE_EVENTS_TODAY, {
// withCredentials: true,
// observe: 'response',
// responseType: 'json',
// });
// }
// public getRaceCard(){
// return this.http.get<any>(ApplicationHttpRouts.RACE_CARD, {
// withCredentials: true,
// observe: 'response',
// responseType: 'json',
// })
// }
// }
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ApplicationHttpRouts } from '../constants/http-routs';
import { of,Observable, tap } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class BtcService {
constructor(private http: HttpClient) {}
btid: string | null = null;
private raceCard: any = null; // cache race card in memory
// user login
public async userLogin(employeeId: string, password: string) {
@ -59,12 +143,12 @@ export class BtcService {
});
}
// what goes to the backend for auth ... is the same as postman's basic auth
// basic auth header
private basicAuthCredentialsBuilder(
employeeOrUserId: string,
password: string
): HttpHeaders {
console.log(`username and password${employeeOrUserId} p = ${password}`);
console.log(`username and password = ${employeeOrUserId} p = ${password}`);
return new HttpHeaders().set(
'Authorization',
@ -72,6 +156,7 @@ export class BtcService {
);
}
// Ping backend
public pingLiveStatus() {
return this.http.get<any>(ApplicationHttpRouts.PING, {
withCredentials: true,
@ -81,7 +166,6 @@ export class BtcService {
}
// Fetch all race events today
public getAllRaceEventsToday() {
return this.http.get<any>(ApplicationHttpRouts.RACE_EVENTS_TODAY, {
withCredentials: true,
@ -90,11 +174,75 @@ export class BtcService {
});
}
public getRaceCard(){
return this.http.get<any>(ApplicationHttpRouts.RACE_CARD, {
withCredentials: true,
observe: 'response',
responseType: 'json',
/**
* Fetch Race Card from backend (POST /download/rpinfo)
* Stores result in memory for reuse
// */
// public fetchRaceCard(
// opCard: string,
// password: string,
// btId: string,
// usrId: string = '',
// btMake: number = 0
// ): Observable<any> {
// const payload = { opCard, password, btId, usrId, btMake };
// return this.http
// .post<any>('http://localhost:8082/download/rpinfo', payload)
// .pipe(
// tap((data) => {
// console.log('📦 Race Card fetched:', data);
// this.raceCard = data; // cache it
// })
// );
// }
// /**
// * Return cached race card (if available)
// */
// public getRaceCard(): any {
// return this.raceCard;
// }
public fetchRaceCard(
opCard: string,
password: string,
btId: string,
usrId: string = '',
btMake: number = 0
): Observable<any> {
const payload = { opCard, password, btId, usrId, btMake };
return this.http
.post<any>('http://localhost:8082/download/rpinfo', payload)
.pipe(
tap((data) => {
console.log('📦 Race Card fetched:', data);
this.raceCard = data; // store in memory
localStorage.setItem('rpinfo', JSON.stringify(data)); // store in localStorage
})
);
}
/**
* Return cached race card:
* - from memory (fastest)
* - fallback to localStorage (if reloaded)
*/
public getRaceCard(): Observable<any> {
if (this.raceCard) {
return of(this.raceCard);
}
const cached = localStorage.getItem('rpinfo');
if (cached) {
this.raceCard = JSON.parse(cached);
return of(this.raceCard);
}
// Nothing available, return empty object
return of(null);
}
}

View File

@ -3,50 +3,23 @@ version: '3.8'
# Define the services for our application stack
services:
# PostgreSQL database service
postgres:
# Using the official PostgreSQL base image.
# 'postgres:16' is recommended for a stable, recent version.
# You can use 'postgres:latest' for the very newest, but versions like 13, 14, 15, 16 are common.
image: postgres:16 # Or postgres:latest, postgres:13-alpine, etc.
container_name: postgres_db # Assign a friendly name to the container
environment:
# --- CRITICAL: These settings will ONLY take effect if ./dbData directory is EMPTY on first run ---
POSTGRES_DB: horse # Your database name
POSTGRES_USER: postgres # Set to 'postgres', the default superuser for the official image
POSTGRES_PASSWORD: root # Your desired password for the 'postgres' user
# --------------------------------------------------------------------------------------------------
ports:
- "5434:5432" # Map host port 5434 to container port 5432
volumes:
# Using a bind mount. You MUST delete the ./dbData directory manually if you change user/pass/db.
# This directory MUST be empty when the container first starts to trigger initialization.
- ./dbData:/var/lib/postgresql/data # Persist PostgreSQL data to a local directory
# Added command for more verbose logging during startup (optional, but highly recommended here)
command: postgres -c log_statement=all
networks:
- app_network # Connect to our custom application network
# networks:
# - app_network # Connect to our custom application network
spring:
#image: mathewfrancisv/spring_back_postgres:v1.0.0
image: mathewfrancisv/btc_cezen_backend:v1.1.6
image: mathewfrancisv/btc_cezen_backend:v2.3.0
container_name: spring_app
ports:
- "8083:8080"
environment:
# jdbc:postgresql://localhost:5432/horse
# SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/horse
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/horse
SPRING_DATASOURCE_USERNAME: postgres # Ensure this matches POSTGRES_USER
SPRING_DATASOURCE_PASSWORD: root # Ensure this matches POSTGRES_PASSWORD
SPRING_DATASOURCE_CORSIP: http://10.74.231.61:4200
SPRING_DATASOURCE_CORSIP: http://10.236.119.124:4200
#network_mode: host
networks:
- app_network
depends_on:
- postgres
# depends_on:
# - postgres
# Angular frontend application service
angular-dev: