diff --git a/client/src/app/components/ps-product-crud/ps-product-crud.component.html b/client/src/app/components/ps-product-crud/ps-product-crud.component.html
index c1ebf79..d1606f6 100644
--- a/client/src/app/components/ps-product-crud/ps-product-crud.component.html
+++ b/client/src/app/components/ps-product-crud/ps-product-crud.component.html
@@ -7,6 +7,16 @@
add Nouveau produit
+ @if (selection.hasValue()) {
+
+ }
+
Filtrer
-
-
-
-
-
+ @if (isLoading) {
+
+
+
+ }
+
+
+ |
+
+
+ |
+
+
+
+ |
+
+
+
+
ID |
{{ el.id }} |
diff --git a/client/src/app/components/ps-product-crud/ps-product-crud.component.ts b/client/src/app/components/ps-product-crud/ps-product-crud.component.ts
index c435b38..9e16e99 100644
--- a/client/src/app/components/ps-product-crud/ps-product-crud.component.ts
+++ b/client/src/app/components/ps-product-crud/ps-product-crud.component.ts
@@ -11,10 +11,12 @@ 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 {FormBuilder, ReactiveFormsModule, FormsModule} from '@angular/forms';
import {MatDialog, MatDialogModule} from '@angular/material/dialog';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {forkJoin, finalize} from 'rxjs';
+import {SelectionModel} from '@angular/cdk/collections';
+import {MatCheckboxModule} from '@angular/material/checkbox';
import {PsItem} from '../../interfaces/ps-item';
import {ProductListItem} from '../../interfaces/product-list-item';
@@ -27,14 +29,15 @@ import {ProductDialogData, PsProductDialogComponent} from '../ps-product-dialog/
templateUrl: './ps-product-crud.component.html',
styleUrls: ['./ps-product-crud.component.css'],
imports: [
- CommonModule, ReactiveFormsModule,
+ CommonModule, ReactiveFormsModule, FormsModule,
MatTable, MatColumnDef, MatHeaderRow, MatHeaderRowDef, MatRow, MatRowDef,
MatHeaderCell, MatHeaderCellDef, MatCell, MatCellDef, MatNoDataRow,
MatSortModule, MatPaginatorModule,
MatFormField, MatLabel, MatInput,
MatButton, MatIconButton, MatIcon,
MatDialogModule,
- MatProgressSpinnerModule
+ MatProgressSpinnerModule,
+ MatCheckboxModule
]
})
export class PsProductCrudComponent implements OnInit {
@@ -50,12 +53,16 @@ export class PsProductCrudComponent implements OnInit {
private manMap = new Map();
private supMap = new Map();
- displayed: string[] = ['id', 'name', 'category', 'manufacturer', 'supplier', 'priceTtc', 'quantity', 'actions'];
+ // added 'select' column first
+ displayed: string[] = ['select', 'id', 'name', 'category', 'manufacturer', 'supplier', 'priceTtc', 'quantity', 'actions'];
dataSource = new MatTableDataSource([]);
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
@ViewChild(MatTable) table!: MatTable;
+ // selection model (multiple)
+ selection = new SelectionModel(true, []);
+
filterCtrl = this.fb.control('');
isLoading = false;
@@ -121,6 +128,8 @@ export class PsProductCrudComponent implements OnInit {
private bindProducts(p: (ProductListItem & { priceHt?: number })[]) {
const vat = 0.2;
+ // clear selection because objects will be new after reload
+ this.selection.clear();
this.dataSource.data = p.map(x => ({
...x,
categoryName: x.id_category_default ? (this.catMap.get(x.id_category_default) ?? '') : '',
@@ -203,4 +212,55 @@ export class PsProductCrudComponent implements OnInit {
}
});
}
+
+ // --- Selection helpers ---
+
+ private getVisibleRows(): any[] {
+ const data = this.dataSource.filteredData || [];
+ if (!this.paginator) return data;
+ const start = this.paginator.pageIndex * this.paginator.pageSize;
+ return data.slice(start, start + this.paginator.pageSize);
+ }
+
+ isAllSelected(): boolean {
+ const visible = this.getVisibleRows();
+ return visible.length > 0 && visible.every(r => this.selection.isSelected(r));
+ }
+
+ isAnySelected(): boolean {
+ return this.selection.hasValue();
+ }
+
+ masterToggle(checked: boolean) {
+ const visible = this.getVisibleRows();
+ if (checked) {
+ visible.forEach(r => this.selection.select(r));
+ } else {
+ visible.forEach(r => this.selection.deselect(r));
+ }
+ }
+
+ toggleSelection(row: any, checked: boolean) {
+ if (checked) this.selection.select(row);
+ else this.selection.deselect(row);
+ }
+
+ deleteSelected() {
+ if (this.isLoading) return;
+ const ids = this.selection.selected.map(s => s.id);
+ if (!ids.length) return;
+ if (!confirm(`Supprimer ${ids.length} produit(s) sélectionné(s) ?`)) return;
+
+ this.isLoading = true;
+ const calls = ids.map((id: number) => this.ps.deleteProduct(id));
+ forkJoin(calls).pipe(finalize(() => {
+ // nothing extra, reload will clear selection
+ })).subscribe({
+ next: () => this.reload(),
+ error: (e: unknown) => {
+ this.isLoading = false;
+ alert('Erreur: ' + (e instanceof Error ? e.message : String(e)));
+ }
+ });
+ }
}