import {Component, computed, inject, OnInit, output, signal, viewChild} from '@angular/core'; import {NzTableComponent, NzThMeasureDirective} from "ng-zorro-antd/table"; import {ModalNav} from "../modal-nav/modal-nav"; import {NzIconDirective} from "ng-zorro-antd/icon"; import {StockForm} from "../stock-form/stock-form"; import {NzDividerComponent} from "ng-zorro-antd/divider"; import {FormsModule} from "@angular/forms"; import {NzCheckboxComponent} from "ng-zorro-antd/checkbox"; import {GetProductDto, ProductsService, WarehouseproductsService} from "../../services/api"; import {NzNotificationService} from "ng-zorro-antd/notification"; import {firstValueFrom} from "rxjs"; interface ProductWithQuantity extends GetProductDto { totalQuantity?: number; } @Component({ selector: 'app-stock-table', imports: [ NzTableComponent, ModalNav, NzIconDirective, StockForm, NzDividerComponent, FormsModule, NzCheckboxComponent, NzThMeasureDirective, ], templateUrl: './stock-table.html', styleUrl: './stock-table.css', }) export class StockTable implements OnInit { private productsService = inject(ProductsService); private wareHousseProductsService = inject(WarehouseproductsService) private notificationService = inject(NzNotificationService) products = signal([]); productsLoading = signal(false); updateProduct = viewChild.required('stockForm'); modal = viewChild.required('modalNav'); checked = false; indeterminate = false; setOfCheckedId = new Set(); selectionChange = output() currentPageData: GetProductDto[] = []; private searchQuery = signal(''); filteredProducts = computed(() => { const q = this.searchQuery().toLowerCase().trim(); if (!q) return this.products(); return this.products().filter(s => { const name = (s.name ?? '').toLowerCase(); return name.includes(q); }); }); applySearch(query: string) { this.searchQuery.set(query); } get hasSelection(): boolean { return this.setOfCheckedId.size > 0; } updateCheckedSet(id: number, checked: boolean): void { if (checked) this.setOfCheckedId.add(id); else this.setOfCheckedId.delete(id); } onItemChecked(id: number, checked: boolean): void { this.updateCheckedSet(id, checked); this.refreshCheckedStatus(); } onAllChecked(checked: boolean): void { this.currentPageData.forEach(item => this.updateCheckedSet(item.id, checked) ); this.refreshCheckedStatus(); } onCurrentPageDataChange($event: GetProductDto[]): void { this.currentPageData = $event; this.refreshCheckedStatus(); } refreshCheckedStatus(): void { const total = this.currentPageData.length; const checkedCount = this.currentPageData.filter(p => this.setOfCheckedId.has(p.id)).length; this.checked = checkedCount === total; this.indeterminate = checkedCount > 0 && checkedCount < total; setTimeout(() => this.selectionChange.emit(this.hasSelection)); } get selectedIds() { return Array.from(this.setOfCheckedId); } async ngOnInit() { await this.fetchProducts(); } async fetchTotalQuantity(product: ProductWithQuantity) { try { const res = await firstValueFrom( this.wareHousseProductsService.getTotalQuantityEndpoint(product.id) ); product.totalQuantity = res.totalQuantity; } catch (e) { product.totalQuantity = 0; } } async fetchProducts() { this.productsLoading.set(true); try { const products = await firstValueFrom(this.productsService.getAllProductsEndpoint()); // transforme chaque produit en ProductWithQuantity const productsWithQuantity: ProductWithQuantity[] = products.map(p => ({ ...p })); this.products.set(productsWithQuantity); // récupérer la quantité pour chaque produit en parallèle await Promise.all(productsWithQuantity.map(p => this.fetchTotalQuantity(p))); // déclencher la mise à jour du signal this.products.set([...productsWithQuantity]); } catch (e) { this.notificationService.error( 'Erreur', 'Erreur de communication avec l\'API' ); } this.productsLoading.set(false); } async delete(productId:number) { try { await firstValueFrom(this.productsService.deleteProductEndpoint(productId)) this.notificationService.success( 'Success', 'Suppression effectuée' ) } catch (e) { this.notificationService.error( 'Erreur', 'Impossible de supprimer la ligne' ) } await this.fetchProducts(); } async edit(id: number, updateProductComponent: StockForm) { if (updateProductComponent.stockForm.invalid) { this.notificationService.error( 'Erreur', 'Erreur d\'écriture dans le formulaire' ) return; } try { const products = updateProductComponent.stockForm.getRawValue(); await firstValueFrom(this.productsService.patchProductMinimalStockEndpoint(id, products)) this.notificationService.success( 'Success', 'Limite de stock modifiée' ) } catch (e) { console.error(e); this.notificationService.error( 'Erreur', 'Erreur lors de la modification' ) } } selectedProduct: GetProductDto | null = null; openEditModal(product: GetProductDto) { this.selectedProduct = { ...product}; this.modal().showModal(); } async onModalOk(productId: number, updateProductComponent: StockForm, modal: ModalNav) { if (!this.selectedProduct) return; await this.edit(productId, updateProductComponent); updateProductComponent.stockForm.reset(); modal.isVisible = false; await this.fetchProducts(); } onModalCancel(modal: ModalNav) { modal.isVisible = false; } }