fix: product delete (single+bulk) had zero confirmation
(delete) and (bulkDelete) called facade.deleteOne()/applyBulkDelete() straight from the click event. Added app-confirm-dialog gates for both, same pattern as categories. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,15 @@
|
|||||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { AdminProductsFacade } from '../facade/admin-products.facade';
|
import { AdminProductsFacade } from '../facade/admin-products.facade';
|
||||||
import { AdminProductsListComponent } from '../components/admin-products-list.component';
|
import { AdminProductsListComponent } from '../components/admin-products-list.component';
|
||||||
import { LanguageService } from '../../../../services/language.service';
|
import { LanguageService } from '../../../../services/language.service';
|
||||||
|
import { ConfirmDialogComponent } from '../../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||||
|
import { TranslatePipe } from '../../../../i18n/translate.pipe';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-admin-products-list-page',
|
selector: 'app-admin-products-list-page',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [AdminProductsListComponent],
|
imports: [AdminProductsListComponent, ConfirmDialogComponent, TranslatePipe],
|
||||||
template: `<app-admin-products-list
|
template: `<app-admin-products-list
|
||||||
[products]="facade.products()"
|
[products]="facade.products()"
|
||||||
[categories]="facade.categories()"
|
[categories]="facade.categories()"
|
||||||
@@ -25,7 +27,7 @@ import { LanguageService } from '../../../../services/language.service';
|
|||||||
(create)="create()"
|
(create)="create()"
|
||||||
(edit)="edit($event)"
|
(edit)="edit($event)"
|
||||||
(duplicate)="duplicate($event)"
|
(duplicate)="duplicate($event)"
|
||||||
(delete)="facade.deleteOne($event)"
|
(delete)="deleteOne($event)"
|
||||||
(archive)="facade.archiveOne($event)"
|
(archive)="facade.archiveOne($event)"
|
||||||
(restore)="facade.restoreOne($event)"
|
(restore)="facade.restoreOne($event)"
|
||||||
(loadMore)="facade.loadMore()"
|
(loadMore)="facade.loadMore()"
|
||||||
@@ -33,14 +35,28 @@ import { LanguageService } from '../../../../services/language.service';
|
|||||||
(selectionChange)="facade.toggleSelection($event.id, $event.checked)"
|
(selectionChange)="facade.toggleSelection($event.id, $event.checked)"
|
||||||
(selectAll)="facade.toggleAll($event)"
|
(selectAll)="facade.toggleAll($event)"
|
||||||
(bulkVisibility)="facade.applyBulkVisibility($event)"
|
(bulkVisibility)="facade.applyBulkVisibility($event)"
|
||||||
(bulkDelete)="facade.applyBulkDelete()"
|
(bulkDelete)="bulkDelete()"
|
||||||
(bulkDuplicate)="facade.applyBulkDuplicate()"
|
(bulkDuplicate)="facade.applyBulkDuplicate()"
|
||||||
(bulkAssignCategory)="facade.applyBulkAssignCategory($event)"
|
(bulkAssignCategory)="facade.applyBulkAssignCategory($event)"
|
||||||
(bulkAssignTags)="facade.applyBulkAssignTags($event)"
|
(bulkAssignTags)="facade.applyBulkAssignTags($event)"
|
||||||
(bulkExport)="facade.exportSelectedAsCsv()"
|
(bulkExport)="facade.exportSelectedAsCsv()"
|
||||||
(viewModeChange)="facade.setViewMode($event)"
|
(viewModeChange)="facade.setViewMode($event)"
|
||||||
(densityChange)="facade.setDensity($event)"
|
(densityChange)="facade.setDensity($event)"
|
||||||
(columnToggle)="facade.setColumnVisible($event.column, $event.visible)" />`,
|
(columnToggle)="facade.setColumnVisible($event.column, $event.visible)" />
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="!!pendingDeleteId()"
|
||||||
|
[titleText]="'adminProducts.deleteTitle' | translate"
|
||||||
|
[message]="'adminProducts.confirmDelete' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmDelete()"
|
||||||
|
(cancelled)="pendingDeleteId.set(null)" />
|
||||||
|
<app-confirm-dialog
|
||||||
|
[open]="bulkDeleteConfirmOpen()"
|
||||||
|
[titleText]="'adminProducts.deleteTitle' | translate"
|
||||||
|
[message]="'adminProducts.confirmBulkDelete' | translate"
|
||||||
|
[destructive]="true"
|
||||||
|
(confirmed)="confirmBulkDelete()"
|
||||||
|
(cancelled)="bulkDeleteConfirmOpen.set(false)" />`,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class AdminProductsListPageComponent {
|
export class AdminProductsListPageComponent {
|
||||||
@@ -50,6 +66,9 @@ export class AdminProductsListPageComponent {
|
|||||||
|
|
||||||
readonly healthFn = (product: Parameters<AdminProductsFacade['health']>[0]) => this.facade.health(product);
|
readonly healthFn = (product: Parameters<AdminProductsFacade['health']>[0]) => this.facade.health(product);
|
||||||
|
|
||||||
|
readonly pendingDeleteId = signal<string | null>(null);
|
||||||
|
readonly bulkDeleteConfirmOpen = signal(false);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.facade.loadCategories();
|
this.facade.loadCategories();
|
||||||
this.facade.loadList();
|
this.facade.loadList();
|
||||||
@@ -60,6 +79,27 @@ export class AdminProductsListPageComponent {
|
|||||||
edit(id: string): void { this.facade.loadForEdit(id, 'edit'); void this.router.navigate([this.lang(), 'backoffice', 'products', id, 'edit']); }
|
edit(id: string): void { this.facade.loadForEdit(id, 'edit'); void this.router.navigate([this.lang(), 'backoffice', 'products', id, 'edit']); }
|
||||||
duplicate(id: string): void { this.facade.loadForEdit(id, 'duplicate'); void this.router.navigate([this.lang(), 'backoffice', 'products', id, 'duplicate']); }
|
duplicate(id: string): void { this.facade.loadForEdit(id, 'duplicate'); void this.router.navigate([this.lang(), 'backoffice', 'products', id, 'duplicate']); }
|
||||||
|
|
||||||
|
deleteOne(id: string): void {
|
||||||
|
this.pendingDeleteId.set(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmDelete(): void {
|
||||||
|
const id = this.pendingDeleteId();
|
||||||
|
if (id) {
|
||||||
|
this.facade.deleteOne(id);
|
||||||
|
}
|
||||||
|
this.pendingDeleteId.set(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
bulkDelete(): void {
|
||||||
|
this.bulkDeleteConfirmOpen.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmBulkDelete(): void {
|
||||||
|
this.facade.applyBulkDelete();
|
||||||
|
this.bulkDeleteConfirmOpen.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
private lang(): string {
|
private lang(): string {
|
||||||
return this.languageService.currentLanguage();
|
return this.languageService.currentLanguage();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1617,6 +1617,9 @@ export const en: Translations = {
|
|||||||
},
|
},
|
||||||
adminProducts: {
|
adminProducts: {
|
||||||
confirmLeaveUnsaved: 'You have unsaved changes. Leave without saving?',
|
confirmLeaveUnsaved: 'You have unsaved changes. Leave without saving?',
|
||||||
|
deleteTitle: 'Delete product',
|
||||||
|
confirmDelete: 'Delete this product? This cannot be undone.',
|
||||||
|
confirmBulkDelete: 'Delete the selected products? This cannot be undone.',
|
||||||
emptyTitle: 'No products found',
|
emptyTitle: 'No products found',
|
||||||
emptyDescription: 'Try adjusting your filters, or create a new product.',
|
emptyDescription: 'Try adjusting your filters, or create a new product.',
|
||||||
emptyGuide: 'Good products have a clear title, at least one photo, a price, and a short description — that\'s enough to publish. You can always add more detail later.',
|
emptyGuide: 'Good products have a clear title, at least one photo, a price, and a short description — that\'s enough to publish. You can always add more detail later.',
|
||||||
|
|||||||
@@ -1612,6 +1612,9 @@ export const hy: Translations = {
|
|||||||
},
|
},
|
||||||
adminProducts: {
|
adminProducts: {
|
||||||
confirmLeaveUnsaved: 'Դուք ունեք չպահպանված փոփոխություններ։ Դո՞ւրս գալ առանց պահպանելու։',
|
confirmLeaveUnsaved: 'Դուք ունեք չպահպանված փոփոխություններ։ Դո՞ւրս գալ առանց պահպանելու։',
|
||||||
|
deleteTitle: 'Ջնջել ապրանքը',
|
||||||
|
confirmDelete: 'Ջնջե՞լ այս ապրանքը։ Հնարավոր չէ հետարկել։',
|
||||||
|
confirmBulkDelete: 'Ջնջե՞լ ընտրված ապրանքները։ Հնարավոր չէ հետարկել։',
|
||||||
emptyTitle: 'Ապրանքներ չեն գտնվել',
|
emptyTitle: 'Ապրանքներ չեն գտնվել',
|
||||||
emptyDescription: 'Փոխեք ֆիլտրերը կամ ստեղծեք նոր ապրանք։',
|
emptyDescription: 'Փոխեք ֆիլտրերը կամ ստեղծեք նոր ապրանք։',
|
||||||
emptyGuide: 'Լավ ապրանքին բավական է հստակ վերնագիր, առնվազն մեկ լուսանկար, գին և կարճ նկարագրություն՝ հրապարակելու համար։ Մնացածը կարող եք ավելացնել հետո։',
|
emptyGuide: 'Լավ ապրանքին բավական է հստակ վերնագիր, առնվազն մեկ լուսանկար, գին և կարճ նկարագրություն՝ հրապարակելու համար։ Մնացածը կարող եք ավելացնել հետո։',
|
||||||
|
|||||||
@@ -1612,6 +1612,9 @@ export const ru: Translations = {
|
|||||||
},
|
},
|
||||||
adminProducts: {
|
adminProducts: {
|
||||||
confirmLeaveUnsaved: 'У вас есть несохранённые изменения. Выйти без сохранения?',
|
confirmLeaveUnsaved: 'У вас есть несохранённые изменения. Выйти без сохранения?',
|
||||||
|
deleteTitle: 'Удалить товар',
|
||||||
|
confirmDelete: 'Удалить этот товар? Это действие нельзя отменить.',
|
||||||
|
confirmBulkDelete: 'Удалить выбранные товары? Это действие нельзя отменить.',
|
||||||
emptyTitle: 'Товары не найдены',
|
emptyTitle: 'Товары не найдены',
|
||||||
emptyDescription: 'Измените фильтры или создайте новый товар.',
|
emptyDescription: 'Измените фильтры или создайте новый товар.',
|
||||||
emptyGuide: 'Хорошему товару нужны понятное название, минимум одно фото, цена и короткое описание — этого достаточно для публикации. Остальное можно добавить позже.',
|
emptyGuide: 'Хорошему товару нужны понятное название, минимум одно фото, цена и короткое описание — этого достаточно для публикации. Остальное можно добавить позже.',
|
||||||
|
|||||||
@@ -1625,6 +1625,9 @@ export interface Translations {
|
|||||||
};
|
};
|
||||||
adminProducts: {
|
adminProducts: {
|
||||||
confirmLeaveUnsaved: string;
|
confirmLeaveUnsaved: string;
|
||||||
|
deleteTitle: string;
|
||||||
|
confirmDelete: string;
|
||||||
|
confirmBulkDelete: string;
|
||||||
emptyTitle: string;
|
emptyTitle: string;
|
||||||
emptyDescription: string;
|
emptyDescription: string;
|
||||||
emptyGuide: string;
|
emptyGuide: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user