fix : field fetches data from the backend

This commit is contained in:
karthik 2025-08-02 22:46:13 +05:30
parent 719f16d734
commit f6b8dba352
5 changed files with 156 additions and 196 deletions

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { SharedStateService } from '../../service/shared-state.service'; // Corrected path
export interface SelectionData {
label: string;
@ -23,6 +24,14 @@ export class SelectionService {
});
currentRow$ = this.currentRow.asObservable();
private runnerCount: number = 12; // Fallback
constructor(private sharedStateService: SharedStateService) {
this.sharedStateService.runnerCount$.subscribe(count => {
this.runnerCount = count || 12; // Update runner count dynamically
});
}
updatePartial(update: Partial<SelectionData>) {
const current = this.currentRow.value;
const updated: SelectionData = { ...current, ...update };
@ -47,7 +56,7 @@ export class SelectionService {
case 'PLC':
case 'SHW': {
if (numbers.includes('F')) {
updated.total = 12 * value * 10;
updated.total = this.runnerCount * value * 10;
} else {
updated.total = numbers.filter(n => typeof n === 'number').length * value * 10;
}
@ -58,23 +67,23 @@ export class SelectionService {
const dashIndex = numbers.indexOf('-');
const group1 = dashIndex === -1 ? numbers : numbers.slice(0, dashIndex);
const group2 = dashIndex === -1 ? [] : numbers.slice(dashIndex + 1);
const group1Count = group1.includes('F') ? 12 : group1.filter(n => typeof n === 'number').length;
const group2Count = group2.includes('F') ? 12 : group2.filter(n => typeof n === 'number').length;
const group1Count = group1.includes('F') ? this.runnerCount : group1.filter(n => typeof n === 'number').length;
const group2Count = group2.includes('F') ? this.runnerCount : group2.filter(n => typeof n === 'number').length;
let combinations = 0;
if (isBoxed) {
const allNums = [...group1, ...group2].filter(n => typeof n === 'number') as number[];
const uniqueCount = new Set(allNums).size + (group1.includes('F') || group2.includes('F') ? 12 : 0);
const uniqueCount = new Set(allNums).size + (group1.includes('F') || group2.includes('F') ? this.runnerCount : 0);
combinations = uniqueCount >= 2 ? this.calculatePermutations(uniqueCount) : 0;
updated.numbers = [...group1, '-', ...group2];
} else {
if (group1.includes('F') && group2.includes('F')) {
combinations = 12 * 11; // nP2 for 12 runners
combinations = this.runnerCount * (this.runnerCount - 1); // nP2 for dynamic runners
} else if (group1.includes('F') || group2.includes('F')) {
const nonFieldGroup = group1.includes('F') ? group2 : group1;
const nonFieldNumbers = nonFieldGroup.filter(n => typeof n === 'number') as number[];
combinations = 12 * nonFieldNumbers.length;
combinations = this.runnerCount * nonFieldNumbers.length;
if (nonFieldNumbers.length > 0) {
combinations -= nonFieldNumbers.length; // Subtract overlapping runners
}
@ -107,15 +116,15 @@ export class SelectionService {
if (isBoxed) {
const allNums = [...group1, ...group2].filter(n => typeof n === 'number') as number[];
const uniqueCount = new Set(allNums).size + (group1_is_field || group2_is_field ? 12 : 0);
const uniqueCount = new Set(allNums).size + (group1_is_field || group2_is_field ? this.runnerCount : 0);
combinations = uniqueCount >= 2 ? (uniqueCount * (uniqueCount - 1)) / 2 : 0;
} else if (group1_is_field && group2_is_field) {
combinations = (12 * 11) / 2; // C(12,2)
combinations = (this.runnerCount * (this.runnerCount - 1)) / 2; // C(n,2)
} else if (group1_is_field || group2_is_field) {
const fieldSide = group1_is_field ? group1Numbers : group2Numbers;
const nonFieldNumbers = (group1_is_field ? group2 : group1).filter(n => typeof n === 'number') as number[];
const pairSet = new Set<string>();
const fieldNumbers = fieldSide.length > 0 ? fieldSide : Array.from({ length: 12 }, (_, i) => i + 1);
const fieldNumbers = fieldSide.length > 0 ? fieldSide : Array.from({ length: this.runnerCount }, (_, i) => i + 1);
for (let j of nonFieldNumbers) {
for (let i of fieldNumbers) {
if (i !== j) {
@ -158,14 +167,13 @@ export class SelectionService {
if (isBoxed) {
if (numbers.includes('F')) {
combinations = this.calculatePermutationsN(12, 3);
combinations = this.calculatePermutationsN(this.runnerCount, 3);
} else {
const allNums = [...group1, ...group2, ...group3].filter(n => typeof n === 'number') as number[];
combinations = allNums.length >= 3 ? this.calculatePermutationsN(allNums.length, 3) : 0;
}
} else {
// Define possible values for each group: 'F' means all runners (1 to 12), else only selected numbers
const runners = Array.from({ length: 12 }, (_, i) => i + 1);
const runners = Array.from({ length: this.runnerCount }, (_, i) => i + 1);
const group1Vals = group1.includes('F') ? runners : group1.filter(n => typeof n === 'number') as number[];
const group2Vals = group2.includes('F') ? runners : group2.filter(n => typeof n === 'number') as number[];
const group3Vals = group3.includes('F') ? runners : group3.filter(n => typeof n === 'number') as number[];
@ -187,13 +195,61 @@ export class SelectionService {
break;
}
case 'EXA': {
const dashIndex = numbers.indexOf('-');
const group1 = dashIndex === -1 ? numbers : numbers.slice(0, dashIndex);
const group2 = dashIndex === -1 ? [] : numbers.slice(dashIndex + 1);
const group1Count = group1.includes('F') ? this.runnerCount : group1.filter(n => typeof n === 'number').length;
const group2Count = group2.includes('F') ? this.runnerCount : group2.filter(n => typeof n === 'number').length;
let combinations = 0;
if (isBoxed) {
const allNums = [...group1, ...group2].filter(n => typeof n === 'number') as number[];
const uniqueCount = new Set(allNums).size + (group1.includes('F') || group2.includes('F') ? this.runnerCount : 0);
combinations = uniqueCount >= 2 ? this.calculatePermutations(uniqueCount) : 0;
} else {
if (group1.includes('F') && group2.includes('F')) {
combinations = this.runnerCount * (this.runnerCount - 1);
} else if (group1.includes('F') || group2.includes('F')) {
const nonFieldGroup = group1.includes('F') ? group2 : group1;
const nonFieldNumbers = nonFieldGroup.filter(n => typeof n === 'number') as number[];
combinations = this.runnerCount * nonFieldNumbers.length;
if (nonFieldNumbers.length > 0) {
combinations -= nonFieldNumbers.length;
}
} else {
const numGroup1 = group1.filter(n => typeof n === 'number') as number[];
const numGroup2 = group2.filter(n => typeof n === 'number') as number[];
for (const a of numGroup1) {
for (const b of numGroup2) {
if (a !== b) combinations++;
}
}
}
}
updated.numbers = [...group1, '-', ...group2];
updated.total = combinations * value * 10;
break;
}
case 'WSP': {
if (numbers.includes('F')) {
updated.total = this.runnerCount * value * 10;
} else {
updated.total = numbers.filter(n => typeof n === 'number').length * value * 10;
}
break;
}
case 'TRE':
case 'MJP':
case 'JKP': {
const legs = this.splitToLegs(numbers, this.getLegCount(label));
const requiredLegs = this.getLegCount(label);
const legCounts = legs.map(leg => leg.includes('F') ? 12 : leg.filter(n => typeof n === 'number').length);
const legCounts = legs.map(leg => leg.includes('F') ? this.runnerCount : leg.filter(n => typeof n === 'number').length);
const filledLegs = legs.filter(leg => leg.length > 0).length;
if (filledLegs >= requiredLegs - 1) {

View File

@ -1,10 +1,8 @@
<!-- Touch Pad Container -->
<div class="touch-pad-container scrollable-touchpad">
<!-- 🔘 Top Button Section (Responsive Wrapper) -->
<div class="container-fluid mb-2">
<div class="d-flex flex-wrap justify-content-center gap-2 flex-md-nowrap flex-column flex-md-row align-items-md-center wrap_one">
<!-- Left Group: CALC, ERASE, BOX, FIELD -->
<div class="d-flex flex-wrap justify-content-center gap-2 first">
<button class="btn btn-dark one" (click)="openCalculator()">CALC</button>
@ -16,11 +14,8 @@
>
BOX
</button>
<!-- ✅ FIELD Button -->
<button class="btn btn-field" [disabled]="!canUseField()" (click)="openFieldModal()">FIELD</button>
</div>
<!-- Right Group: ENTER, COPY, CLEAR, PR -->
<div class="d-flex flex-wrap justify-content-center gap-2 second">
<button
@ -31,21 +26,16 @@
>
shash ENTER
</button>
<button class="btn btn-dark">COPY</button>
<button class="btn btn-danger btn-bkp" *ngIf="showBackspace" (click)="removeLastNumber()">BKP</button>
<button class="btn btn-dark">CLEAR</button>
<button class="btn btn-info pool-replace-btn" (click)="openPoolReplaceModal()">PR</button>
</div>
</div>
</div>
<!-- 🔳 Main Grid Area -->
<div class="container-fluid">
<div class="row gx-2 gy-2 wrapper">
<!-- Label Section -->
<div class="col-3">
<div
@ -66,8 +56,7 @@
</div>
</div>
</div>
<!-- Numbers 1 to 30 -->
<!-- Numbers 1 to runnerCount -->
<div class="col-7">
<div class="p-2 rounded wrapper-pad" style="background-color: #f1f1f1df">
<div class="row gx-1 gy-1 custom-touchpad">
@ -83,7 +72,6 @@
</div>
</div>
</div>
<!-- Numeric Pad -->
<div class="col-2 column">
<div class="p-2 rounded wrapper-pad" style="background-color: #f1f1f1df">
@ -94,7 +82,6 @@
<div class="col-6">
<button class="btn btn-light w-100 number-button" [disabled]="!numericPadEnabled" (click)="enterPadVal('X')">X</button>
</div>
<ng-container *ngFor="let key of ['7', '8', '9', '4', '5', '6', '1', '2', '3']">
<div class="col-4">
<button class="btn btn-light w-100 number-button" [disabled]="!numericPadEnabled" (click)="enterPadVal(key)">
@ -102,7 +89,6 @@
</button>
</div>
</ng-container>
<div class="col-6">
<button class="btn btn-secondary w-100 number-button" [disabled]="!numericPadEnabled" (click)="onPadEnter()">Enter</button>
</div>
@ -114,7 +100,6 @@
</div>
</div>
</div>
<!-- 🧮 Calculator Modal -->
<div id="calculatorModal" class="calculator-modal" [style.display]="calculatorOpen ? 'block' : 'none'">
<div class="calculator-content">
@ -128,28 +113,23 @@
<button (click)="press('8')">8</button>
<button (click)="press('9')">9</button>
<button (click)="press('/')">÷</button>
<button (click)="press('4')">4</button>
<button (click)="press('5')">5</button>
<button (click)="press('6')">6</button>
<button (click)="press('*')">×</button>
<button (click)="press('1')">1</button>
<button (click)="press('2')">2</button>
<button (click)="press('3')">3</button>
<button (click)="press('-')"></button>
<button (click)="press('0')">0</button>
<button (click)="press('.')">.</button>
<button (click)="calculate()">=</button>
<button (click)="press('+')">+</button>
<button (click)="clearDisplay()" class="span-two">C</button>
<button class="calc-backspace-btn" (click)="backspace()"></button>
</div>
</div>
</div>
<!-- 🟦 FIELD Modal -->
<div id="fieldModal" class="field-modal" [style.display]="fieldModalOpen ? 'block' : 'none'">
<div class="field-modal-content">
@ -157,12 +137,9 @@
<span class="close-btn" (click)="closeFieldModal()">&times;</span>
<h5>FIELD</h5>
</div>
<label>Runners selection</label>
<input type="text" class="field-display" [value]="fieldInput" readonly />
<p><strong>Total Runners</strong><br />1,2,3,4,5,6,7,8,9,10,11,12</p>
<p><strong>Total Runners</strong><br />{{ numbers.join(',') }}</p>
<div class="field-buttons">
<ng-container *ngFor="let key of ['0', '-', 'F', 'BACK', '7', '8', '9', '4', '5', '6', '1', '2', '3']">
<button
@ -172,13 +149,11 @@
{{ key }}
</button>
</ng-container>
<button class="span-two" (click)="confirmFieldEntry()" [disabled]="!fieldFEntered">ENTER</button>
<button class="field-cancel-btn" (click)="closeFieldModal()">CANCEL</button>
</div>
</div>
</div>
<!-- 🟩 POOL REPLACE Modal -->
<div
id="poolReplaceModal"
@ -205,7 +180,6 @@
</div>
</div>
</div>
<!-- 🟦 TRE Popup Modal -->
<div
class="tre-popup-backdrop"
@ -224,7 +198,6 @@
<button class="btn btn-secondary my-2 w-100" style="opacity: 0.6; pointer-events: none;">TRB3</button>
</div>
</div>
<!-- 🟥 Limit Popup Modal -->
<div
class="limit-overlay"
@ -246,5 +219,4 @@
<button class="limit-button" (click)="closeLimitPopup()">OK</button>
</div>
</div>
</div>

View File

@ -1,8 +1,9 @@
import { Component, Input, OnInit, OnDestroy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SelectionService } from '../selection.service/selection.service';
import { Subscription } from 'rxjs';
import { SelectionService } from '../selection.service/selection.service';
import { SelectionData } from '../selection.service/selection.service';
import { SharedStateService } from '../../service/shared-state.service';
@Component({
selector: 'app-touch-pad-menu',
@ -26,6 +27,7 @@ export class TouchPadMenuComponent implements OnInit, OnDestroy {
];
numbers: number[] = Array.from({ length: 30 }, (_, i) => i + 1);
runnerCount: number = 12; // Fallback
labelRowsFlat: string[] = [];
numbersFlat: number[] = [];
@ -69,14 +71,23 @@ export class TouchPadMenuComponent implements OnInit, OnDestroy {
private currentRowSubscription: Subscription | null = null;
private selectionsSubscription: Subscription | null = null;
private runnerCountSubscription: Subscription | null = null;
private currentTotal: number = 0;
private currentSelections: SelectionData[] = [];
constructor(private selectionService: SelectionService) {}
constructor(
private selectionService: SelectionService,
private sharedStateService: SharedStateService
) {}
ngOnInit() {
this.labelRowsFlat = this.labelRows.flat();
this.runnerCountSubscription = this.sharedStateService.runnerCount$.subscribe(count => {
this.runnerCount = count || 12;
this.numbers = Array.from({ length: 30 }, (_, i) => i + 1); // Always 1 to 30
this.numbersFlat = this.numberRows.flat();
});
this.labelRowsFlat = this.labelRows.flat();
this.selectionsSubscription = this.selectionService.selections$.subscribe(selections => {
this.currentSelections = selections;
this.maxRowsReached = selections.length >= 5;
@ -92,12 +103,9 @@ export class TouchPadMenuComponent implements OnInit, OnDestroy {
}
ngOnDestroy() {
if (this.currentRowSubscription) {
this.currentRowSubscription.unsubscribe();
}
if (this.selectionsSubscription) {
this.selectionsSubscription.unsubscribe();
}
this.currentRowSubscription?.unsubscribe();
this.selectionsSubscription?.unsubscribe();
this.runnerCountSubscription?.unsubscribe();
}
get labelRows() {
@ -170,6 +178,20 @@ export class TouchPadMenuComponent implements OnInit, OnDestroy {
return this.disabledLabels.includes(label) || this.totalAmountLimitReached;
}
isNumberDisabled(number: number): boolean {
// Only numbers up to runnerCount are enabled, others are disabled
if (number > this.runnerCount) {
return true;
}
if (this.selectedLabel === 'TAN' && this.isBoxed) {
return false;
}
if (this.selectedLabel === 'TAN' || this.multiLegLabels.includes(this.selectedLabel || '') || this.twoGroupLabels.includes(this.selectedLabel || '')) {
return false;
}
return this.selectedNumbers.includes(number) || this.totalAmountLimitReached;
}
selectLabel(label: string) {
if (this.totalAmountLimitReached) {
this.showLimitPopup = true;
@ -202,7 +224,7 @@ export class TouchPadMenuComponent implements OnInit, OnDestroy {
}
selectNumber(number: number) {
if (!this.selectedLabel || this.totalAmountLimitReached) return;
if (!this.selectedLabel || this.totalAmountLimitReached || number > this.runnerCount) return;
// TAN Box mode: freestyle selection with dash-separated format
if (this.selectedLabel === 'TAN' && this.isBoxed) {
@ -287,17 +309,6 @@ export class TouchPadMenuComponent implements OnInit, OnDestroy {
this.selectionService.updatePartial({ numbers: [...this.selectedNumbers] });
}
isNumberDisabled(number: number): boolean {
// For TAN Box mode, allow all numbers to be selectable
if (this.selectedLabel === 'TAN' && this.isBoxed) {
return false;
}
if (this.selectedLabel === 'TAN' || this.multiLegLabels.includes(this.selectedLabel || '') || this.twoGroupLabels.includes(this.selectedLabel || '')) {
return false;
}
return this.selectedNumbers.includes(number) || this.totalAmountLimitReached;
}
onPadEnter() {
if (this.canPrint) {
this.print();
@ -392,11 +403,9 @@ export class TouchPadMenuComponent implements OnInit, OnDestroy {
this.tanGroupStage = 0;
this.tanGroups = [[], [], []];
this.isFirstGroupComplete = false;
this.firstGroup = [];
this.secondGroup = [];
this.multiLegStage = 0;
this.multiLegGroups = [[], [], [], [], []];

View File

@ -1,109 +1,4 @@
// import {
// Component,
// ChangeDetectorRef,
// OnInit,
// OnDestroy,
// } from '@angular/core';
// import { SidebarComponent } from '../components/sidebar/sidebar.component';
// import { NavbarComponent } from '../components/navbar/navbar.component';
// import { MiddleSectionComponent } from '../components/middle-section/middle-section.component';
// import { TouchPadMenuComponent } from '../components/touch-pad-menu/touch-pad-menu.component';
// import { CommonModule } from '@angular/common';
// import { BtcService } from '../service/btc.service';
// import { HttpResponse } from '@angular/common/http';
// import { HorseService } from '../service/horseData.service';
// import { HorseRaceModel } from '../model/horseRaceData';
// import { SharedStateService } from '../service/shared-state.service';
// @Component({
// selector: 'app-home',
// standalone: true,
// imports: [
// CommonModule,
// SidebarComponent,
// NavbarComponent,
// MiddleSectionComponent,
// TouchPadMenuComponent,
// ],
// templateUrl: './home.component.html',
// styleUrls: ['./home.component.css'],
// })
// export class HomeComponent implements OnInit, OnDestroy {
// isTabletView = false;
// isTicketingActive = false;
// private resizeObserver!: () => void;
// constructor(
// private cdr: ChangeDetectorRef,
// private btcService: BtcService,
// private horseService: HorseService,
// private sharedStateService: SharedStateService,
// ) {}
// ngOnInit() {
// this.updateView();
// this.resizeObserver = () => {
// this.updateView();
// this.cdr.detectChanges();
// };
// window.addEventListener('resize', this.resizeObserver);
// console.log('Hit hitttt');
// // Fetch race data and push to shared screen
// this.btcService.getAllRaceEventsToday().subscribe({
// next: (response: HttpResponse<HorseRaceModel[]>) => {
// const horseRaceData = response.body;
// this.horseService.HorseData.next(horseRaceData);
// // 🔁 Push to shared screen
// this.sharedStateService.updateSharedData({
// type: 'horseData',
// value: horseRaceData,
// });
// },
// error: (error) => {
// console.log('Error fetching race data');
// },
// });
// }
// ngOnDestroy() {
// window.removeEventListener('resize', this.resizeObserver);
// }
// private updateView() {
// this.isTabletView = window.innerWidth <= 800;
// }
// onTicketingClicked() {
// this.isTicketingActive = true;
// // 🔁 Push ticketing state to shared screen
// this.sharedStateService.updateSharedData({
// type: 'ticketing',
// value: true,
// });
// }
// onOtherActionClicked() {
// this.isTicketingActive = false;
// // 🔁 Push ticketing state to shared screen
// this.sharedStateService.updateSharedData({
// type: 'ticketing',
// value: false,
// });
// }
// }
import {
Component,
ChangeDetectorRef,
OnInit,
OnDestroy,
} from '@angular/core';
import { Component, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core';
import { SidebarComponent } from '../components/sidebar/sidebar.component';
import { NavbarComponent } from '../components/navbar/navbar.component';
import { MiddleSectionComponent } from '../components/middle-section/middle-section.component';
@ -132,6 +27,8 @@ export class HomeComponent implements OnInit, OnDestroy {
isTabletView = false;
isTicketingActive = false;
private resizeObserver!: () => void;
private currentRaceIdx: number = 0; // Track current race
races: any[] = []; // Store race data
constructor(
private cdr: ChangeDetectorRef,
@ -156,8 +53,7 @@ export class HomeComponent implements OnInit, OnDestroy {
next: (response: HttpResponse<HorseRaceModel[]>) => {
const horseRaceData = response.body;
this.horseService.HorseData.next(horseRaceData);
// 🔁 Push to shared screen
this.races = horseRaceData || []; // Populate races for selection
this.sharedStateService.updateSharedData({
type: 'horseData',
value: horseRaceData,
@ -176,6 +72,7 @@ export class HomeComponent implements OnInit, OnDestroy {
const raceCardData = res.body;
console.log('📦 Race card preloaded:', raceCardData);
localStorage.setItem('raceCardData', JSON.stringify(raceCardData));
this.updateRunnerCount(0); // Initialize with first race
},
error: (err) => {
console.error('❌ Failed to preload race card:', err);
@ -183,6 +80,7 @@ export class HomeComponent implements OnInit, OnDestroy {
});
} else {
console.log('📦 Race card already cached');
this.updateRunnerCount(0); // Initialize with first race
}
}
@ -213,4 +111,18 @@ export class HomeComponent implements OnInit, OnDestroy {
value: false,
});
}
onRaceSelected(raceIdx: number) {
this.currentRaceIdx = raceIdx;
this.updateRunnerCount(raceIdx);
}
private updateRunnerCount(raceIdx: number) {
const raceCardData = JSON.parse(localStorage.getItem('raceCardData') || '{}');
const runnerCount = raceCardData?.raceVenueRaces?.races?.[raceIdx]?.length || 12;
if (!raceCardData?.raceVenueRaces?.races?.[raceIdx]) {
console.warn('⚠️ Race data not found for index:', raceIdx);
}
this.sharedStateService.setRunnerCount(runnerCount);
}
}

View File

@ -1,9 +1,11 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class SharedStateService {
private runnerCountSubject = new BehaviorSubject<number>(12); // Default runner count
runnerCount$ = this.runnerCountSubject.asObservable();
private sharedDataSubject = new BehaviorSubject<any>(null);
sharedData$ = this.sharedDataSubject.asObservable();
@ -13,13 +15,22 @@ export class SharedStateService {
// Listen to messages from other windows
this.channel.onmessage = (event) => {
console.log('[BroadcastChannel] Received data:', event.data);
this.sharedDataSubject.next(event.data); // sync incoming data
if (event.data?.runnerCount !== undefined) {
this.runnerCountSubject.next(event.data.runnerCount); // Sync runner count
}
this.sharedDataSubject.next(event.data); // Sync other shared data
};
}
setRunnerCount(count: number) {
console.log('[SharedStateService] Broadcasting runner count:', count);
this.channel.postMessage({ runnerCount: count }); // Broadcast runner count
this.runnerCountSubject.next(count); // Update local runner count
}
updateSharedData(data: any) {
console.log('[SharedStateService] Broadcasting data:', data);
this.channel.postMessage(data); // send to other windows
this.sharedDataSubject.next(data); // update local BehaviorSubject
this.channel.postMessage(data); // Send to other windows
this.sharedDataSubject.next(data); // Update local BehaviorSubject
}
}