Add loading indicators to product CRUD and dialog components

This commit is contained in:
Vincent Guillet
2025-12-03 21:46:18 +01:00
parent 1a5d3a570a
commit 00f45ae6c7
10 changed files with 323 additions and 179 deletions

View File

@@ -13,7 +13,8 @@ import {MatButton, MatIconButton} from '@angular/material/button';
import {MatIcon} from '@angular/material/icon';
import {FormBuilder, ReactiveFormsModule} from '@angular/forms';
import {MatDialog, MatDialogModule} from '@angular/material/dialog';
import {forkJoin} from 'rxjs';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {forkJoin, finalize} from 'rxjs';
import {PsItem} from '../../interfaces/ps-item';
import {ProductListItem} from '../../interfaces/product-list-item';
@@ -32,7 +33,8 @@ import {ProductDialogData, PsProductDialogComponent} from '../ps-product-dialog/
MatSortModule, MatPaginatorModule,
MatFormField, MatLabel, MatInput,
MatButton, MatIconButton, MatIcon,
MatDialogModule
MatDialogModule,
MatProgressSpinnerModule
]
})
export class PsProductCrudComponent implements OnInit {
@@ -40,26 +42,24 @@ export class PsProductCrudComponent implements OnInit {
private readonly ps = inject(PrestashopService);
private readonly dialog = inject(MatDialog);
// référentiels
categories: PsItem[] = [];
manufacturers: PsItem[] = [];
suppliers: PsItem[] = [];
// maps daffichage
private catMap = new Map<number, string>();
private manMap = new Map<number, string>();
private supMap = new Map<number, string>();
// table
displayed: string[] = ['id', 'name', 'category', 'manufacturer', 'supplier', 'priceTtc', 'quantity', 'actions'];
dataSource = new MatTableDataSource<any>([]);
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
@ViewChild(MatTable) table!: MatTable<any>;
// filtre
filterCtrl = this.fb.control<string>('');
isLoading = false;
ngOnInit(): void {
forkJoin({
cats: this.ps.list('categories'),
@@ -73,6 +73,7 @@ export class PsProductCrudComponent implements OnInit {
this.manMap = new Map(this.manufacturers.map(x => [x.id, x.name]));
this.suppliers = sups ?? [];
this.supMap = new Map(this.suppliers.map(x => [x.id, x.name]));
this.reload();
},
error: err => {
@@ -80,7 +81,6 @@ export class PsProductCrudComponent implements OnInit {
}
});
// filtre client
this.filterCtrl.valueChanges.subscribe(v => {
this.dataSource.filter = (v ?? '').toString().trim().toLowerCase();
if (this.paginator) this.paginator.firstPage();
@@ -133,10 +133,24 @@ export class PsProductCrudComponent implements OnInit {
}
reload() {
this.ps.listProducts().subscribe(p => this.bindProducts(p));
this.isLoading = true;
this.ps.listProducts()
.pipe(
finalize(() => {
this.isLoading = false;
})
)
.subscribe({
next: p => this.bindProducts(p),
error: err => {
console.error('Erreur lors du chargement des produits', err);
}
});
}
create() {
if (this.isLoading) return;
const data: ProductDialogData = {
mode: 'create',
refs: {
@@ -145,12 +159,16 @@ export class PsProductCrudComponent implements OnInit {
suppliers: this.suppliers
}
};
this.dialog.open(PsProductDialogComponent, {width: '900px', data}).afterClosed().subscribe(ok => {
if (ok) this.reload();
});
this.dialog.open(PsProductDialogComponent, {width: '900px', data})
.afterClosed()
.subscribe(ok => {
if (ok) this.reload();
});
}
edit(row: ProductListItem & { priceHt?: number }) {
if (this.isLoading) return;
const data: ProductDialogData = {
mode: 'edit',
productRow: row,
@@ -160,16 +178,29 @@ export class PsProductCrudComponent implements OnInit {
suppliers: this.suppliers
}
};
this.dialog.open(PsProductDialogComponent, {width: '900px', data}).afterClosed().subscribe(ok => {
if (ok) this.reload();
});
this.dialog.open(PsProductDialogComponent, {width: '900px', data})
.afterClosed()
.subscribe(ok => {
if (ok) this.reload();
});
}
remove(row: ProductListItem) {
if (this.isLoading) return;
if (!confirm(`Supprimer le produit "${row.name}" (#${row.id}) ?`)) return;
this.ps.deleteProduct(row.id).subscribe({
next: () => this.reload(),
error: (e: unknown) => alert('Erreur: ' + (e instanceof Error ? e.message : String(e)))
});
this.isLoading = true;
this.ps.deleteProduct(row.id)
.pipe(
finalize(() => {
})
)
.subscribe({
next: () => this.reload(),
error: (e: unknown) => {
this.isLoading = false;
alert('Erreur: ' + (e instanceof Error ? e.message : String(e)));
}
});
}
}