first step for plr game platform
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
import { Course } from './course';
|
||||
|
||||
|
||||
export enum ResultatStatut {
|
||||
PROVISOIRE,
|
||||
OFFICIEL,
|
||||
ANNULE,
|
||||
EN_ATTENTE
|
||||
}
|
||||
|
||||
export interface Resultat {
|
||||
id: string;
|
||||
course: Course;
|
||||
@@ -8,7 +16,7 @@ export interface Resultat {
|
||||
* The backend returns an array of strings/numbers (cheval numbers);
|
||||
* in the UI we normalize them to plain numbers.
|
||||
*/
|
||||
ordreArrivee: number[];
|
||||
ordreArrivee: string;
|
||||
/**
|
||||
* Chevaux en dead-heat (ex aequo), represented by their numbers.
|
||||
*/
|
||||
@@ -26,33 +34,24 @@ export interface Resultat {
|
||||
// API response structure (course may be just an ID in some cases)
|
||||
export interface ResultatApiResponse {
|
||||
id: string | number;
|
||||
course: Course | string | number;
|
||||
/**
|
||||
* In the raw API this is an array of strings/numbers.
|
||||
*/
|
||||
ordreArrivee: (string | number)[];
|
||||
chevauxDeadHeat: (string | number)[];
|
||||
totalMises: number;
|
||||
masseAPartager: number;
|
||||
prelevementsLegaux: number;
|
||||
montantRembourse: number;
|
||||
montantCagnotte: number;
|
||||
adeadHeat: boolean;
|
||||
courseId: string | number;
|
||||
ordreArrivee: string;
|
||||
courseNom: string;
|
||||
courseNumero: number;
|
||||
reunionNumero:number;
|
||||
hippodromeNom: string;
|
||||
statut: ResultatStatut;
|
||||
datePublication?: string;
|
||||
dateAnnulation?: string;
|
||||
dateValidation?: string;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
// POST payload structure
|
||||
export interface CreateResultatPayload {
|
||||
course: {
|
||||
id: string | number;
|
||||
};
|
||||
ordreArrivee: string[];
|
||||
chevauxDeadHeat?: (string | number)[];
|
||||
totalMises?: number;
|
||||
masseAPartager?: number;
|
||||
prelevementsLegaux?: number;
|
||||
montantRembourse?: number;
|
||||
montantCagnotte?: number;
|
||||
adeadHeat?: boolean;
|
||||
courseId: number;
|
||||
statut: ResultatStatut;
|
||||
ordreArrivee: string;
|
||||
notes?: string
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ export class AgentLimitService {
|
||||
let httpParams = new HttpParams();
|
||||
if (params) {
|
||||
if (params.page) httpParams = httpParams.set('page', params.page.toString());
|
||||
if (params.perPage) httpParams = httpParams.set('perPage', params.perPage.toString());
|
||||
if (params.size) httpParams = httpParams.set('size', params.size.toString());
|
||||
if (params.search) httpParams = httpParams.set('search', params.search);
|
||||
if (params.sortKey) httpParams = httpParams.set('sortKey', params.sortKey);
|
||||
if (params.sortDir) httpParams = httpParams.set('sortDir', params.sortDir);
|
||||
@@ -127,7 +127,7 @@ export class AgentLimitService {
|
||||
return normalizePage<AgentLimit>(
|
||||
{ data: limits, meta: { total: limits.length } },
|
||||
params.page || 1,
|
||||
params.perPage || 10
|
||||
params.size || 10
|
||||
);
|
||||
}
|
||||
// Otherwise return all as single page
|
||||
@@ -207,7 +207,7 @@ export class AgentLimitService {
|
||||
// First, find the previous default limit
|
||||
return this.list({
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
size: 1000,
|
||||
search: '',
|
||||
sortKey: 'code',
|
||||
sortDir: 'asc',
|
||||
|
||||
@@ -274,7 +274,7 @@ export class AgentService {
|
||||
let httpParams = new HttpParams();
|
||||
if (params) {
|
||||
if (params.page) httpParams = httpParams.set('page', params.page.toString());
|
||||
if (params.perPage) httpParams = httpParams.set('perPage', params.perPage.toString());
|
||||
if (params.size) httpParams = httpParams.set('perPage', params.size.toString());
|
||||
if (params.search) httpParams = httpParams.set('search', params.search);
|
||||
if (params.sortKey) httpParams = httpParams.set('sortKey', params.sortKey);
|
||||
if (params.sortDir) httpParams = httpParams.set('sortDir', params.sortDir);
|
||||
@@ -296,7 +296,7 @@ export class AgentService {
|
||||
return normalizePage<Agent>(
|
||||
{ data: agents, meta: { total: agents.length } },
|
||||
params.page || 1,
|
||||
params.perPage || 10
|
||||
params.size || 10
|
||||
);
|
||||
}
|
||||
// Otherwise return all as single page
|
||||
@@ -445,7 +445,7 @@ export class AgentService {
|
||||
// Get all agents first
|
||||
return this.list({
|
||||
page: 1,
|
||||
perPage: 10000,
|
||||
size: 10000,
|
||||
search: '',
|
||||
sortKey: 'code',
|
||||
sortDir: 'asc',
|
||||
|
||||
@@ -59,7 +59,7 @@ export class CourseService {
|
||||
return isNgrok ? { 'ngrok-skip-browser-warning': 'true' } : {};
|
||||
}
|
||||
|
||||
list(params: ListParams, usePaginationEndpoint: boolean = true): Observable<PagedResult<Course>> {
|
||||
list(params: ListParams): Observable<PagedResult<Course>> {
|
||||
const coursesList = this.http.get<PagedResult<CourseApiResponse>>(this.apiUrl, {
|
||||
headers: this.getNgrokHeaders(),
|
||||
params: this.servivesUtil.getParamsFromModel(params),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable, of, forkJoin } from 'rxjs';
|
||||
import { PaginatedHttpService } from '@shared/paging/paginated-http.service';
|
||||
import { ListParams, PagedResult } from '@shared/paging/paging';
|
||||
import { map, catchError, switchMap } from 'rxjs/operators';
|
||||
import { Resultat, ResultatApiResponse, CreateResultatPayload } from '../interfaces/resultat';
|
||||
import { Course } from '../interfaces/course';
|
||||
@@ -8,13 +10,20 @@ import { CourseService } from './course';
|
||||
import { environment } from 'src/environments/environment.development';
|
||||
|
||||
const USE_SERVER = true;
|
||||
const API_BASE = '/api/v1/resultat';
|
||||
const API_BASE = '/api/resultats';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ResultatService {
|
||||
private apiUrl = environment.apiBaseUrl + API_BASE;
|
||||
|
||||
constructor(private http: HttpClient, private courseService: CourseService) {}
|
||||
constructor(private http: HttpClient, private courseService: CourseService, private pager: PaginatedHttpService) {}
|
||||
|
||||
// Fetch raw paginated resultats from the backend and normalize paging
|
||||
listRawPaged(params: ListParams): Observable<PagedResult<ResultatApiResponse>> {
|
||||
const url = this.apiUrl;
|
||||
// Delegate to shared paginated HTTP helper (Spring-style defaults)
|
||||
return this.pager.fetch<ResultatApiResponse>(url, params);
|
||||
}
|
||||
|
||||
private getNgrokHeaders(): Record<string, string> {
|
||||
const isNgrok =
|
||||
@@ -31,11 +40,8 @@ export class ResultatService {
|
||||
.get<ResultatApiResponse>(`${this.apiUrl}/${id}`, { headers: this.getNgrokHeaders() })
|
||||
.pipe(
|
||||
switchMap((apiResultat) => {
|
||||
// Fetch the full course object if course is just an ID
|
||||
const courseId =
|
||||
typeof apiResultat.course === 'object' && 'id' in apiResultat.course
|
||||
? String(apiResultat.course.id)
|
||||
: String(apiResultat.course);
|
||||
// New API uses `courseId` explicitly
|
||||
const courseId = String((apiResultat as any).courseId ?? '');
|
||||
|
||||
return this.courseService.getById(courseId).pipe(
|
||||
map((course) => {
|
||||
@@ -62,16 +68,8 @@ export class ResultatService {
|
||||
.get<ResultatApiResponse[]>(this.apiUrl, { headers: this.getNgrokHeaders() })
|
||||
.pipe(
|
||||
switchMap((apiResultats) => {
|
||||
// Fetch all unique course IDs
|
||||
const courseIds = [
|
||||
...new Set(
|
||||
apiResultats.map((r) =>
|
||||
typeof r.course === 'object' && 'id' in r.course
|
||||
? String(r.course.id)
|
||||
: String(r.course)
|
||||
)
|
||||
),
|
||||
];
|
||||
// Fetch all unique course IDs (API uses courseId)
|
||||
const courseIds = [...new Set(apiResultats.map((r) => String((r as any).courseId ?? '')))].filter(Boolean);
|
||||
// Fetch all courses in parallel
|
||||
const courseRequests = courseIds.map((id) =>
|
||||
this.courseService
|
||||
@@ -91,10 +89,7 @@ export class ResultatService {
|
||||
|
||||
return apiResultats
|
||||
.map((apiResultat) => {
|
||||
const courseId =
|
||||
typeof apiResultat.course === 'object' && 'id' in apiResultat.course
|
||||
? String(apiResultat.course.id)
|
||||
: String(apiResultat.course);
|
||||
const courseId = String((apiResultat as any).courseId ?? '');
|
||||
const course = courseMap.get(courseId);
|
||||
if (!course) {
|
||||
return null;
|
||||
@@ -118,11 +113,23 @@ export class ResultatService {
|
||||
return of([]);
|
||||
}
|
||||
|
||||
// GET raw API responses (ResultatApiResponse[])
|
||||
listRaw(): Observable<ResultatApiResponse[]> {
|
||||
if (USE_SERVER) {
|
||||
return this.http
|
||||
.get<ResultatApiResponse[]>(this.apiUrl, { headers: this.getNgrokHeaders() })
|
||||
.pipe(
|
||||
catchError((err) => {
|
||||
console.error('Error fetching raw resultats:', err);
|
||||
return of([] as ResultatApiResponse[]);
|
||||
})
|
||||
);
|
||||
}
|
||||
return of([] as ResultatApiResponse[]);
|
||||
}
|
||||
|
||||
// GET /api/v1/resultat/course/{courseId}
|
||||
getByCourseId(courseId: string): Observable<Resultat | undefined> {
|
||||
if (!USE_SERVER) {
|
||||
return of(undefined);
|
||||
}
|
||||
|
||||
return this.http
|
||||
.get<any>(`${this.apiUrl}/course/${courseId}`, {
|
||||
@@ -169,28 +176,25 @@ export class ResultatService {
|
||||
|
||||
// POST /api/v1/resultat
|
||||
create(payload: CreateResultatPayload): Observable<Resultat> {
|
||||
if (USE_SERVER) {
|
||||
return this.http
|
||||
.post<ResultatApiResponse>(this.apiUrl, payload, { headers: this.getNgrokHeaders() })
|
||||
.pipe(
|
||||
switchMap((apiResultat) => {
|
||||
const courseId = String(payload.course.id);
|
||||
return this.courseService.getById(courseId).pipe(
|
||||
map((course) => {
|
||||
if (!course) {
|
||||
throw new Error('Course not found');
|
||||
}
|
||||
return this.transformApiResponse(apiResultat, course);
|
||||
})
|
||||
);
|
||||
}),
|
||||
catchError((err) => {
|
||||
console.error('Error creating resultat:', err);
|
||||
throw err;
|
||||
})
|
||||
);
|
||||
}
|
||||
throw new Error('Server mode is required');
|
||||
return this.http
|
||||
.post<ResultatApiResponse>(this.apiUrl, payload, { headers: this.getNgrokHeaders() })
|
||||
.pipe(
|
||||
switchMap((apiResultat) => {
|
||||
const courseId = String(payload.courseId);
|
||||
return this.courseService.getById(courseId).pipe(
|
||||
map((course) => {
|
||||
if (!course) {
|
||||
throw new Error('Course not found');
|
||||
}
|
||||
return this.transformApiResponse(apiResultat, course);
|
||||
})
|
||||
);
|
||||
}),
|
||||
catchError((err) => {
|
||||
console.error('Error creating resultat:', err);
|
||||
throw err;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// PUT /api/v1/resultat/{id}
|
||||
@@ -202,10 +206,7 @@ export class ResultatService {
|
||||
})
|
||||
.pipe(
|
||||
switchMap((apiResultat) => {
|
||||
const courseId =
|
||||
typeof apiResultat.course === 'object' && 'id' in apiResultat.course
|
||||
? String(apiResultat.course.id)
|
||||
: String(apiResultat.course);
|
||||
const courseId = String((apiResultat as any).courseId ?? '');
|
||||
|
||||
return this.courseService.getById(courseId).pipe(
|
||||
map((course) => {
|
||||
@@ -261,20 +262,17 @@ export class ResultatService {
|
||||
return {
|
||||
id: String(apiResultat.id),
|
||||
course,
|
||||
// Normalize ordreArrivee to an array of cheval numbers
|
||||
ordreArrivee: (apiResultat.ordreArrivee || [])
|
||||
.map((v) => (typeof v === 'string' ? Number(v) : v))
|
||||
.filter((v): v is number => typeof v === 'number' && !Number.isNaN(v)),
|
||||
// Normalize dead-heat horses to numbers as well
|
||||
chevauxDeadHeat: (apiResultat.chevauxDeadHeat || [])
|
||||
.map((v) => (typeof v === 'string' ? Number(v) : v))
|
||||
.filter((v): v is number => typeof v === 'number' && !Number.isNaN(v)),
|
||||
totalMises: apiResultat.totalMises,
|
||||
masseAPartager: apiResultat.masseAPartager,
|
||||
prelevementsLegaux: apiResultat.prelevementsLegaux,
|
||||
montantRembourse: apiResultat.montantRembourse,
|
||||
montantCagnotte: apiResultat.montantCagnotte,
|
||||
adeadHeat: apiResultat.adeadHeat,
|
||||
// API now returns 'ordreArrivee' as CSV/string; normalize to number[]
|
||||
ordreArrivee: apiResultat.ordreArrivee,
|
||||
// dead-heat not provided by new API shape — default to empty
|
||||
chevauxDeadHeat: [],
|
||||
// Financial fields may not be present in new API; default to 0
|
||||
totalMises: (apiResultat as any).totalMises ?? 0,
|
||||
masseAPartager: (apiResultat as any).masseAPartager ?? 0,
|
||||
prelevementsLegaux: (apiResultat as any).prelevementsLegaux ?? 0,
|
||||
montantRembourse: (apiResultat as any).montantRembourse ?? 0,
|
||||
montantCagnotte: (apiResultat as any).montantCagnotte ?? 0,
|
||||
adeadHeat: (apiResultat as any).adeadHeat ?? false,
|
||||
createdAt: apiResultat.createdAt,
|
||||
updatedAt: apiResultat.updatedAt,
|
||||
};
|
||||
|
||||
@@ -180,7 +180,7 @@ export class ReunionService {
|
||||
meta: { total: 0, uniqueHippodromes: 0, upcomingReunions: 0, pastReunions: 0 },
|
||||
},
|
||||
params.page,
|
||||
params.perPage
|
||||
params.size
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -197,7 +197,7 @@ export class ReunionService {
|
||||
meta: { total: 0, uniqueHippodromes: 0, upcomingReunions: 0, pastReunions: 0 },
|
||||
},
|
||||
params.page,
|
||||
params.perPage
|
||||
params.size
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -273,8 +273,8 @@ export class ReunionService {
|
||||
// Apply client-side filtering, sorting, and pagination
|
||||
let filtered = this.applyClientFilters(transformedData, params);
|
||||
const total = filtered.length;
|
||||
const start = (params.page - 1) * params.perPage;
|
||||
const pageData = filtered.slice(start, start + params.perPage);
|
||||
const start = (params.page - 1) * params.size;
|
||||
const pageData = filtered.slice(start, start + params.size);
|
||||
|
||||
const upcomingReunions = filtered.filter(
|
||||
(r) => new Date(r.date) >= new Date()
|
||||
@@ -288,7 +288,7 @@ export class ReunionService {
|
||||
meta: { total, uniqueHippodromes, upcomingReunions, pastReunions },
|
||||
},
|
||||
params.page,
|
||||
params.perPage
|
||||
params.size
|
||||
);
|
||||
})
|
||||
);
|
||||
@@ -359,8 +359,8 @@ export class ReunionService {
|
||||
});
|
||||
}
|
||||
|
||||
const start = (params.page - 1) * params.perPage;
|
||||
const pageData = data.slice(start, start + params.perPage);
|
||||
const start = (params.page - 1) * params.size;
|
||||
const pageData = data.slice(start, start + params.size);
|
||||
|
||||
const upcomingReunions = data.filter((r) => new Date(r.date) >= new Date()).length;
|
||||
const pastReunions = data.filter((r) => new Date(r.date) < new Date()).length;
|
||||
@@ -373,7 +373,7 @@ export class ReunionService {
|
||||
meta: { total: data.length, uniqueHippodromes, upcomingReunions, pastReunions },
|
||||
},
|
||||
params.page,
|
||||
params.perPage
|
||||
params.size
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export class RoleService {
|
||||
private buildParams(params: ListParams): HttpParams {
|
||||
let httpParams = new HttpParams()
|
||||
.set('page', String(params.page - 1))
|
||||
.set('size', String(params.perPage));
|
||||
.set('size', String(params.size));
|
||||
if (params.search) {
|
||||
httpParams = httpParams.set('search', params.search);
|
||||
}
|
||||
@@ -117,13 +117,13 @@ export class RoleService {
|
||||
return normalizePage<Role>(
|
||||
{ data: roles, meta: { total: roles.length } },
|
||||
params.page,
|
||||
params.perPage
|
||||
params.size
|
||||
);
|
||||
}),
|
||||
catchError((err) => {
|
||||
console.error('Error fetching roles:', err);
|
||||
return of(
|
||||
normalizePage<Role>({ data: [], meta: { total: 0 } }, params.page, params.perPage)
|
||||
normalizePage<Role>({ data: [], meta: { total: 0 } }, params.page, params.size)
|
||||
);
|
||||
})
|
||||
);
|
||||
@@ -137,7 +137,7 @@ export class RoleService {
|
||||
meta: { total: 0 },
|
||||
},
|
||||
params.page,
|
||||
params.perPage
|
||||
params.size
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export class ServicesUtils{
|
||||
let httpParams = new HttpParams();
|
||||
Object.entries(params).forEach(([key, value])=>{
|
||||
if(params != null && params!=undefined){
|
||||
httpParams.set(key, String(value))
|
||||
httpParams = httpParams.set(key, String(value))
|
||||
}
|
||||
})
|
||||
return httpParams;
|
||||
|
||||
@@ -211,7 +211,7 @@ export class TpeService {
|
||||
let httpParams = new HttpParams();
|
||||
if (params) {
|
||||
if (params.page) httpParams = httpParams.set('page', params.page.toString());
|
||||
if (params.perPage) httpParams = httpParams.set('perPage', params.perPage.toString());
|
||||
if (params.size) httpParams = httpParams.set('perPage', params.size.toString());
|
||||
if (params.search) httpParams = httpParams.set('search', params.search);
|
||||
if (params.sortKey) httpParams = httpParams.set('sortKey', params.sortKey);
|
||||
if (params.sortDir) httpParams = httpParams.set('sortDir', params.sortDir);
|
||||
@@ -230,7 +230,7 @@ export class TpeService {
|
||||
return normalizePage<TpeDevice>(
|
||||
{ data: tpes, meta: { total: tpes.length } },
|
||||
params.page || 1,
|
||||
params.perPage || 10
|
||||
params.size || 10
|
||||
);
|
||||
}
|
||||
// Otherwise return all as single page
|
||||
|
||||
@@ -117,23 +117,23 @@ export class UserService {
|
||||
});
|
||||
}
|
||||
|
||||
const start = (params.page - 1) * params.perPage;
|
||||
const pageData = data.slice(start, start + params.perPage);
|
||||
const start = (params.page - 1) * params.size;
|
||||
const pageData = data.slice(start, start + params.size);
|
||||
|
||||
return normalizePage<User>(
|
||||
{ data: pageData, meta: { total: data.length } },
|
||||
params.page,
|
||||
params.perPage
|
||||
params.size
|
||||
);
|
||||
}),
|
||||
catchError(() =>
|
||||
of(normalizePage<User>({ data: [], meta: { total: 0 } }, params.page, params.perPage))
|
||||
of(normalizePage<User>({ data: [], meta: { total: 0 } }, params.page, params.size))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback should not be used anymore
|
||||
return of(normalizePage<User>({ data: [], meta: { total: 0 } }, params.page, params.perPage));
|
||||
return of(normalizePage<User>({ data: [], meta: { total: 0 } }, params.page, params.size));
|
||||
}
|
||||
|
||||
create(payload: Omit<User, 'id'>): Observable<User> {
|
||||
|
||||
Reference in New Issue
Block a user