feat: implement image carousel in product dialog; enhance image upload handling and preview functionality

This commit is contained in:
Vincent Guillet
2025-11-18 16:32:29 +01:00
parent d4ffcf0562
commit b756c9fa2d
7 changed files with 302 additions and 58 deletions

View File

@@ -37,6 +37,7 @@ const UPDATE_CFG: Record<Resource, {
export class PrestashopService {
private readonly http = inject(HttpClient);
private readonly base = '/ps';
private readonly frontBase = 'https://shop.gameovergne.fr'
// -------- Utils
private readonly headersXml = new HttpHeaders({
@@ -465,20 +466,34 @@ export class PrestashopService {
// -------- Images
private getProductImageIds(productId: number) {
return this.http.get<any>(`${this.base}/images/products/${productId}`, {
responseType: 'json' as any
}).pipe(
/** Retourne les URLs publiques des images du produit (FO Presta, pas lAPI) */
getProductImageUrls(productId: number) {
const params = new HttpParams()
.set('output_format', 'JSON')
.set('display', 'full');
return this.http.get<any>(`${this.base}/products/${productId}`, { params }).pipe(
map(r => {
const arr = (r?.image ?? r?.images ?? []) as Array<any>;
return Array.isArray(arr) ? arr.map(x => +x.id) : [];
// même logique que pour les autres méthodes : format 1 ou 2
const p = r?.product ?? (Array.isArray(r?.products) ? r.products[0] : r);
const rawAssoc = p?.associations ?? {};
const rawImages = rawAssoc?.images?.image ?? rawAssoc?.images ?? [];
const arr: any[] = Array.isArray(rawImages) ? rawImages : (rawImages ? [rawImages] : []);
const ids = arr
.map(img => +img.id)
.filter(id => Number.isFinite(id) && id > 0);
return ids.map(id => this.buildFrontImageUrl(id));
})
);
}
getProductImageUrls(productId: number) {
return this.getProductImageIds(productId).pipe(
map(ids => ids.map(idImg => `${this.base}/images/products/${productId}/${idImg}`))
/** Retourne la première URL d'image (miniature) du produit, ou null s'il n'y en a pas */
getProductThumbnailUrl(productId: number) {
return this.getProductImageUrls(productId).pipe(
map(urls => urls.length ? urls[0] : null),
catchError(() => of(null))
);
}
@@ -810,8 +825,6 @@ export class PrestashopService {
);
}
// -------- Helpers internes (produit complet pour update)
// -------- Helpers internes (produit complet pour update)
private getProductForUpdate(id: number) {
const params = new HttpParams().set('output_format', 'JSON').set('display', 'full');
@@ -890,8 +903,14 @@ export class PrestashopService {
);
}
// -------- Description (désormais: uniquement la description libre)
/** Construit lURL publique dune image produit Presta à partir de son id_image */
private buildFrontImageUrl(imageId: number): string {
const idStr = String(imageId);
const path = idStr.split('').join('/'); // "123" -> "1/2/3"
return `${this.frontBase}/img/p/${path}/${idStr}.jpg`;
}
// -------- Description (désormais: uniquement la description libre)
private buildAugmentedDescription(dto: PsProduct): string {
return dto.description?.trim() || '';
}
@@ -1040,11 +1059,20 @@ export class PrestashopService {
return this.http.put(`${this.base}/products/${id}`, xml, {
headers: this.headersXml,
responseType: 'text'
}).pipe(
switchMap(() => this.setProductQuantity(id, dto.quantity)),
map(() => true)
);
})
});
}),
switchMap(() => {
const ops: Array<Observable<unknown>> = [];
if (dto.images?.length) {
ops.push(forkJoin(dto.images.map(f => this.uploadProductImage(id, f))));
}
ops.push(this.setProductQuantity(id, dto.quantity));
return ops.length ? forkJoin(ops) : of(true);
}),
map(() => true)
);
}