fix: category bulk-delete had no confirmation, single-delete used native dialogs

Bulk-delete called facade.applyBulkDelete() straight from the button
with zero confirmation. Single-delete had a confirm gate but via raw
window.confirm/alert instead of the themed dialog used elsewhere.

Migrated both to app-confirm-dialog (single + new bulk), and the
delete-blocked message to a themed app-dialog instead of window.alert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-08-13 08:19:22 +04:00
parent 8a91a862ca
commit 320f1f44b7
5 changed files with 59 additions and 6 deletions

View File

@@ -1,14 +1,18 @@
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 { AdminCategoriesFacade } from '../facade/admin-categories.facade'; import { AdminCategoriesFacade } from '../facade/admin-categories.facade';
import { AdminCategoriesListComponent } from '../components/admin-categories-list.component'; import { AdminCategoriesListComponent } from '../components/admin-categories-list.component';
import { LanguageService } from '../../../../services/language.service'; import { LanguageService } from '../../../../services/language.service';
import { TranslateService } from '../../../../i18n/translate.service'; import { TranslateService } from '../../../../i18n/translate.service';
import { ConfirmDialogComponent } from '../../../../shared/ui/confirm-dialog/confirm-dialog.component';
import { DialogComponent } from '../../../../shared/ui/dialog/dialog.component';
import { ButtonComponent } from '../../../../shared/ui/button/button.component';
import { TranslatePipe } from '../../../../i18n/translate.pipe';
@Component({ @Component({
selector: 'app-admin-categories-list-page', selector: 'app-admin-categories-list-page',
standalone: true, standalone: true,
imports: [AdminCategoriesListComponent], imports: [AdminCategoriesListComponent, ConfirmDialogComponent, DialogComponent, ButtonComponent, TranslatePipe],
template: `<app-admin-categories-list template: `<app-admin-categories-list
[treeRows]="facade.visibleTreeRows()" [treeRows]="facade.visibleTreeRows()"
[flatCategories]="facade.filteredCategories()" [flatCategories]="facade.filteredCategories()"
@@ -37,14 +41,36 @@ import { TranslateService } from '../../../../i18n/translate.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()"
(bulkAssignParent)="facade.applyBulkAssignParent($event)" (bulkAssignParent)="facade.applyBulkAssignParent($event)"
(bulkAssignImage)="facade.applyBulkAssignImage($event)" (bulkAssignImage)="facade.applyBulkAssignImage($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]="'adminCategories.deleteTitle' | translate"
[message]="'adminCategories.confirmDelete' | translate"
[destructive]="true"
(confirmed)="confirmDelete()"
(cancelled)="pendingDeleteId.set(null)" />
<app-confirm-dialog
[open]="bulkDeleteConfirmOpen()"
[titleText]="'adminCategories.deleteTitle' | translate"
[message]="'adminCategories.confirmBulkDelete' | translate"
[destructive]="true"
(confirmed)="confirmBulkDelete()"
(cancelled)="bulkDeleteConfirmOpen.set(false)" />
@if (deleteBlockedMessage()) {
<app-dialog [open]="true" [titleText]="'adminCategories.deleteBlocked' | translate" size="sm" (closed)="deleteBlockedMessage.set(null)">
<p>{{ deleteBlockedMessage() }}</p>
<div class="app-confirm-dialog__actions">
<app-button variant="primary" (click)="deleteBlockedMessage.set(null)">{{ 'common.confirm' | translate }}</app-button>
</div>
</app-dialog>
}`,
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class AdminCategoriesListPageComponent { export class AdminCategoriesListPageComponent {
@@ -57,6 +83,10 @@ export class AdminCategoriesListPageComponent {
readonly isExpandedFn = (id: string) => this.facade.isExpanded(id); readonly isExpandedFn = (id: string) => this.facade.isExpanded(id);
readonly canDeleteFn = (id: string) => this.facade.canDelete(id); readonly canDeleteFn = (id: string) => this.facade.canDelete(id);
readonly pendingDeleteId = signal<string | null>(null);
readonly bulkDeleteConfirmOpen = signal(false);
readonly deleteBlockedMessage = signal<string | null>(null);
constructor() { constructor() {
this.facade.loadList(); this.facade.loadList();
this.facade.loadDashboardStats(); this.facade.loadDashboardStats();
@@ -75,12 +105,27 @@ export class AdminCategoriesListPageComponent {
deleteOne(id: string): void { deleteOne(id: string): void {
if (!this.facade.canDelete(id)) { if (!this.facade.canDelete(id)) {
window.alert(this.translate.t('adminCategories.deleteBlocked')); this.deleteBlockedMessage.set(this.translate.t('adminCategories.deleteBlocked'));
return; return;
} }
if (window.confirm(this.translate.t('adminCategories.confirmDelete'))) { this.pendingDeleteId.set(id);
}
confirmDelete(): void {
const id = this.pendingDeleteId();
if (id) {
this.facade.deleteOne(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 {

View File

@@ -1549,6 +1549,8 @@ export const en: Translations = {
saveDraft: 'Save draft', saveDraft: 'Save draft',
publish: 'Publish', publish: 'Publish',
confirmDelete: 'Delete this category? This cannot be undone.', confirmDelete: 'Delete this category? This cannot be undone.',
confirmBulkDelete: 'Delete the selected categories? This cannot be undone.',
deleteTitle: 'Delete category',
confirmLeaveUnsaved: 'You have unsaved changes. Leave without saving?', confirmLeaveUnsaved: 'You have unsaved changes. Leave without saving?',
deleteBlocked: 'This category can\'t be deleted while it has subcategories or assigned products.', deleteBlocked: 'This category can\'t be deleted while it has subcategories or assigned products.',
emptyTitle: 'No categories yet', emptyTitle: 'No categories yet',

View File

@@ -1544,6 +1544,8 @@ export const hy: Translations = {
saveDraft: 'Պահպանել սևագիրը', saveDraft: 'Պահպանել սևագիրը',
publish: 'Հրապարակել', publish: 'Հրապարակել',
confirmDelete: 'Ջնջե՞լ այս կատեգորիան։ Հնարավոր չէ հետարկել։', confirmDelete: 'Ջնջե՞լ այս կատեգորիան։ Հնարավոր չէ հետարկել։',
confirmBulkDelete: 'Ջնջե՞լ ընտրված կատեգորիաները։ Հնարավոր չէ հետարկել։',
deleteTitle: 'Ջնջել կատեգորիան',
confirmLeaveUnsaved: 'Դուք ունեք չպահպանված փոփոխություններ։ Դո՞ւրս գալ առանց պահպանելու։', confirmLeaveUnsaved: 'Դուք ունեք չպահպանված փոփոխություններ։ Դո՞ւրս գալ առանց պահպանելու։',
deleteBlocked: 'Այս կատեգորիան հնարավոր չէ ջնջել, քանի դեռ ունի ենթակատեգորիաներ կամ նշանակված ապրանքներ։', deleteBlocked: 'Այս կատեգորիան հնարավոր չէ ջնջել, քանի դեռ ունի ենթակատեգորիաներ կամ նշանակված ապրանքներ։',
emptyTitle: 'Կատեգորիաներ դեռ չկան', emptyTitle: 'Կատեգորիաներ դեռ չկան',

View File

@@ -1544,6 +1544,8 @@ export const ru: Translations = {
saveDraft: 'Сохранить черновик', saveDraft: 'Сохранить черновик',
publish: 'Опубликовать', publish: 'Опубликовать',
confirmDelete: 'Удалить эту категорию? Это действие нельзя отменить.', confirmDelete: 'Удалить эту категорию? Это действие нельзя отменить.',
confirmBulkDelete: 'Удалить выбранные категории? Это действие нельзя отменить.',
deleteTitle: 'Удалить категорию',
confirmLeaveUnsaved: 'У вас есть несохранённые изменения. Выйти без сохранения?', confirmLeaveUnsaved: 'У вас есть несохранённые изменения. Выйти без сохранения?',
deleteBlocked: 'Эту категорию нельзя удалить, пока у неё есть подкатегории или назначенные товары.', deleteBlocked: 'Эту категорию нельзя удалить, пока у неё есть подкатегории или назначенные товары.',
emptyTitle: 'Категорий пока нет', emptyTitle: 'Категорий пока нет',

View File

@@ -1557,6 +1557,8 @@ export interface Translations {
saveDraft: string; saveDraft: string;
publish: string; publish: string;
confirmDelete: string; confirmDelete: string;
confirmBulkDelete: string;
deleteTitle: string;
confirmLeaveUnsaved: string; confirmLeaveUnsaved: string;
deleteBlocked: string; deleteBlocked: string;
emptyTitle: string; emptyTitle: string;