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:
@@ -127,4 +127,17 @@
|
||||
<app-button variant="danger" (click)="proceedDelete()">{{ 'mediaLibrary.confirm' | translate }}</app-button>
|
||||
</div>
|
||||
</app-dialog>
|
||||
|
||||
<app-dialog
|
||||
[open]="bulkDeleteConfirmOpen()"
|
||||
[titleText]="'mediaLibrary.deleteConfirmTitle' | translate"
|
||||
size="sm"
|
||||
(closed)="bulkDeleteConfirmOpen.set(false)"
|
||||
>
|
||||
<p>{{ 'mediaLibrary.bulkDeleteConfirm' | translate }}</p>
|
||||
<div class="media-page__dialog-actions">
|
||||
<app-button variant="secondary" (click)="bulkDeleteConfirmOpen.set(false)">{{ 'mediaLibrary.cancel' | translate }}</app-button>
|
||||
<app-button variant="danger" (click)="proceedBulkDelete()">{{ 'mediaLibrary.confirm' | translate }}</app-button>
|
||||
</div>
|
||||
</app-dialog>
|
||||
</section>
|
||||
|
||||
@@ -49,6 +49,7 @@ export class MediaLibraryPageComponent implements OnInit {
|
||||
@ViewChild('fileInput') private fileInput?: ElementRef<HTMLInputElement>;
|
||||
|
||||
protected readonly pendingDelete = signal<MediaAsset | null>(null);
|
||||
protected readonly bulkDeleteConfirmOpen = signal(false);
|
||||
protected readonly detailsAsset = signal<MediaAsset | null>(null);
|
||||
protected readonly dragActive = signal(false);
|
||||
protected readonly totalPages = () => Math.max(1, Math.ceil(this.facade.total() / PAGE_SIZE));
|
||||
@@ -212,10 +213,15 @@ export class MediaLibraryPageComponent implements OnInit {
|
||||
return this.facade.items().filter(a => this.facade.isSelected(a.id));
|
||||
}
|
||||
|
||||
protected async bulkDelete(): Promise<void> {
|
||||
protected bulkDelete(): void {
|
||||
if (this.selectedAssets().length === 0) return;
|
||||
this.bulkDeleteConfirmOpen.set(true);
|
||||
}
|
||||
|
||||
protected async proceedBulkDelete(): Promise<void> {
|
||||
const ids = this.selectedAssets().map(a => a.id);
|
||||
this.bulkDeleteConfirmOpen.set(false);
|
||||
if (ids.length === 0) return;
|
||||
if (!confirm(this.translate.t('mediaLibrary.bulkDeleteConfirm'))) return;
|
||||
await this.facade.bulkDelete(ids);
|
||||
}
|
||||
|
||||
|
||||
@@ -94,4 +94,22 @@
|
||||
}
|
||||
|
||||
<app-media-picker [open]="mediaPickerOpen" (selected)="onImagePicked($event)" (closed)="mediaPickerOpen = false" />
|
||||
|
||||
<app-confirm-dialog
|
||||
[open]="!!pendingDeletePageId()"
|
||||
[titleText]="'staticPages.confirmDeletePage' | translate"
|
||||
[message]="'staticPages.confirmDeletePage' | translate"
|
||||
[destructive]="true"
|
||||
(confirmed)="confirmDeletePage()"
|
||||
(cancelled)="pendingDeletePageId.set(null)"
|
||||
/>
|
||||
|
||||
<app-confirm-dialog
|
||||
[open]="bulkDeleteConfirmOpen()"
|
||||
[titleText]="'staticPages.confirmBulkDelete' | translate"
|
||||
[message]="'staticPages.confirmBulkDelete' | translate"
|
||||
[destructive]="true"
|
||||
(confirmed)="confirmBulkDelete()"
|
||||
(cancelled)="bulkDeleteConfirmOpen.set(false)"
|
||||
/>
|
||||
</app-section-card>
|
||||
|
||||
@@ -20,6 +20,7 @@ import { StaticPagePreviewComponent } from './static-page-preview/static-page-pr
|
||||
import { ContentDashboardComponent } from './content-dashboard/content-dashboard.component';
|
||||
import { PageCardComponent, PageSeoStatus } from './page-card/page-card.component';
|
||||
import { PageEditorComponent, PageMediaField } from './page-editor/page-editor.component';
|
||||
import { ConfirmDialogComponent } from '../../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||
|
||||
type StatusFilter = 'all' | ContentPageStatus;
|
||||
const ALL_LOCALES_FILTER = 'all';
|
||||
@@ -42,6 +43,7 @@ const ALL_LOCALES_FILTER = 'all';
|
||||
ContentDashboardComponent,
|
||||
PageCardComponent,
|
||||
PageEditorComponent,
|
||||
ConfirmDialogComponent,
|
||||
],
|
||||
templateUrl: './static-pages-editor.component.html',
|
||||
styleUrls: ['../../project-editor/sections/section.shared.scss', './static-pages-editor.component.scss'],
|
||||
@@ -259,10 +261,16 @@ export class StaticPagesEditorComponent {
|
||||
this.persist([...this.allPages(), clone]);
|
||||
}
|
||||
|
||||
readonly pendingDeletePageId = signal<string | null>(null);
|
||||
|
||||
deletePage(id: string): void {
|
||||
if (!confirm(this.translate.t('staticPages.confirmDeletePage'))) {
|
||||
return;
|
||||
}
|
||||
this.pendingDeletePageId.set(id);
|
||||
}
|
||||
|
||||
confirmDeletePage(): void {
|
||||
const id = this.pendingDeletePageId();
|
||||
this.pendingDeletePageId.set(null);
|
||||
if (!id) return;
|
||||
this.persist(this.allPages().filter(page => page.id !== id));
|
||||
this.deselect(id);
|
||||
if (this.editingPageId() === id) {
|
||||
@@ -378,10 +386,14 @@ export class StaticPagesEditorComponent {
|
||||
this.selectedIds.set(new Set());
|
||||
}
|
||||
|
||||
readonly bulkDeleteConfirmOpen = signal(false);
|
||||
|
||||
bulkDelete(): void {
|
||||
if (!confirm(this.translate.t('staticPages.confirmBulkDelete'))) {
|
||||
return;
|
||||
}
|
||||
this.bulkDeleteConfirmOpen.set(true);
|
||||
}
|
||||
|
||||
confirmBulkDelete(): void {
|
||||
this.bulkDeleteConfirmOpen.set(false);
|
||||
const ids = this.selectedIds();
|
||||
this.persist(this.allPages().filter(page => !ids.has(page.id)));
|
||||
this.clearSelection();
|
||||
|
||||
@@ -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 => ({
|
||||
|
||||
@@ -1097,6 +1097,8 @@ export const en: Translations = {
|
||||
qrCode: 'QR Code',
|
||||
bankCardPayment: 'Bank card payment',
|
||||
guest: 'Guest',
|
||||
cancel: 'Cancel',
|
||||
confirm: 'Confirm',
|
||||
},
|
||||
location: {
|
||||
allRegions: 'All regions',
|
||||
|
||||
@@ -1097,6 +1097,8 @@ export const hy: Translations = {
|
||||
qrCode: 'QR կոդ',
|
||||
bankCardPayment: 'Վճարում բանկային քարտով',
|
||||
guest: 'Հյուր',
|
||||
cancel: 'Չեղարկել',
|
||||
confirm: 'Հաստատել',
|
||||
},
|
||||
location: {
|
||||
allRegions: 'Բոլոր տարածաշրջանները',
|
||||
|
||||
@@ -1097,6 +1097,8 @@ export const ru: Translations = {
|
||||
qrCode: 'QR-код',
|
||||
bankCardPayment: 'Оплата банковской картой',
|
||||
guest: 'Гость',
|
||||
cancel: 'Отмена',
|
||||
confirm: 'Подтвердить',
|
||||
},
|
||||
location: {
|
||||
allRegions: 'Все регионы',
|
||||
|
||||
@@ -1096,6 +1096,8 @@ export interface Translations {
|
||||
qrCode: string;
|
||||
bankCardPayment: string;
|
||||
guest: string;
|
||||
cancel: string;
|
||||
confirm: string;
|
||||
};
|
||||
location: {
|
||||
allRegions: string;
|
||||
|
||||
@@ -293,4 +293,13 @@
|
||||
|
||||
<app-telegram-login />
|
||||
|
||||
<app-confirm-dialog
|
||||
[open]="clearCartConfirmOpen()"
|
||||
[titleText]="'cart.confirmClear' | translate"
|
||||
[message]="'cart.confirmClear' | translate"
|
||||
[destructive]="true"
|
||||
(confirmed)="confirmClearCart()"
|
||||
(cancelled)="clearCartConfirmOpen.set(false)"
|
||||
/>
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import { EmptyStateComponent } from '../../shared/ui/empty-state/empty-state.com
|
||||
import { ButtonComponent } from '../../shared/ui/button/button.component';
|
||||
import { ConfigService } from '../../core/config/config.service';
|
||||
import { TenantResolverService } from '../../core/config/tenant-resolver.service';
|
||||
import { UserNotificationService } from '../../features/website/user-experience/services/user-notification.service';
|
||||
import { ConfirmDialogComponent } from '../../shared/ui/confirm-dialog/confirm-dialog.component';
|
||||
|
||||
type PaymentMethod = 'qr' | 'card';
|
||||
|
||||
@@ -27,7 +29,7 @@ const MODAL_FOCUSABLE_SELECTOR =
|
||||
|
||||
@Component({
|
||||
selector: 'app-cart',
|
||||
imports: [DecimalPipe, RouterLink, FormsModule, DeliverySelectorComponent, TelegramLoginComponent, LangRoutePipe, TranslatePipe, IconComponent, EmptyStateComponent, ButtonComponent],
|
||||
imports: [DecimalPipe, RouterLink, FormsModule, DeliverySelectorComponent, TelegramLoginComponent, LangRoutePipe, TranslatePipe, IconComponent, EmptyStateComponent, ButtonComponent, ConfirmDialogComponent],
|
||||
templateUrl: './cart.component.html',
|
||||
styleUrls: ['./cart.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
@@ -44,6 +46,7 @@ export class CartComponent implements OnDestroy {
|
||||
|
||||
private i18n = inject(TranslateService);
|
||||
private authService = inject(AuthService);
|
||||
private notifications = inject(UserNotificationService);
|
||||
|
||||
isAuthenticated = this.authService.isAuthenticated;
|
||||
|
||||
@@ -240,10 +243,15 @@ export class CartComponent implements OnDestroy {
|
||||
document.addEventListener('touchend', cleanup);
|
||||
}
|
||||
|
||||
readonly clearCartConfirmOpen = signal(false);
|
||||
|
||||
clearCart(): void {
|
||||
if (confirm(this.i18n.t('cart.confirmClear'))) {
|
||||
this.cartService.clearCart();
|
||||
}
|
||||
this.clearCartConfirmOpen.set(true);
|
||||
}
|
||||
|
||||
confirmClearCart(): void {
|
||||
this.cartService.clearCart();
|
||||
this.clearCartConfirmOpen.set(false);
|
||||
}
|
||||
|
||||
readonly getMainImage = getMainImage;
|
||||
@@ -262,12 +270,12 @@ export class CartComponent implements OnDestroy {
|
||||
|
||||
checkout(paymentMethod: PaymentMethod): void {
|
||||
if (!this.allRequiredDeliveriesSelected()) {
|
||||
alert(this.i18n.t('cart.deliveryRequired'));
|
||||
this.notifications.show(this.i18n.t('cart.deliveryRequired'), 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.termsAccepted) {
|
||||
alert(this.i18n.t('cart.acceptTerms'));
|
||||
this.notifications.show(this.i18n.t('cart.acceptTerms'), 'warning');
|
||||
return;
|
||||
}
|
||||
this.openPaymentPopup(paymentMethod);
|
||||
@@ -615,8 +623,7 @@ export class CartComponent implements OnDestroy {
|
||||
this.apiService.submitPurchaseEmail(emailData).subscribe({
|
||||
next: () => {
|
||||
this.emailSubmitting.set(false);
|
||||
// Show success message
|
||||
alert(this.i18n.t('cart.emailSuccess'));
|
||||
this.notifications.show(this.i18n.t('cart.emailSuccess'), 'success');
|
||||
// Close popup and redirect to home page
|
||||
setTimeout(() => {
|
||||
this.closePaymentPopup();
|
||||
@@ -627,7 +634,7 @@ export class CartComponent implements OnDestroy {
|
||||
error: (err) => {
|
||||
console.error('Error submitting email:', err);
|
||||
this.emailSubmitting.set(false);
|
||||
alert(this.i18n.t('cart.emailError'));
|
||||
this.notifications.show(this.i18n.t('cart.emailError'), 'warning');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<app-dialog [open]="open()" [titleText]="titleText()" size="sm" (closed)="cancelled.emit()">
|
||||
<p class="app-confirm-dialog__message">{{ message() }}</p>
|
||||
<div class="app-confirm-dialog__actions">
|
||||
<app-button variant="secondary" (click)="cancelled.emit()">
|
||||
{{ cancelLabel() ?? ('common.cancel' | translate) }}
|
||||
</app-button>
|
||||
<app-button [variant]="destructive() ? 'danger' : 'primary'" (click)="confirmed.emit()">
|
||||
{{ confirmLabel() ?? ('common.confirm' | translate) }}
|
||||
</app-button>
|
||||
</div>
|
||||
</app-dialog>
|
||||
@@ -0,0 +1,10 @@
|
||||
.app-confirm-dialog__message {
|
||||
margin: 0 0 var(--space-lg, 1.5rem);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.app-confirm-dialog__actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--space-sm, 0.5rem);
|
||||
}
|
||||
24
src/app/shared/ui/confirm-dialog/confirm-dialog.component.ts
Normal file
24
src/app/shared/ui/confirm-dialog/confirm-dialog.component.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
|
||||
import { DialogComponent } from '../dialog/dialog.component';
|
||||
import { ButtonComponent } from '../button/button.component';
|
||||
import { TranslatePipe } from '../../../i18n/translate.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-confirm-dialog',
|
||||
standalone: true,
|
||||
imports: [DialogComponent, ButtonComponent, TranslatePipe],
|
||||
templateUrl: './confirm-dialog.component.html',
|
||||
styleUrl: './confirm-dialog.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ConfirmDialogComponent {
|
||||
readonly open = input(false);
|
||||
readonly titleText = input<string | null>(null);
|
||||
readonly message = input<string>('');
|
||||
readonly confirmLabel = input<string | null>(null);
|
||||
readonly cancelLabel = input<string | null>(null);
|
||||
readonly destructive = input(false);
|
||||
|
||||
readonly confirmed = output<void>();
|
||||
readonly cancelled = output<void>();
|
||||
}
|
||||
Reference in New Issue
Block a user