fix(ui): replace native confirm()/alert() with shared dialogs and toasts
New app-confirm-dialog (wraps existing app-dialog + app-button) replaces every native confirm() across media library bulk-delete, static pages editor (delete/bulk-delete), builder save-bar (publish/reset-draft), project-editor-page (reset-section), homepage/languages/widgets sections (remove block/language/widget), and cart (clear-cart). Cart's native alert() calls (delivery/terms validation, email send success/failure) now route through the existing UserNotificationService toast pipeline instead. No native confirm()/alert()/prompt() remain in production UI.
This commit is contained in:
@@ -28,3 +28,20 @@
|
||||
<app-button variant="primary" size="sm" [disabled]="hasBlockingIssues()" (click)="publish()">{{ 'builder.publish' | translate }}</app-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-confirm-dialog
|
||||
[open]="publishConfirmOpen()"
|
||||
[titleText]="'builder.confirmPublish' | translate"
|
||||
[message]="'builder.confirmPublish' | translate"
|
||||
(confirmed)="confirmPublish()"
|
||||
(cancelled)="publishConfirmOpen.set(false)"
|
||||
/>
|
||||
|
||||
<app-confirm-dialog
|
||||
[open]="resetDraftConfirmOpen()"
|
||||
[titleText]="'builder.confirmResetDraft' | translate"
|
||||
[message]="'builder.confirmResetDraft' | translate"
|
||||
[destructive]="true"
|
||||
(confirmed)="confirmResetDraft()"
|
||||
(cancelled)="resetDraftConfirmOpen.set(false)"
|
||||
/>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';
|
||||
import { TranslatePipe } from '../../../../i18n/translate.pipe';
|
||||
import { TranslateService } from '../../../../i18n/translate.service';
|
||||
import { ProjectEditorFacade } from '../../facade/project-editor.facade';
|
||||
import { ButtonComponent } from '../../../../shared/ui/button/button.component';
|
||||
import { ConfirmDialogComponent } from '../../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-project-editor-save-bar',
|
||||
standalone: true,
|
||||
imports: [TranslatePipe, ButtonComponent],
|
||||
imports: [TranslatePipe, ButtonComponent, ConfirmDialogComponent],
|
||||
templateUrl: './project-editor-save-bar.component.html',
|
||||
styleUrls: ['./project-editor-save-bar.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
@@ -36,16 +37,25 @@ export class ProjectEditorSaveBarComponent {
|
||||
this.facade.save();
|
||||
}
|
||||
|
||||
readonly publishConfirmOpen = signal(false);
|
||||
readonly resetDraftConfirmOpen = signal(false);
|
||||
|
||||
publish(): void {
|
||||
if (confirm(this.translate.t('builder.confirmPublish'))) {
|
||||
this.facade.publish();
|
||||
}
|
||||
this.publishConfirmOpen.set(true);
|
||||
}
|
||||
|
||||
confirmPublish(): void {
|
||||
this.publishConfirmOpen.set(false);
|
||||
this.facade.publish();
|
||||
}
|
||||
|
||||
resetDraft(): void {
|
||||
if (confirm(this.translate.t('builder.confirmResetDraft'))) {
|
||||
this.facade.resetDraft();
|
||||
}
|
||||
this.resetDraftConfirmOpen.set(true);
|
||||
}
|
||||
|
||||
confirmResetDraft(): void {
|
||||
this.resetDraftConfirmOpen.set(false);
|
||||
this.facade.resetDraft();
|
||||
}
|
||||
|
||||
formatSavedAt(timestamp: number): string {
|
||||
|
||||
@@ -107,3 +107,12 @@
|
||||
}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<app-confirm-dialog
|
||||
[open]="resetSectionConfirmOpen()"
|
||||
[titleText]="'builder.confirmResetSection' | translate"
|
||||
[message]="'builder.confirmResetSection' | translate"
|
||||
[destructive]="true"
|
||||
(confirmed)="confirmResetActiveSection()"
|
||||
(cancelled)="resetSectionConfirmOpen.set(false)"
|
||||
/>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { ProjectEditorPreviewSectionComponent } from '../sections/preview-sectio
|
||||
import { TranslatePipe } from '../../../i18n/translate.pipe';
|
||||
import { TranslateService } from '../../../i18n/translate.service';
|
||||
import { LanguageService } from '../../../services/language.service';
|
||||
import { ConfirmDialogComponent } from '../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||
import { IconComponent } from '../../../shared/ui/icon/icon.component';
|
||||
import { ButtonComponent } from '../../../shared/ui/button/button.component';
|
||||
import { StaticPagesEditorComponent } from '../../content-management/components/static-pages-editor.component';
|
||||
@@ -66,6 +67,7 @@ const SECTION_LABEL_KEYS: Record<ProjectEditorSectionId, string> = {
|
||||
ProjectEditorSaveBarComponent,
|
||||
IconComponent,
|
||||
ButtonComponent,
|
||||
ConfirmDialogComponent,
|
||||
],
|
||||
templateUrl: './project-editor-page.component.html',
|
||||
styleUrls: ['./project-editor-page.component.scss'],
|
||||
@@ -114,10 +116,15 @@ export class ProjectEditorPageComponent {
|
||||
});
|
||||
}
|
||||
|
||||
readonly resetSectionConfirmOpen = signal(false);
|
||||
|
||||
resetActiveSection(): void {
|
||||
if (confirm(this.translate.t('builder.confirmResetSection'))) {
|
||||
this.facade.resetSection(this.activeSection());
|
||||
}
|
||||
this.resetSectionConfirmOpen.set(true);
|
||||
}
|
||||
|
||||
confirmResetActiveSection(): void {
|
||||
this.resetSectionConfirmOpen.set(false);
|
||||
this.facade.resetSection(this.activeSection());
|
||||
}
|
||||
|
||||
toggleHelp(): void {
|
||||
|
||||
@@ -82,4 +82,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</app-section-card>
|
||||
|
||||
<app-confirm-dialog
|
||||
[open]="!!pendingRemoveBlockId()"
|
||||
[titleText]="'builder.blockRemoveConfirm' | translate"
|
||||
[message]="'builder.blockRemoveConfirm' | translate"
|
||||
[destructive]="true"
|
||||
(confirmed)="confirmRemoveBlock()"
|
||||
(cancelled)="pendingRemoveBlockId.set(null)"
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CdkDragDrop, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ProjectEditorFacade } from '../facade/project-editor.facade';
|
||||
import { TranslatePipe } from '../../../i18n/translate.pipe';
|
||||
@@ -12,6 +12,7 @@ import { HomepageOverviewComponent } from './homepage/homepage-overview.componen
|
||||
import { SectionConfig } from '../../../shared/models/config';
|
||||
import { IconComponent } from '../../../shared/ui/icon/icon.component';
|
||||
import { AppIconName } from '../../../shared/ui/icon/icon-registry';
|
||||
import { ConfirmDialogComponent } from '../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||
|
||||
export interface LayoutStrategyOption {
|
||||
value: 'stack' | 'grid' | 'hero' | 'carousel' | 'split';
|
||||
@@ -44,7 +45,7 @@ const BLOCK_BY_TYPE = new Map(BLOCK_CATALOG.map(entry => [entry.type, entry]));
|
||||
@Component({
|
||||
selector: 'app-project-editor-homepage-section',
|
||||
standalone: true,
|
||||
imports: [DragDropModule, FormsModule, TranslatePipe, InputComponent, SectionCardComponent, ToggleComponent, EmptyStateComponent, HomepageOverviewComponent, IconComponent],
|
||||
imports: [DragDropModule, FormsModule, TranslatePipe, InputComponent, SectionCardComponent, ToggleComponent, EmptyStateComponent, HomepageOverviewComponent, IconComponent, ConfirmDialogComponent],
|
||||
templateUrl: './homepage-section.component.html',
|
||||
styleUrls: ['./section.shared.scss', './homepage-section.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
@@ -164,10 +165,16 @@ export class ProjectEditorHomepageSectionComponent {
|
||||
this.replaceSections([...this.sections(), copy]);
|
||||
}
|
||||
|
||||
readonly pendingRemoveBlockId = signal<string | null>(null);
|
||||
|
||||
removeBlock(sectionId: string): void {
|
||||
if (!confirm(this.translate.t('builder.blockRemoveConfirm'))) {
|
||||
return;
|
||||
}
|
||||
this.pendingRemoveBlockId.set(sectionId);
|
||||
}
|
||||
|
||||
confirmRemoveBlock(): void {
|
||||
const sectionId = this.pendingRemoveBlockId();
|
||||
this.pendingRemoveBlockId.set(null);
|
||||
if (!sectionId) return;
|
||||
this.replaceSections(this.sections().filter(section => section.id !== sectionId));
|
||||
}
|
||||
|
||||
|
||||
@@ -38,3 +38,12 @@
|
||||
}
|
||||
</div>
|
||||
</app-section-card>
|
||||
|
||||
<app-confirm-dialog
|
||||
[open]="!!pendingRemoveLocale()"
|
||||
[titleText]="'builder.confirmRemoveLanguage' | translate"
|
||||
[message]="'builder.confirmRemoveLanguage' | translate"
|
||||
[destructive]="true"
|
||||
(confirmed)="confirmRemoveLocale()"
|
||||
(cancelled)="pendingRemoveLocale.set(null)"
|
||||
/>
|
||||
|
||||
@@ -7,11 +7,12 @@ import { ButtonComponent } from '../../../shared/ui/button/button.component';
|
||||
import { InputComponent } from '../../../shared/ui/input/input.component';
|
||||
import { SectionCardComponent } from '../../../shared/ui/section-card/section-card.component';
|
||||
import { LocaleTabsComponent } from '../../../shared/ui/locale-tabs/locale-tabs.component';
|
||||
import { ConfirmDialogComponent } from '../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-project-editor-languages-section',
|
||||
standalone: true,
|
||||
imports: [FormsModule, TranslatePipe, ButtonComponent, InputComponent, SectionCardComponent, LocaleTabsComponent],
|
||||
imports: [FormsModule, TranslatePipe, ButtonComponent, InputComponent, SectionCardComponent, LocaleTabsComponent, ConfirmDialogComponent],
|
||||
templateUrl: './languages-section.component.html',
|
||||
styleUrls: ['./section.shared.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
@@ -58,10 +59,17 @@ export class ProjectEditorLanguagesSectionComponent {
|
||||
this.addLocaleError.set(null);
|
||||
}
|
||||
|
||||
readonly pendingRemoveLocale = signal<string | null>(null);
|
||||
|
||||
removeLocale(code: string): void {
|
||||
if (confirm(this.translate.t('builder.confirmRemoveLanguage'))) {
|
||||
this.facade.removeLocale(code);
|
||||
}
|
||||
this.pendingRemoveLocale.set(code);
|
||||
}
|
||||
|
||||
confirmRemoveLocale(): void {
|
||||
const code = this.pendingRemoveLocale();
|
||||
this.pendingRemoveLocale.set(null);
|
||||
if (!code) return;
|
||||
this.facade.removeLocale(code);
|
||||
}
|
||||
|
||||
setDefault(code: string): void {
|
||||
|
||||
@@ -108,4 +108,13 @@
|
||||
}
|
||||
</div>
|
||||
</app-section-card>
|
||||
|
||||
<app-confirm-dialog
|
||||
[open]="!!pendingRemoveWidgetId()"
|
||||
[titleText]="'builder.widgetRemoveConfirm' | translate"
|
||||
[message]="'builder.widgetRemoveConfirm' | translate"
|
||||
[destructive]="true"
|
||||
(confirmed)="confirmRemoveWidget()"
|
||||
(cancelled)="pendingRemoveWidgetId.set(null)"
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { EmptyStateComponent } from '../../../shared/ui/empty-state/empty-state.
|
||||
import { WidgetConfig } from '../../../shared/models/config';
|
||||
import { IconComponent } from '../../../shared/ui/icon/icon.component';
|
||||
import { AppIconName } from '../../../shared/ui/icon/icon-registry';
|
||||
import { ConfirmDialogComponent } from '../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||
|
||||
interface HeroSlideDraft {
|
||||
title: string;
|
||||
@@ -32,7 +33,7 @@ const WIDGET_LABEL_KEYS: Record<string, string> = {
|
||||
@Component({
|
||||
selector: 'app-project-editor-widgets-section',
|
||||
standalone: true,
|
||||
imports: [FormsModule, TranslatePipe, InputComponent, ButtonComponent, SectionCardComponent, ToggleComponent, EmptyStateComponent, IconComponent],
|
||||
imports: [FormsModule, TranslatePipe, InputComponent, ButtonComponent, SectionCardComponent, ToggleComponent, EmptyStateComponent, IconComponent, ConfirmDialogComponent],
|
||||
templateUrl: './widgets-section.component.html',
|
||||
styleUrls: ['./section.shared.scss', './widgets-section.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
@@ -108,10 +109,16 @@ export class ProjectEditorWidgetsSectionComponent {
|
||||
}));
|
||||
}
|
||||
|
||||
readonly pendingRemoveWidgetId = signal<string | null>(null);
|
||||
|
||||
removeWidget(widgetId: string): void {
|
||||
if (!confirm(this.translate.t('builder.widgetRemoveConfirm'))) {
|
||||
return;
|
||||
}
|
||||
this.pendingRemoveWidgetId.set(widgetId);
|
||||
}
|
||||
|
||||
confirmRemoveWidget(): void {
|
||||
const widgetId = this.pendingRemoveWidgetId();
|
||||
this.pendingRemoveWidgetId.set(null);
|
||||
if (!widgetId) return;
|
||||
this.facade.updateBootstrap(current => ({
|
||||
...current,
|
||||
pages: current.pages.map(page => ({
|
||||
|
||||
Reference in New Issue
Block a user