Files
gameovergne-app/client/src/app/components/ps-product-crud/ps-product-crud.component.ts

207 lines
6.7 KiB
TypeScript

import {Component, inject, OnInit, ViewChild} from '@angular/core';
import {CommonModule} from '@angular/common';
import {
MatCell, MatCellDef, MatColumnDef, MatHeaderCell, MatHeaderCellDef,
MatHeaderRow, MatHeaderRowDef, MatRow, MatRowDef,
MatNoDataRow, MatTable, MatTableDataSource
} from '@angular/material/table';
import {MatPaginator, MatPaginatorModule} from '@angular/material/paginator';
import {MatSort, MatSortModule} from '@angular/material/sort';
import {MatFormField, MatLabel} from '@angular/material/form-field';
import {MatInput} from '@angular/material/input';
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 {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {forkJoin, finalize} from 'rxjs';
import {PsItem} from '../../interfaces/ps-item';
import {ProductListItem} from '../../interfaces/product-list-item';
import {PrestashopService} from '../../services/prestashop.serivce';
import {ProductDialogData, PsProductDialogComponent} from '../ps-product-dialog/ps-product-dialog.component';
@Component({
selector: 'app-ps-product-crud',
standalone: true,
templateUrl: './ps-product-crud.component.html',
styleUrls: ['./ps-product-crud.component.css'],
imports: [
CommonModule, ReactiveFormsModule,
MatTable, MatColumnDef, MatHeaderRow, MatHeaderRowDef, MatRow, MatRowDef,
MatHeaderCell, MatHeaderCellDef, MatCell, MatCellDef, MatNoDataRow,
MatSortModule, MatPaginatorModule,
MatFormField, MatLabel, MatInput,
MatButton, MatIconButton, MatIcon,
MatDialogModule,
MatProgressSpinnerModule
]
})
export class PsProductCrudComponent implements OnInit {
private readonly fb = inject(FormBuilder);
private readonly ps = inject(PrestashopService);
private readonly dialog = inject(MatDialog);
categories: PsItem[] = [];
manufacturers: PsItem[] = [];
suppliers: PsItem[] = [];
private catMap = new Map<number, string>();
private manMap = new Map<number, string>();
private supMap = new Map<number, string>();
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>;
filterCtrl = this.fb.control<string>('');
isLoading = false;
ngOnInit(): void {
forkJoin({
cats: this.ps.list('categories'),
mans: this.ps.list('manufacturers'),
sups: this.ps.list('suppliers')
}).subscribe({
next: ({cats, mans, sups}) => {
this.categories = cats ?? [];
this.catMap = new Map(this.categories.map(x => [x.id, x.name]));
this.manufacturers = mans ?? [];
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 => {
console.error('Erreur lors du chargement des référentiels', err);
}
});
this.filterCtrl.valueChanges.subscribe(v => {
this.dataSource.filter = (v ?? '').toString().trim().toLowerCase();
if (this.paginator) this.paginator.firstPage();
});
this.dataSource.filterPredicate = (row: any, f: string) =>
row.name?.toLowerCase().includes(f) ||
String(row.id).includes(f) ||
(row.categoryName?.toLowerCase().includes(f)) ||
(row.manufacturerName?.toLowerCase().includes(f)) ||
(row.supplierName?.toLowerCase().includes(f)) ||
String(row.quantity ?? '').includes(f);
}
private toTtc(ht: number, vat: number) {
return Math.round(((ht * (1 + vat)) + Number.EPSILON) * 100) / 100;
}
private attachSortingAccessors() {
this.dataSource.sortingDataAccessor = (item: any, property: string) => {
switch (property) {
case 'category':
return (item.categoryName ?? '').toLowerCase();
case 'manufacturer':
return (item.manufacturerName ?? '').toLowerCase();
case 'supplier':
return (item.supplierName ?? '').toLowerCase();
case 'priceTtc':
return Number(item.priceTtc ?? 0);
case 'name':
return (item.name ?? '').toLowerCase();
default:
return item[property];
}
};
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
private bindProducts(p: (ProductListItem & { priceHt?: number })[]) {
const vat = 0.2;
this.dataSource.data = p.map(x => ({
...x,
categoryName: x.id_category_default ? (this.catMap.get(x.id_category_default) ?? '') : '',
manufacturerName: x.id_manufacturer ? (this.manMap.get(x.id_manufacturer) ?? '') : '',
supplierName: x.id_supplier ? (this.supMap.get(x.id_supplier) ?? '') : '',
priceTtc: this.toTtc(x.priceHt ?? 0, vat)
}));
this.attachSortingAccessors();
this.table?.renderRows?.();
}
reload() {
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: {
categories: this.categories,
manufacturers: this.manufacturers,
suppliers: this.suppliers
}
};
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,
refs: {
categories: this.categories,
manufacturers: this.manufacturers,
suppliers: this.suppliers
}
};
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.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)));
}
});
}
}