diff --git a/src/app/features/backoffice/media/media-library-page.component.html b/src/app/features/backoffice/media/media-library-page.component.html index 95b3004..6e4ee48 100644 --- a/src/app/features/backoffice/media/media-library-page.component.html +++ b/src/app/features/backoffice/media/media-library-page.component.html @@ -127,4 +127,17 @@ {{ 'mediaLibrary.confirm' | translate }} + + +

{{ 'mediaLibrary.bulkDeleteConfirm' | translate }}

+
+ {{ 'mediaLibrary.cancel' | translate }} + {{ 'mediaLibrary.confirm' | translate }} +
+
diff --git a/src/app/features/backoffice/media/media-library-page.component.ts b/src/app/features/backoffice/media/media-library-page.component.ts index 541b2d3..24aedf5 100644 --- a/src/app/features/backoffice/media/media-library-page.component.ts +++ b/src/app/features/backoffice/media/media-library-page.component.ts @@ -49,6 +49,7 @@ export class MediaLibraryPageComponent implements OnInit { @ViewChild('fileInput') private fileInput?: ElementRef; protected readonly pendingDelete = signal(null); + protected readonly bulkDeleteConfirmOpen = signal(false); protected readonly detailsAsset = signal(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 { + protected bulkDelete(): void { + if (this.selectedAssets().length === 0) return; + this.bulkDeleteConfirmOpen.set(true); + } + + protected async proceedBulkDelete(): Promise { 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); } diff --git a/src/app/features/content-management/components/static-pages-editor.component.html b/src/app/features/content-management/components/static-pages-editor.component.html index f3992db..ff235a0 100644 --- a/src/app/features/content-management/components/static-pages-editor.component.html +++ b/src/app/features/content-management/components/static-pages-editor.component.html @@ -94,4 +94,22 @@ } + + + + diff --git a/src/app/features/content-management/components/static-pages-editor.component.ts b/src/app/features/content-management/components/static-pages-editor.component.ts index b4de642..07c0bc3 100644 --- a/src/app/features/content-management/components/static-pages-editor.component.ts +++ b/src/app/features/content-management/components/static-pages-editor.component.ts @@ -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(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(); diff --git a/src/app/features/project-editor/components/save-bar/project-editor-save-bar.component.html b/src/app/features/project-editor/components/save-bar/project-editor-save-bar.component.html index 4b06b3e..19d3073 100644 --- a/src/app/features/project-editor/components/save-bar/project-editor-save-bar.component.html +++ b/src/app/features/project-editor/components/save-bar/project-editor-save-bar.component.html @@ -28,3 +28,20 @@ {{ 'builder.publish' | translate }} + + + + diff --git a/src/app/features/project-editor/components/save-bar/project-editor-save-bar.component.ts b/src/app/features/project-editor/components/save-bar/project-editor-save-bar.component.ts index 4407201..edc782b 100644 --- a/src/app/features/project-editor/components/save-bar/project-editor-save-bar.component.ts +++ b/src/app/features/project-editor/components/save-bar/project-editor-save-bar.component.ts @@ -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 { diff --git a/src/app/features/project-editor/pages/project-editor-page.component.html b/src/app/features/project-editor/pages/project-editor-page.component.html index 76cbf36..0db21a5 100644 --- a/src/app/features/project-editor/pages/project-editor-page.component.html +++ b/src/app/features/project-editor/pages/project-editor-page.component.html @@ -107,3 +107,12 @@ } + + diff --git a/src/app/features/project-editor/pages/project-editor-page.component.ts b/src/app/features/project-editor/pages/project-editor-page.component.ts index 74ff896..6bb6dda 100644 --- a/src/app/features/project-editor/pages/project-editor-page.component.ts +++ b/src/app/features/project-editor/pages/project-editor-page.component.ts @@ -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 = { 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 { diff --git a/src/app/features/project-editor/sections/homepage-section.component.html b/src/app/features/project-editor/sections/homepage-section.component.html index a3e338c..12ddb89 100644 --- a/src/app/features/project-editor/sections/homepage-section.component.html +++ b/src/app/features/project-editor/sections/homepage-section.component.html @@ -82,4 +82,13 @@ + + } diff --git a/src/app/features/project-editor/sections/homepage-section.component.ts b/src/app/features/project-editor/sections/homepage-section.component.ts index 98a3d1c..2379eda 100644 --- a/src/app/features/project-editor/sections/homepage-section.component.ts +++ b/src/app/features/project-editor/sections/homepage-section.component.ts @@ -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(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)); } diff --git a/src/app/features/project-editor/sections/languages-section.component.html b/src/app/features/project-editor/sections/languages-section.component.html index 8f7dcac..17f08dd 100644 --- a/src/app/features/project-editor/sections/languages-section.component.html +++ b/src/app/features/project-editor/sections/languages-section.component.html @@ -38,3 +38,12 @@ } + + diff --git a/src/app/features/project-editor/sections/languages-section.component.ts b/src/app/features/project-editor/sections/languages-section.component.ts index 2795408..c349764 100644 --- a/src/app/features/project-editor/sections/languages-section.component.ts +++ b/src/app/features/project-editor/sections/languages-section.component.ts @@ -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(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 { diff --git a/src/app/features/project-editor/sections/widgets-section.component.html b/src/app/features/project-editor/sections/widgets-section.component.html index 0a71f76..2bf4d37 100644 --- a/src/app/features/project-editor/sections/widgets-section.component.html +++ b/src/app/features/project-editor/sections/widgets-section.component.html @@ -108,4 +108,13 @@ } + + } diff --git a/src/app/features/project-editor/sections/widgets-section.component.ts b/src/app/features/project-editor/sections/widgets-section.component.ts index a05b91c..f1ab087 100644 --- a/src/app/features/project-editor/sections/widgets-section.component.ts +++ b/src/app/features/project-editor/sections/widgets-section.component.ts @@ -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 = { @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(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 => ({ diff --git a/src/app/i18n/en.ts b/src/app/i18n/en.ts index 31784f6..7b42583 100644 --- a/src/app/i18n/en.ts +++ b/src/app/i18n/en.ts @@ -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', diff --git a/src/app/i18n/hy.ts b/src/app/i18n/hy.ts index 6d31d81..782e2c2 100644 --- a/src/app/i18n/hy.ts +++ b/src/app/i18n/hy.ts @@ -1097,6 +1097,8 @@ export const hy: Translations = { qrCode: 'QR կոդ', bankCardPayment: 'Վճարում բանկային քարտով', guest: 'Հյուր', + cancel: 'Չեղարկել', + confirm: 'Հաստատել', }, location: { allRegions: 'Բոլոր տարածաշրջանները', diff --git a/src/app/i18n/ru.ts b/src/app/i18n/ru.ts index e8652c0..e299a45 100644 --- a/src/app/i18n/ru.ts +++ b/src/app/i18n/ru.ts @@ -1097,6 +1097,8 @@ export const ru: Translations = { qrCode: 'QR-код', bankCardPayment: 'Оплата банковской картой', guest: 'Гость', + cancel: 'Отмена', + confirm: 'Подтвердить', }, location: { allRegions: 'Все регионы', diff --git a/src/app/i18n/translations.ts b/src/app/i18n/translations.ts index e8334de..65e06be 100644 --- a/src/app/i18n/translations.ts +++ b/src/app/i18n/translations.ts @@ -1096,6 +1096,8 @@ export interface Translations { qrCode: string; bankCardPayment: string; guest: string; + cancel: string; + confirm: string; }; location: { allRegions: string; diff --git a/src/app/pages/cart/cart.component.html b/src/app/pages/cart/cart.component.html index c0ce9ba..b8c52d4 100644 --- a/src/app/pages/cart/cart.component.html +++ b/src/app/pages/cart/cart.component.html @@ -293,4 +293,13 @@ + + diff --git a/src/app/pages/cart/cart.component.ts b/src/app/pages/cart/cart.component.ts index 7975a3f..6818d0c 100644 --- a/src/app/pages/cart/cart.component.ts +++ b/src/app/pages/cart/cart.component.ts @@ -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'); } }); } diff --git a/src/app/shared/ui/confirm-dialog/confirm-dialog.component.html b/src/app/shared/ui/confirm-dialog/confirm-dialog.component.html new file mode 100644 index 0000000..d4f38cd --- /dev/null +++ b/src/app/shared/ui/confirm-dialog/confirm-dialog.component.html @@ -0,0 +1,11 @@ + +

{{ message() }}

+
+ + {{ cancelLabel() ?? ('common.cancel' | translate) }} + + + {{ confirmLabel() ?? ('common.confirm' | translate) }} + +
+
diff --git a/src/app/shared/ui/confirm-dialog/confirm-dialog.component.scss b/src/app/shared/ui/confirm-dialog/confirm-dialog.component.scss new file mode 100644 index 0000000..c4a6fce --- /dev/null +++ b/src/app/shared/ui/confirm-dialog/confirm-dialog.component.scss @@ -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); +} diff --git a/src/app/shared/ui/confirm-dialog/confirm-dialog.component.ts b/src/app/shared/ui/confirm-dialog/confirm-dialog.component.ts new file mode 100644 index 0000000..cf3cc3f --- /dev/null +++ b/src/app/shared/ui/confirm-dialog/confirm-dialog.component.ts @@ -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(null); + readonly message = input(''); + readonly confirmLabel = input(null); + readonly cancelLabel = input(null); + readonly destructive = input(false); + + readonly confirmed = output(); + readonly cancelled = output(); +}