@masterteam/system-settings 0.0.21 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, inject, ChangeDetectionStrategy, Component, signal, computed, DestroyRef } from '@angular/core';
3
- import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
2
+ import { input, inject, ChangeDetectionStrategy, Component, signal, computed, DestroyRef, effect, untracked, HostListener } from '@angular/core';
3
+ import { toSignal, takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
4
4
  import { ActivatedRoute, Router } from '@angular/router';
5
5
  import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
6
6
  import { Breadcrumb } from '@masterteam/components/breadcrumb';
@@ -8,12 +8,12 @@ import { EmptyState } from '@masterteam/components/empty-state';
8
8
  import { Page } from '@masterteam/components/page';
9
9
  import { ToastService } from '@masterteam/components/toast';
10
10
  import { Tooltip } from '@masterteam/components/tooltip';
11
- import { E as EmailDeliveryFacade, i as isConnectionTestAttempt, a as EmailDeliveryStatus, g as getEmailDeliveryStatusMap, b as EMAIL_DELIVERY_ATTEMPT_STATUSES, c as createEmailTestRequestId, d as areEmailTestRecipientsValid, p as parseEmailTestRecipients, e as isEmailTestRequestPending, f as EMAIL_DELIVERY_PERMISSIONS } from './masterteam-system-settings-masterteam-system-settings-DBy-LQB0.mjs';
11
+ import { E as EmailDeliveryFacade, i as isConnectionTestAttempt, a as EmailDeliveryStatus, g as getEmailDeliveryStatusMap, b as EMAIL_DELIVERY_ATTEMPT_STATUSES, c as createEmailTestRequestId, d as areEmailTestRecipientsValid, p as parseEmailTestRecipients, e as isEmailTestRequestPending, f as EMAIL_DELIVERY_PERMISSIONS, h as EmailLayoutFacade, j as EMAIL_LAYOUT_CONTENT_SLOT, k as EMAIL_LAYOUT_MAX_BYTES, r as readLayoutErrorDiagnostics, l as EmailVisualEditor, m as EmailHtmlPreview } from './masterteam-system-settings-masterteam-system-settings-C51hZw-Q.mjs';
12
12
  import { Icon } from '@masterteam/icons';
13
13
  import { Button } from '@masterteam/components/button';
14
14
  import { ModalService } from '@masterteam/components/modal';
15
15
  import { Table } from '@masterteam/components/table';
16
- import { take, timer, of, map, switchMap, finalize } from 'rxjs';
16
+ import { take, timer, of, map, switchMap, finalize, Subject, merge, debounceTime, distinctUntilChanged, catchError, startWith } from 'rxjs';
17
17
  import { DatePipe, DOCUMENT } from '@angular/common';
18
18
  import { ModalRef } from '@masterteam/components/dialog';
19
19
  import * as i1 from '@angular/forms';
@@ -26,7 +26,7 @@ import { TextareaFieldConfig, TextFieldConfig, ToggleFieldConfig, ValidatorConfi
26
26
  import { DynamicForm } from '@masterteam/forms/dynamic-form';
27
27
  import { Menu } from '@masterteam/components/menu';
28
28
  import * as i1$1 from 'primeng/skeleton';
29
- import { SkeletonModule } from 'primeng/skeleton';
29
+ import { SkeletonModule, Skeleton } from 'primeng/skeleton';
30
30
 
31
31
  class EmailOperatorReasonDialog {
32
32
  itemName = input.required(...(ngDevMode ? [{ debugName: "itemName" }] : /* istanbul ignore next */ []));
@@ -338,7 +338,7 @@ class EmailDeliveryAttempts {
338
338
  return this.transloco.translate(`email-delivery.${key}`);
339
339
  }
340
340
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryAttempts, deps: [], target: i0.ɵɵFactoryTarget.Component });
341
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryAttempts, isStandalone: true, selector: "mt-email-delivery-attempts", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\r\n <section class=\"space-y-4\" aria-label=\"{{ t('activity-title') }}\">\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [rowActions]=\"actions()\"\r\n [filters]=\"tableFilters()\"\r\n [loading]=\"facade.attemptsLoading()\"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [emptyTitle]=\"t('no-attempts-title')\"\r\n [emptyDescription]=\"t('no-attempts-description')\"\r\n filterMode=\"column\"\r\n dataKey=\"id\"\r\n actionShape=\"popover\"\r\n [clickableRows]=\"true\"\r\n storageKey=\"email-delivery-attempts\"\r\n (filtersChange)=\"onFiltersChange($event)\"\r\n (rowClick)=\"openDetails($event)\"\r\n >\r\n <ng-template #captionEnd>\r\n <mt-button\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n icon=\"arrow.refresh-cw-01\"\r\n [label]=\"t('refresh')\"\r\n [loading]=\"facade.attemptsLoading()\"\r\n (onClick)=\"refresh()\"\r\n />\r\n </ng-template>\r\n </mt-table>\r\n\r\n @if (facade.attemptsNextCursor()) {\r\n <div class=\"flex justify-center\">\r\n <mt-button\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n [label]=\"t('load-more')\"\r\n [loading]=\"facade.attemptsLoading()\"\r\n (onClick)=\"loadMore()\"\r\n />\r\n </div>\r\n }\r\n </section>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "captionStartTemplate", "captionEndTemplate", "emptyTitle", "emptyDescription", "emptyActionLabel", "emptyActionIcon", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "emptyAction", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
341
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryAttempts, isStandalone: true, selector: "mt-email-delivery-attempts", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\r\n <section class=\"space-y-4\" aria-label=\"{{ t('activity-title') }}\">\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [rowActions]=\"actions()\"\r\n [filters]=\"tableFilters()\"\r\n [loading]=\"facade.attemptsLoading()\"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [emptyTitle]=\"t('no-attempts-title')\"\r\n [emptyDescription]=\"t('no-attempts-description')\"\r\n filterMode=\"column\"\r\n dataKey=\"id\"\r\n actionShape=\"popover\"\r\n [clickableRows]=\"true\"\r\n storageKey=\"email-delivery-attempts\"\r\n (filtersChange)=\"onFiltersChange($event)\"\r\n (rowClick)=\"openDetails($event)\"\r\n >\r\n <ng-template #captionEnd>\r\n <mt-button\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n icon=\"arrow.refresh-cw-01\"\r\n [label]=\"t('refresh')\"\r\n [loading]=\"facade.attemptsLoading()\"\r\n (onClick)=\"refresh()\"\r\n />\r\n </ng-template>\r\n </mt-table>\r\n\r\n @if (facade.attemptsNextCursor()) {\r\n <div class=\"flex justify-center\">\r\n <mt-button\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n [label]=\"t('load-more')\"\r\n [loading]=\"facade.attemptsLoading()\"\r\n (onClick)=\"loadMore()\"\r\n />\r\n </div>\r\n }\r\n </section>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "filterCloseOnOutsideClick", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "captionStartTemplate", "captionEndTemplate", "emptyTitle", "emptyDescription", "emptyActionLabel", "emptyActionIcon", "externalFiltersActive", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "emptyAction", "filtersCleared", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
342
342
  }
343
343
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryAttempts, decorators: [{
344
344
  type: Component,
@@ -525,7 +525,7 @@ class EmailDeliveryHostEvidence {
525
525
  return this.transloco.translate(`email-delivery.${key}`);
526
526
  }
527
527
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryHostEvidence, deps: [], target: i0.ɵɵFactoryTarget.Component });
528
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryHostEvidence, isStandalone: true, selector: "mt-email-delivery-host-evidence", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\r\n <section class=\"space-y-3\" aria-labelledby=\"host-evidence-heading\">\r\n <div\r\n class=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\"\r\n >\r\n <div>\r\n <h3 id=\"host-evidence-heading\" class=\"font-semibold text-color\">\r\n {{ t(\"host-evidence-title\") }}\r\n </h3>\r\n <p class=\"mt-1 text-sm text-muted-color\">\r\n {{ t(\"host-evidence-description\") }}\r\n </p>\r\n </div>\r\n <mt-button\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n icon=\"arrow.refresh-cw-01\"\r\n [label]=\"t('rebuild-evidence-index')\"\r\n [disabled]=\"!facade.managementAvailable()\"\r\n [loading]=\"facade.mutationLoading() === 'rebuild-evidence'\"\r\n (onClick)=\"confirmRebuild()\"\r\n />\r\n </div>\r\n\r\n @if (facade.hostEvidenceScanBudgetExhausted()) {\r\n <div\r\n class=\"rounded-md border border-amber-200 bg-amber-50 p-3 text-sm text-amber-950\"\r\n role=\"status\"\r\n >\r\n {{ t(\"scan-budget-exhausted\") }}\r\n </div>\r\n }\r\n\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [rowActions]=\"actions()\"\r\n [loading]=\"facade.hostEvidenceLoading()\"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [emptyTitle]=\"t('no-evidence-title')\"\r\n [emptyDescription]=\"t('no-evidence-description')\"\r\n filterMode=\"column\"\r\n actionShape=\"popover\"\r\n dataKey=\"evidenceRowId\"\r\n storageKey=\"email-delivery-host-evidence\"\r\n />\r\n\r\n @if (facade.hostEvidenceNextCursor()) {\r\n <div class=\"flex justify-center\">\r\n <mt-button\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n [label]=\"t('load-more')\"\r\n [loading]=\"facade.hostEvidenceLoading()\"\r\n (onClick)=\"loadMore()\"\r\n />\r\n </div>\r\n }\r\n </section>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "captionStartTemplate", "captionEndTemplate", "emptyTitle", "emptyDescription", "emptyActionLabel", "emptyActionIcon", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "emptyAction", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
528
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryHostEvidence, isStandalone: true, selector: "mt-email-delivery-host-evidence", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\r\n <section class=\"space-y-3\" aria-labelledby=\"host-evidence-heading\">\r\n <div\r\n class=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\"\r\n >\r\n <div>\r\n <h3 id=\"host-evidence-heading\" class=\"font-semibold text-color\">\r\n {{ t(\"host-evidence-title\") }}\r\n </h3>\r\n <p class=\"mt-1 text-sm text-muted-color\">\r\n {{ t(\"host-evidence-description\") }}\r\n </p>\r\n </div>\r\n <mt-button\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n icon=\"arrow.refresh-cw-01\"\r\n [label]=\"t('rebuild-evidence-index')\"\r\n [disabled]=\"!facade.managementAvailable()\"\r\n [loading]=\"facade.mutationLoading() === 'rebuild-evidence'\"\r\n (onClick)=\"confirmRebuild()\"\r\n />\r\n </div>\r\n\r\n @if (facade.hostEvidenceScanBudgetExhausted()) {\r\n <div\r\n class=\"rounded-md border border-amber-200 bg-amber-50 p-3 text-sm text-amber-950\"\r\n role=\"status\"\r\n >\r\n {{ t(\"scan-budget-exhausted\") }}\r\n </div>\r\n }\r\n\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [rowActions]=\"actions()\"\r\n [loading]=\"facade.hostEvidenceLoading()\"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [emptyTitle]=\"t('no-evidence-title')\"\r\n [emptyDescription]=\"t('no-evidence-description')\"\r\n filterMode=\"column\"\r\n actionShape=\"popover\"\r\n dataKey=\"evidenceRowId\"\r\n storageKey=\"email-delivery-host-evidence\"\r\n />\r\n\r\n @if (facade.hostEvidenceNextCursor()) {\r\n <div class=\"flex justify-center\">\r\n <mt-button\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n [label]=\"t('load-more')\"\r\n [loading]=\"facade.hostEvidenceLoading()\"\r\n (onClick)=\"loadMore()\"\r\n />\r\n </div>\r\n }\r\n </section>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "filterCloseOnOutsideClick", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "captionStartTemplate", "captionEndTemplate", "emptyTitle", "emptyDescription", "emptyActionLabel", "emptyActionIcon", "externalFiltersActive", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "emptyAction", "filtersCleared", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
529
529
  }
530
530
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryHostEvidence, decorators: [{
531
531
  type: Component,
@@ -650,7 +650,7 @@ class EmailDeliveryQuarantine {
650
650
  return this.transloco.translate(`email-delivery.${key}`);
651
651
  }
652
652
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryQuarantine, deps: [], target: i0.ɵɵFactoryTarget.Component });
653
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryQuarantine, isStandalone: true, selector: "mt-email-delivery-quarantine", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\r\n <section class=\"space-y-3\" aria-labelledby=\"quarantine-heading\">\r\n <div>\r\n <h3 id=\"quarantine-heading\" class=\"font-semibold text-color\">\r\n {{ t(\"quarantine-title\") }}\r\n </h3>\r\n <p class=\"mt-1 text-sm text-muted-color\">\r\n {{ t(\"quarantine-description\") }}\r\n </p>\r\n </div>\r\n\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [rowActions]=\"actions()\"\r\n [loading]=\"facade.quarantineLoading()\"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [emptyTitle]=\"t('no-quarantine-title')\"\r\n [emptyDescription]=\"t('no-quarantine-description')\"\r\n filterMode=\"column\"\r\n actionShape=\"popover\"\r\n dataKey=\"itemId\"\r\n storageKey=\"email-delivery-quarantine\"\r\n />\r\n\r\n @if (facade.quarantineNextCursor()) {\r\n <div class=\"flex justify-center\">\r\n <mt-button\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n [label]=\"t('load-more')\"\r\n [loading]=\"facade.quarantineLoading()\"\r\n (onClick)=\"loadMore()\"\r\n />\r\n </div>\r\n }\r\n </section>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "captionStartTemplate", "captionEndTemplate", "emptyTitle", "emptyDescription", "emptyActionLabel", "emptyActionIcon", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "emptyAction", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
653
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryQuarantine, isStandalone: true, selector: "mt-email-delivery-quarantine", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\r\n <section class=\"space-y-3\" aria-labelledby=\"quarantine-heading\">\r\n <div>\r\n <h3 id=\"quarantine-heading\" class=\"font-semibold text-color\">\r\n {{ t(\"quarantine-title\") }}\r\n </h3>\r\n <p class=\"mt-1 text-sm text-muted-color\">\r\n {{ t(\"quarantine-description\") }}\r\n </p>\r\n </div>\r\n\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [rowActions]=\"actions()\"\r\n [loading]=\"facade.quarantineLoading()\"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [emptyTitle]=\"t('no-quarantine-title')\"\r\n [emptyDescription]=\"t('no-quarantine-description')\"\r\n filterMode=\"column\"\r\n actionShape=\"popover\"\r\n dataKey=\"itemId\"\r\n storageKey=\"email-delivery-quarantine\"\r\n />\r\n\r\n @if (facade.quarantineNextCursor()) {\r\n <div class=\"flex justify-center\">\r\n <mt-button\r\n severity=\"secondary\"\r\n variant=\"outlined\"\r\n [label]=\"t('load-more')\"\r\n [loading]=\"facade.quarantineLoading()\"\r\n (onClick)=\"loadMore()\"\r\n />\r\n </div>\r\n }\r\n </section>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "filterCloseOnOutsideClick", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "captionStartTemplate", "captionEndTemplate", "emptyTitle", "emptyDescription", "emptyActionLabel", "emptyActionIcon", "externalFiltersActive", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "emptyAction", "filtersCleared", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
654
654
  }
655
655
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryQuarantine, decorators: [{
656
656
  type: Component,
@@ -2041,7 +2041,7 @@ class EmailDeliveryConnections {
2041
2041
  return this.transloco.translate(`email-delivery.${key}`);
2042
2042
  }
2043
2043
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryConnections, deps: [], target: i0.ɵɵFactoryTarget.Component });
2044
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryConnections, isStandalone: true, selector: "mt-email-delivery-connections", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\r\n <section class=\"space-y-4\" aria-label=\"{{ t('connections-title') }}\">\r\n @if (\r\n !facade.statusLoading() &&\r\n !facade.catalogLoading() &&\r\n facade.providers().length === 0\r\n ) {\r\n <mt-empty-state\r\n illustration=\"none\"\r\n [title]=\"t('no-providers-title')\"\r\n [description]=\"t('no-providers-description')\"\r\n />\r\n } @else {\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [rowActions]=\"rowActions()\"\r\n [loading]=\"\r\n facade.statusLoading() ||\r\n facade.connectionsLoading() ||\r\n facade.catalogLoading()\r\n \"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [emptyTitle]=\"t('no-connections-title')\"\r\n [emptyDescription]=\"t('no-connections-description')\"\r\n [emptyActionLabel]=\"canCreate() ? t('create-connection') : undefined\"\r\n emptyActionIcon=\"general.plus\"\r\n filterMode=\"column\"\r\n actionShape=\"popover\"\r\n [clickableRows]=\"true\"\r\n dataKey=\"id\"\r\n storageKey=\"email-delivery-connections\"\r\n (emptyAction)=\"createConnection()\"\r\n (rowClick)=\"openDetails($event)\"\r\n >\r\n <ng-template #captionEnd>\r\n @if (canCreate()) {\r\n <mt-button\r\n [label]=\"t('create-connection')\"\r\n icon=\"general.plus\"\r\n (onClick)=\"createConnection()\"\r\n />\r\n }\r\n </ng-template>\r\n </mt-table>\r\n }\r\n </section>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: EmptyState, selector: "mt-empty-state", inputs: ["variant", "illustration", "title", "description", "actionLabel", "actionIcon"], outputs: ["actionClick"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "captionStartTemplate", "captionEndTemplate", "emptyTitle", "emptyDescription", "emptyActionLabel", "emptyActionIcon", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "emptyAction", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2044
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryConnections, isStandalone: true, selector: "mt-email-delivery-connections", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\r\n <section class=\"space-y-4\" aria-label=\"{{ t('connections-title') }}\">\r\n @if (\r\n !facade.statusLoading() &&\r\n !facade.catalogLoading() &&\r\n facade.providers().length === 0\r\n ) {\r\n <mt-empty-state\r\n illustration=\"none\"\r\n [title]=\"t('no-providers-title')\"\r\n [description]=\"t('no-providers-description')\"\r\n />\r\n } @else {\r\n <mt-table\r\n [data]=\"rows()\"\r\n [columns]=\"columns()\"\r\n [rowActions]=\"rowActions()\"\r\n [loading]=\"\r\n facade.statusLoading() ||\r\n facade.connectionsLoading() ||\r\n facade.catalogLoading()\r\n \"\r\n [generalSearch]=\"true\"\r\n [showFilters]=\"true\"\r\n [emptyTitle]=\"t('no-connections-title')\"\r\n [emptyDescription]=\"t('no-connections-description')\"\r\n [emptyActionLabel]=\"canCreate() ? t('create-connection') : undefined\"\r\n emptyActionIcon=\"general.plus\"\r\n filterMode=\"column\"\r\n actionShape=\"popover\"\r\n [clickableRows]=\"true\"\r\n dataKey=\"id\"\r\n storageKey=\"email-delivery-connections\"\r\n (emptyAction)=\"createConnection()\"\r\n (rowClick)=\"openDetails($event)\"\r\n >\r\n <ng-template #captionEnd>\r\n @if (canCreate()) {\r\n <mt-button\r\n [label]=\"t('create-connection')\"\r\n icon=\"general.plus\"\r\n (onClick)=\"createConnection()\"\r\n />\r\n }\r\n </ng-template>\r\n </mt-table>\r\n }\r\n </section>\r\n</ng-container>\r\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: EmptyState, selector: "mt-empty-state", inputs: ["variant", "illustration", "title", "description", "actionLabel", "actionIcon"], outputs: ["actionClick"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "filterCloseOnOutsideClick", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "persistStateExclude", "exportable", "printable", "groupable", "groupCountMap", "cellClickFilter", "freezeActions", "virtualScroll", "virtualScrollItemSize", "scrollHeight", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "captionStartTemplate", "captionEndTemplate", "emptyTitle", "emptyDescription", "emptyActionLabel", "emptyActionIcon", "externalFiltersActive", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy", "sortField", "sortDirection"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "emptyAction", "filtersCleared", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange", "sortFieldChange", "sortDirectionChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2045
2045
  }
2046
2046
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryConnections, decorators: [{
2047
2047
  type: Component,
@@ -2061,6 +2061,244 @@ function toKebabCase(value) {
2061
2061
  return value.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
2062
2062
  }
2063
2063
 
2064
+ /**
2065
+ * Product Settings → Email → Email design. Edits the one shared base that wraps
2066
+ * every notification email (logo, header, footer, typography). The source is
2067
+ * edited as exact HTML — no visual editor rewrites tables, inline styles,
2068
+ * `lang`/`dir` or the `{content}` slot — next to a live, sandboxed preview of
2069
+ * the server's fixed bilingual sample.
2070
+ */
2071
+ class EmailDeliveryDesign {
2072
+ facade = inject(EmailLayoutFacade);
2073
+ confirmation = inject(ConfirmationService);
2074
+ toast = inject(ToastService);
2075
+ transloco = inject(TranslocoService);
2076
+ destroyRef = inject(DestroyRef);
2077
+ sourceControl = new FormControl('', { nonNullable: true });
2078
+ narrow = new FormControl(false, { nonNullable: true });
2079
+ narrowValue = signal(false, ...(ngDevMode ? [{ debugName: "narrowValue" }] : /* istanbul ignore next */ []));
2080
+ preview = signal({ status: 'idle' }, ...(ngDevMode ? [{ debugName: "preview" }] : /* istanbul ignore next */ []));
2081
+ /** Last successful render — kept on screen (dimmed) while the next one runs. */
2082
+ rendered = signal(null, ...(ngDevMode ? [{ debugName: "rendered" }] : /* istanbul ignore next */ []));
2083
+ previewErrorCode = computed(() => {
2084
+ const state = this.preview();
2085
+ return state.status === 'error' ? state.code : null;
2086
+ }, ...(ngDevMode ? [{ debugName: "previewErrorCode" }] : /* istanbul ignore next */ []));
2087
+ retry$ = new Subject();
2088
+ writeDiagnostics = signal([], ...(ngDevMode ? [{ debugName: "writeDiagnostics" }] : /* istanbul ignore next */ []));
2089
+ slotCount = computed(() => this.facade.draft().split(EMAIL_LAYOUT_CONTENT_SLOT).length - 1, ...(ngDevMode ? [{ debugName: "slotCount" }] : /* istanbul ignore next */ []));
2090
+ byteSize = computed(() => new TextEncoder().encode(this.facade.draft()).length, ...(ngDevMode ? [{ debugName: "byteSize" }] : /* istanbul ignore next */ []));
2091
+ sizeLabel = computed(() => this.transloco.translate('email-delivery.design.size', {
2092
+ used: (this.byteSize() / 1024).toFixed(1),
2093
+ limit: EMAIL_LAYOUT_MAX_BYTES / 1024,
2094
+ }), ...(ngDevMode ? [{ debugName: "sizeLabel" }] : /* istanbul ignore next */ []));
2095
+ overLimit = computed(() => this.byteSize() > EMAIL_LAYOUT_MAX_BYTES, ...(ngDevMode ? [{ debugName: "overLimit" }] : /* istanbul ignore next */ []));
2096
+ /** The size only matters, and is only shown, close to the limit. */
2097
+ nearLimit = computed(() => this.byteSize() >= EMAIL_LAYOUT_MAX_BYTES * 0.8, ...(ngDevMode ? [{ debugName: "nearLimit" }] : /* istanbul ignore next */ []));
2098
+ /** A preview only counts while it was rendered from exactly the current draft. */
2099
+ freshPreview = computed(() => {
2100
+ const state = this.preview();
2101
+ return state.status === 'ready' && state.body === this.facade.draft()
2102
+ ? state.result
2103
+ : null;
2104
+ }, ...(ngDevMode ? [{ debugName: "freshPreview" }] : /* istanbul ignore next */ []));
2105
+ /** The preview ran against a newer live revision than this draft started from. */
2106
+ baselineMoved = computed(() => {
2107
+ const result = this.freshPreview();
2108
+ const snapshot = this.facade.snapshot();
2109
+ return (!!result && !!snapshot && result.effectiveRevision !== snapshot.revision);
2110
+ }, ...(ngDevMode ? [{ debugName: "baselineMoved" }] : /* istanbul ignore next */ []));
2111
+ blockingDiagnostics = computed(() => (this.facade.snapshot()?.diagnostics ?? []).filter((diagnostic) => diagnostic.severity === 'error'), ...(ngDevMode ? [{ debugName: "blockingDiagnostics" }] : /* istanbul ignore next */ []));
2112
+ canPublish = computed(() => {
2113
+ const snapshot = this.facade.snapshot();
2114
+ return (!!snapshot &&
2115
+ (this.facade.dirty() || snapshot.source !== 'published') &&
2116
+ !!this.freshPreview() &&
2117
+ !this.baselineMoved() &&
2118
+ this.slotCount() === 1 &&
2119
+ !this.overLimit() &&
2120
+ !this.facade.conflict() &&
2121
+ this.facade.saving() === null);
2122
+ }, ...(ngDevMode ? [{ debugName: "canPublish" }] : /* istanbul ignore next */ []));
2123
+ publishBlockedReason = computed(() => {
2124
+ if (this.canPublish() || this.facade.saving())
2125
+ return '';
2126
+ const key = this.overLimit()
2127
+ ? 'size-limit'
2128
+ : this.slotCount() !== 1
2129
+ ? 'slot'
2130
+ : this.facade.conflict() || this.baselineMoved()
2131
+ ? 'conflict'
2132
+ : !this.freshPreview()
2133
+ ? 'preview-required'
2134
+ : 'no-changes';
2135
+ return this.transloco.translate(`email-delivery.design.publish-blocked.${key}`);
2136
+ }, ...(ngDevMode ? [{ debugName: "publishBlockedReason" }] : /* istanbul ignore next */ []));
2137
+ constructor() {
2138
+ // The facade owns the draft so it survives tab switches; mirror it into
2139
+ // the textarea only when it changed from outside (reload, publish, discard).
2140
+ effect(() => {
2141
+ const draft = this.facade.draft();
2142
+ untracked(() => {
2143
+ if (this.sourceControl.value !== draft) {
2144
+ this.sourceControl.setValue(draft, { emitEvent: false });
2145
+ }
2146
+ });
2147
+ });
2148
+ this.sourceControl.valueChanges
2149
+ .pipe(takeUntilDestroyed())
2150
+ .subscribe((value) => this.facade.draft.set(value));
2151
+ this.narrow.valueChanges
2152
+ .pipe(takeUntilDestroyed())
2153
+ .subscribe((value) => this.narrowValue.set(value));
2154
+ // One preview stream per editor: a pause in typing cancels the previous
2155
+ // request and renders the current draft; Retry re-renders the same draft.
2156
+ const draft$ = toObservable(this.facade.draft);
2157
+ merge(draft$.pipe(debounceTime(500), distinctUntilChanged()), this.retry$.pipe(map(() => this.facade.draft())))
2158
+ .pipe(switchMap((body) => {
2159
+ if (!this.facade.snapshot())
2160
+ return of({ status: 'idle' });
2161
+ return this.facade.preview(body).pipe(map((result) => ({ status: 'ready', body, result })), catchError((error) => of({
2162
+ status: 'error',
2163
+ body,
2164
+ code: this.facade.readErrorCode(error),
2165
+ })), startWith({ status: 'updating', body }));
2166
+ }), takeUntilDestroyed())
2167
+ .subscribe((state) => {
2168
+ this.preview.set(state);
2169
+ if (state.status === 'ready')
2170
+ this.rendered.set(state.result);
2171
+ });
2172
+ }
2173
+ ngOnInit() {
2174
+ // Unsaved edits from an earlier visit to this tab are kept; otherwise
2175
+ // always start from what is live now.
2176
+ if (this.facade.dirty()) {
2177
+ this.retry$.next();
2178
+ }
2179
+ else {
2180
+ this.reload();
2181
+ }
2182
+ }
2183
+ onBeforeUnload(event) {
2184
+ if (this.facade.dirty() || this.facade.saving()) {
2185
+ event.preventDefault();
2186
+ }
2187
+ }
2188
+ reload() {
2189
+ this.facade
2190
+ .load()
2191
+ .pipe(takeUntilDestroyed(this.destroyRef))
2192
+ .subscribe({
2193
+ next: () => this.retry$.next(),
2194
+ error: () => undefined,
2195
+ });
2196
+ }
2197
+ retryPreview() {
2198
+ this.retry$.next();
2199
+ }
2200
+ discard() {
2201
+ if (!this.facade.dirty())
2202
+ return;
2203
+ // Discarded edits cannot be brought back, so ask the same question as
2204
+ // leaving the page.
2205
+ this.confirmation.confirm({
2206
+ type: 'dialog',
2207
+ header: this.t('leave.header'),
2208
+ message: this.t('leave.message'),
2209
+ acceptLabel: this.t('leave.discard'),
2210
+ rejectLabel: this.t('leave.stay'),
2211
+ acceptButton: { severity: 'danger' },
2212
+ accept: () => {
2213
+ this.facade.discard();
2214
+ this.writeDiagnostics.set([]);
2215
+ },
2216
+ });
2217
+ }
2218
+ publish() {
2219
+ if (!this.canPublish())
2220
+ return;
2221
+ this.confirmation.confirm({
2222
+ header: this.t('confirm-publish.header'),
2223
+ message: this.t('confirm-publish.message'),
2224
+ icon: 'communication.mail-01',
2225
+ acceptLabel: this.t('actions.publish'),
2226
+ accept: () => this.facade.publish().subscribe({
2227
+ next: () => {
2228
+ this.writeDiagnostics.set([]);
2229
+ this.toast.success(this.t('published'));
2230
+ this.retry$.next();
2231
+ },
2232
+ error: (error) => this.onWriteError(error),
2233
+ }),
2234
+ });
2235
+ }
2236
+ restorePrevious() {
2237
+ if (this.facade.saving())
2238
+ return;
2239
+ this.confirmation.confirm({
2240
+ header: this.t('confirm-restore.header'),
2241
+ message: this.t(this.facade.dirty()
2242
+ ? 'confirm-restore.message-dirty'
2243
+ : 'confirm-restore.message'),
2244
+ icon: 'arrow.refresh-cw-01',
2245
+ acceptLabel: this.t('actions.restore'),
2246
+ accept: () => this.facade.restorePrevious().subscribe({
2247
+ next: () => {
2248
+ this.writeDiagnostics.set([]);
2249
+ this.toast.success(this.t('restored'));
2250
+ this.retry$.next();
2251
+ },
2252
+ error: (error) => this.onWriteError(error),
2253
+ }),
2254
+ });
2255
+ }
2256
+ resolveConflict(keepDraft) {
2257
+ this.facade
2258
+ .reloadLatest(keepDraft)
2259
+ .pipe(takeUntilDestroyed(this.destroyRef))
2260
+ .subscribe({ next: () => this.retry$.next(), error: () => undefined });
2261
+ }
2262
+ describeDiagnostic(diagnostic) {
2263
+ return this.facade.describeDiagnostic(diagnostic);
2264
+ }
2265
+ describeCode(code) {
2266
+ return this.facade.describeCode(code);
2267
+ }
2268
+ onWriteError(error) {
2269
+ this.writeDiagnostics.set(readLayoutErrorDiagnostics(error));
2270
+ if (this.facade.conflict())
2271
+ return; // the banner explains it
2272
+ this.toast.error(this.facade.describeCode(this.facade.readErrorCode(error)));
2273
+ }
2274
+ t(key) {
2275
+ return this.transloco.translate(`email-delivery.design.${key}`);
2276
+ }
2277
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryDesign, deps: [], target: i0.ɵɵFactoryTarget.Component });
2278
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryDesign, isStandalone: true, selector: "mt-email-delivery-design", host: { listeners: { "window:beforeunload": "onBeforeUnload($event)" } }, ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery.design'\">\n @if (facade.loading() && !facade.snapshot()) {\n <div class=\"grid grid-cols-1 gap-4 xl:grid-cols-2\" aria-live=\"polite\">\n <p-skeleton width=\"100%\" height=\"32rem\" borderRadius=\"0.375rem\" />\n <p-skeleton width=\"100%\" height=\"32rem\" borderRadius=\"0.375rem\" />\n </div>\n } @else if (facade.loadFailed() && !facade.snapshot()) {\n <mt-empty-state\n illustration=\"none\"\n [title]=\"t('load-error-title')\"\n [description]=\"describeCode(facade.loadErrorCode())\"\n [actionLabel]=\"t('actions.retry')\"\n actionIcon=\"arrow.refresh-cw-01\"\n (actionClick)=\"reload()\"\n />\n } @else if (facade.snapshot(); as snapshot) {\n <div class=\"flex min-w-0 flex-col gap-4\">\n <!-- Status and actions -->\n <div\n class=\"flex flex-wrap items-center justify-between gap-3 rounded-md border border-surface bg-content px-4 py-3\"\n >\n <div class=\"flex min-w-0 flex-wrap items-center gap-2 text-sm\">\n <span\n class=\"rounded-md border border-surface bg-surface-50 px-2 py-0.5 font-medium\"\n >\n {{ t(\"source.\" + snapshot.source) }}\n @if (snapshot.revision > 0) {\n \u00B7 {{ t(\"revision\", { revision: snapshot.revision }) }}\n }\n </span>\n @if (facade.dirty()) {\n <span\n class=\"rounded-md border border-amber-200 bg-amber-50 px-2 py-0.5 font-medium text-amber-700\"\n >\n {{ t(\"unsaved\") }}\n </span>\n }\n </div>\n <div class=\"flex flex-wrap items-center gap-2\">\n @if (snapshot.canRestore) {\n <mt-button\n icon=\"arrow.refresh-cw-01\"\n [label]=\"t('actions.restore')\"\n severity=\"secondary\"\n variant=\"outlined\"\n [loading]=\"facade.saving() === 'restore'\"\n [disabled]=\"facade.saving() !== null\"\n (onClick)=\"restorePrevious()\"\n />\n }\n <mt-button\n [label]=\"t('actions.discard')\"\n severity=\"secondary\"\n variant=\"text\"\n [disabled]=\"!facade.dirty() || facade.saving() !== null\"\n (onClick)=\"discard()\"\n />\n <mt-button\n icon=\"general.check\"\n [label]=\"t('actions.publish')\"\n [tooltip]=\"publishBlockedReason()\"\n [loading]=\"facade.saving() === 'publish'\"\n [disabled]=\"!canPublish()\"\n (onClick)=\"publish()\"\n />\n </div>\n </div>\n\n @if (facade.conflict() || baselineMoved()) {\n <div\n class=\"flex flex-wrap items-center justify-between gap-3 rounded-md border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-700\"\n role=\"alert\"\n >\n <span>{{ t(\"conflict\") }}</span>\n <div class=\"flex flex-wrap gap-2\">\n <mt-button\n [label]=\"t('actions.keep-mine')\"\n size=\"small\"\n severity=\"secondary\"\n variant=\"outlined\"\n (onClick)=\"resolveConflict(true)\"\n />\n <mt-button\n [label]=\"t('actions.load-latest')\"\n size=\"small\"\n severity=\"secondary\"\n (onClick)=\"resolveConflict(false)\"\n />\n </div>\n </div>\n }\n\n @if (blockingDiagnostics().length > 0) {\n <div\n class=\"rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\n role=\"alert\"\n >\n <p class=\"font-medium\">{{ t(\"blocked\") }}</p>\n <ul class=\"mt-1 list-disc ps-5\">\n @for (diagnostic of blockingDiagnostics(); track $index) {\n <li>{{ describeDiagnostic(diagnostic) }}</li>\n }\n </ul>\n </div>\n }\n\n @if (writeDiagnostics().length > 0) {\n <div\n class=\"rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\n role=\"alert\"\n >\n <ul class=\"list-disc ps-5\">\n @for (diagnostic of writeDiagnostics(); track $index) {\n <li>{{ describeDiagnostic(diagnostic) }}</li>\n }\n </ul>\n </div>\n }\n\n <div class=\"grid min-w-0 grid-cols-1 gap-4 xl:grid-cols-2\">\n <!-- HTML source -->\n <section\n class=\"flex min-w-0 flex-col overflow-hidden rounded-md border border-surface bg-content\"\n >\n <header\n class=\"flex items-center justify-between gap-2 border-b border-surface bg-surface-50 px-4 py-2.5\"\n >\n <h3 class=\"text-sm font-semibold\">{{ t(\"source-title\") }}</h3>\n @if (nearLimit()) {\n <span\n class=\"text-xs\"\n [class.text-red-600]=\"overLimit()\"\n [class.text-amber-700]=\"!overLimit()\"\n >{{ sizeLabel() }}</span\n >\n }\n </header>\n <div class=\"flex flex-col gap-2 p-4\">\n <mt-email-visual-editor\n [formControl]=\"sourceControl\"\n [label]=\"t('source-label')\"\n [hint]=\"t('source-hint')\"\n [layout]=\"true\"\n [approvedImageOrigins]=\"snapshot.approvedImageOrigins\"\n height=\"34rem\"\n />\n @if (slotCount() !== 1) {\n <p class=\"text-sm text-red-600\" role=\"alert\">\n {{ t(slotCount() === 0 ? \"slot-missing\" : \"slot-duplicate\") }}\n </p>\n }\n </div>\n </section>\n\n <!-- Live preview -->\n <section\n class=\"flex min-w-0 flex-col overflow-hidden rounded-md border border-surface bg-content\"\n >\n <header\n class=\"flex flex-wrap items-center justify-between gap-2 border-b border-surface bg-surface-50 px-4 py-2.5\"\n >\n <div class=\"flex items-center gap-2\">\n <h3 class=\"text-sm font-semibold\">{{ t(\"preview-title\") }}</h3>\n <span class=\"text-xs text-muted-color\" aria-live=\"polite\">{{\n preview().status === \"updating\" ? t(\"preview-updating\") : \"\"\n }}</span>\n </div>\n <mt-toggle-field [formControl]=\"narrow\" [label]=\"t('narrow')\" />\n </header>\n <div class=\"flex flex-col gap-3 p-4\">\n @if (preview().status === \"error\") {\n <div\n class=\"flex flex-wrap items-center justify-between gap-3 rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\n role=\"alert\"\n >\n <span>{{ describeCode(previewErrorCode()) }}</span>\n <mt-button\n [label]=\"t('actions.retry')\"\n size=\"small\"\n severity=\"secondary\"\n variant=\"outlined\"\n (onClick)=\"retryPreview()\"\n />\n </div>\n }\n <!-- 39rem = the editor frame (34rem) plus its label and toolbar, so\n both columns end level. -->\n @if (rendered(); as rendered) {\n <div\n class=\"mx-auto w-full overflow-hidden rounded-md border border-surface transition-opacity\"\n [class.max-w-[375px]]=\"narrowValue()\"\n [class.opacity-60]=\"!freshPreview()\"\n >\n <mt-email-html-preview\n [html]=\"rendered.html\"\n [showImages]=\"true\"\n [approvedImageOrigins]=\"rendered.approvedImageOrigins\"\n [title]=\"t('preview-title')\"\n height=\"39rem\"\n />\n </div>\n @if (rendered.diagnostics.length > 0) {\n <ul class=\"list-disc ps-5 text-sm text-amber-700\">\n @for (diagnostic of rendered.diagnostics; track $index) {\n <li>{{ describeDiagnostic(diagnostic) }}</li>\n }\n </ul>\n }\n } @else if (preview().status !== \"error\") {\n <p-skeleton width=\"100%\" height=\"39rem\" borderRadius=\"0.375rem\" />\n }\n </div>\n </section>\n </div>\n </div>\n }\n</ng-container>\n", dependencies: [{ kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: EmptyState, selector: "mt-empty-state", inputs: ["variant", "illustration", "title", "description", "actionLabel", "actionIcon"], outputs: ["actionClick"] }, { kind: "component", type: EmailVisualEditor, selector: "mt-email-visual-editor", inputs: ["label", "hint", "placeholders", "layout", "height", "direction", "starter", "approvedImageOrigins"], outputs: ["focused"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "component", type: Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: EmailHtmlPreview, selector: "mt-email-html-preview", inputs: ["html", "showImages", "approvedImageOrigins", "fragment", "direction", "title", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2279
+ }
2280
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryDesign, decorators: [{
2281
+ type: Component,
2282
+ args: [{ selector: 'mt-email-delivery-design', imports: [
2283
+ TranslocoDirective,
2284
+ ReactiveFormsModule,
2285
+ Button,
2286
+ EmptyState,
2287
+ EmailVisualEditor,
2288
+ ToggleField,
2289
+ Skeleton,
2290
+ EmailHtmlPreview,
2291
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery.design'\">\n @if (facade.loading() && !facade.snapshot()) {\n <div class=\"grid grid-cols-1 gap-4 xl:grid-cols-2\" aria-live=\"polite\">\n <p-skeleton width=\"100%\" height=\"32rem\" borderRadius=\"0.375rem\" />\n <p-skeleton width=\"100%\" height=\"32rem\" borderRadius=\"0.375rem\" />\n </div>\n } @else if (facade.loadFailed() && !facade.snapshot()) {\n <mt-empty-state\n illustration=\"none\"\n [title]=\"t('load-error-title')\"\n [description]=\"describeCode(facade.loadErrorCode())\"\n [actionLabel]=\"t('actions.retry')\"\n actionIcon=\"arrow.refresh-cw-01\"\n (actionClick)=\"reload()\"\n />\n } @else if (facade.snapshot(); as snapshot) {\n <div class=\"flex min-w-0 flex-col gap-4\">\n <!-- Status and actions -->\n <div\n class=\"flex flex-wrap items-center justify-between gap-3 rounded-md border border-surface bg-content px-4 py-3\"\n >\n <div class=\"flex min-w-0 flex-wrap items-center gap-2 text-sm\">\n <span\n class=\"rounded-md border border-surface bg-surface-50 px-2 py-0.5 font-medium\"\n >\n {{ t(\"source.\" + snapshot.source) }}\n @if (snapshot.revision > 0) {\n \u00B7 {{ t(\"revision\", { revision: snapshot.revision }) }}\n }\n </span>\n @if (facade.dirty()) {\n <span\n class=\"rounded-md border border-amber-200 bg-amber-50 px-2 py-0.5 font-medium text-amber-700\"\n >\n {{ t(\"unsaved\") }}\n </span>\n }\n </div>\n <div class=\"flex flex-wrap items-center gap-2\">\n @if (snapshot.canRestore) {\n <mt-button\n icon=\"arrow.refresh-cw-01\"\n [label]=\"t('actions.restore')\"\n severity=\"secondary\"\n variant=\"outlined\"\n [loading]=\"facade.saving() === 'restore'\"\n [disabled]=\"facade.saving() !== null\"\n (onClick)=\"restorePrevious()\"\n />\n }\n <mt-button\n [label]=\"t('actions.discard')\"\n severity=\"secondary\"\n variant=\"text\"\n [disabled]=\"!facade.dirty() || facade.saving() !== null\"\n (onClick)=\"discard()\"\n />\n <mt-button\n icon=\"general.check\"\n [label]=\"t('actions.publish')\"\n [tooltip]=\"publishBlockedReason()\"\n [loading]=\"facade.saving() === 'publish'\"\n [disabled]=\"!canPublish()\"\n (onClick)=\"publish()\"\n />\n </div>\n </div>\n\n @if (facade.conflict() || baselineMoved()) {\n <div\n class=\"flex flex-wrap items-center justify-between gap-3 rounded-md border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-700\"\n role=\"alert\"\n >\n <span>{{ t(\"conflict\") }}</span>\n <div class=\"flex flex-wrap gap-2\">\n <mt-button\n [label]=\"t('actions.keep-mine')\"\n size=\"small\"\n severity=\"secondary\"\n variant=\"outlined\"\n (onClick)=\"resolveConflict(true)\"\n />\n <mt-button\n [label]=\"t('actions.load-latest')\"\n size=\"small\"\n severity=\"secondary\"\n (onClick)=\"resolveConflict(false)\"\n />\n </div>\n </div>\n }\n\n @if (blockingDiagnostics().length > 0) {\n <div\n class=\"rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\n role=\"alert\"\n >\n <p class=\"font-medium\">{{ t(\"blocked\") }}</p>\n <ul class=\"mt-1 list-disc ps-5\">\n @for (diagnostic of blockingDiagnostics(); track $index) {\n <li>{{ describeDiagnostic(diagnostic) }}</li>\n }\n </ul>\n </div>\n }\n\n @if (writeDiagnostics().length > 0) {\n <div\n class=\"rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\n role=\"alert\"\n >\n <ul class=\"list-disc ps-5\">\n @for (diagnostic of writeDiagnostics(); track $index) {\n <li>{{ describeDiagnostic(diagnostic) }}</li>\n }\n </ul>\n </div>\n }\n\n <div class=\"grid min-w-0 grid-cols-1 gap-4 xl:grid-cols-2\">\n <!-- HTML source -->\n <section\n class=\"flex min-w-0 flex-col overflow-hidden rounded-md border border-surface bg-content\"\n >\n <header\n class=\"flex items-center justify-between gap-2 border-b border-surface bg-surface-50 px-4 py-2.5\"\n >\n <h3 class=\"text-sm font-semibold\">{{ t(\"source-title\") }}</h3>\n @if (nearLimit()) {\n <span\n class=\"text-xs\"\n [class.text-red-600]=\"overLimit()\"\n [class.text-amber-700]=\"!overLimit()\"\n >{{ sizeLabel() }}</span\n >\n }\n </header>\n <div class=\"flex flex-col gap-2 p-4\">\n <mt-email-visual-editor\n [formControl]=\"sourceControl\"\n [label]=\"t('source-label')\"\n [hint]=\"t('source-hint')\"\n [layout]=\"true\"\n [approvedImageOrigins]=\"snapshot.approvedImageOrigins\"\n height=\"34rem\"\n />\n @if (slotCount() !== 1) {\n <p class=\"text-sm text-red-600\" role=\"alert\">\n {{ t(slotCount() === 0 ? \"slot-missing\" : \"slot-duplicate\") }}\n </p>\n }\n </div>\n </section>\n\n <!-- Live preview -->\n <section\n class=\"flex min-w-0 flex-col overflow-hidden rounded-md border border-surface bg-content\"\n >\n <header\n class=\"flex flex-wrap items-center justify-between gap-2 border-b border-surface bg-surface-50 px-4 py-2.5\"\n >\n <div class=\"flex items-center gap-2\">\n <h3 class=\"text-sm font-semibold\">{{ t(\"preview-title\") }}</h3>\n <span class=\"text-xs text-muted-color\" aria-live=\"polite\">{{\n preview().status === \"updating\" ? t(\"preview-updating\") : \"\"\n }}</span>\n </div>\n <mt-toggle-field [formControl]=\"narrow\" [label]=\"t('narrow')\" />\n </header>\n <div class=\"flex flex-col gap-3 p-4\">\n @if (preview().status === \"error\") {\n <div\n class=\"flex flex-wrap items-center justify-between gap-3 rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\"\n role=\"alert\"\n >\n <span>{{ describeCode(previewErrorCode()) }}</span>\n <mt-button\n [label]=\"t('actions.retry')\"\n size=\"small\"\n severity=\"secondary\"\n variant=\"outlined\"\n (onClick)=\"retryPreview()\"\n />\n </div>\n }\n <!-- 39rem = the editor frame (34rem) plus its label and toolbar, so\n both columns end level. -->\n @if (rendered(); as rendered) {\n <div\n class=\"mx-auto w-full overflow-hidden rounded-md border border-surface transition-opacity\"\n [class.max-w-[375px]]=\"narrowValue()\"\n [class.opacity-60]=\"!freshPreview()\"\n >\n <mt-email-html-preview\n [html]=\"rendered.html\"\n [showImages]=\"true\"\n [approvedImageOrigins]=\"rendered.approvedImageOrigins\"\n [title]=\"t('preview-title')\"\n height=\"39rem\"\n />\n </div>\n @if (rendered.diagnostics.length > 0) {\n <ul class=\"list-disc ps-5 text-sm text-amber-700\">\n @for (diagnostic of rendered.diagnostics; track $index) {\n <li>{{ describeDiagnostic(diagnostic) }}</li>\n }\n </ul>\n }\n } @else if (preview().status !== \"error\") {\n <p-skeleton width=\"100%\" height=\"39rem\" borderRadius=\"0.375rem\" />\n }\n </div>\n </section>\n </div>\n </div>\n }\n</ng-container>\n" }]
2292
+ }], ctorParameters: () => [], propDecorators: { onBeforeUnload: [{
2293
+ type: HostListener,
2294
+ args: ['window:beforeunload', ['$event']]
2295
+ }] } });
2296
+
2297
+ const EMAIL_DELIVERY_TABS = [
2298
+ 'connections',
2299
+ 'activity',
2300
+ 'design',
2301
+ ];
2064
2302
  class EmailDeliveryShell {
2065
2303
  route = inject(ActivatedRoute);
2066
2304
  router = inject(Router);
@@ -2077,6 +2315,7 @@ class EmailDeliveryShell {
2077
2315
  return [
2078
2316
  { label: this.t('tabs.connections'), value: 'connections' },
2079
2317
  { label: this.t('tabs.activity'), value: 'activity' },
2318
+ { label: this.t('tabs.design'), value: 'design' },
2080
2319
  ];
2081
2320
  }, ...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
2082
2321
  /**
@@ -2118,7 +2357,7 @@ class EmailDeliveryShell {
2118
2357
  }, ...(ngDevMode ? [{ debugName: "readinessHint" }] : /* istanbul ignore next */ []));
2119
2358
  constructor() {
2120
2359
  this.route.queryParamMap.pipe(takeUntilDestroyed()).subscribe((params) => {
2121
- this.activeTab.set(params.get('tab') === 'activity' ? 'activity' : 'connections');
2360
+ this.activeTab.set(toTab(params.get('tab')));
2122
2361
  });
2123
2362
  }
2124
2363
  ngOnInit() {
@@ -2144,7 +2383,7 @@ class EmailDeliveryShell {
2144
2383
  });
2145
2384
  }
2146
2385
  onTabChange(value) {
2147
- if (value !== 'connections' && value !== 'activity')
2386
+ if (!EMAIL_DELIVERY_TABS.includes(value))
2148
2387
  return;
2149
2388
  void this.router.navigate([], {
2150
2389
  relativeTo: this.route,
@@ -2156,9 +2395,7 @@ class EmailDeliveryShell {
2156
2395
  void this.router.navigate([PRODUCT_SETTINGS_ROUTE]);
2157
2396
  }
2158
2397
  syncTabFromUrl() {
2159
- this.activeTab.set(this.route.snapshot.queryParamMap.get('tab') === 'activity'
2160
- ? 'activity'
2161
- : 'connections');
2398
+ this.activeTab.set(toTab(this.route.snapshot.queryParamMap.get('tab')));
2162
2399
  }
2163
2400
  handleAuthorizationCallback() {
2164
2401
  const query = this.route.snapshot.queryParamMap;
@@ -2194,7 +2431,7 @@ class EmailDeliveryShell {
2194
2431
  return this.transloco.translate(`email-delivery.${key}`);
2195
2432
  }
2196
2433
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
2197
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryShell, isStandalone: true, selector: "mt-email-delivery-shell", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\r\n <mt-page\r\n [title]=\"t('title')\"\r\n avatarIcon=\"communication.mail-01\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-blue-100)',\r\n '--p-avatar-color': 'var(--p-blue-700)',\r\n }\"\r\n [tabs]=\"canView() ? tabs() : []\"\r\n [activeTab]=\"activeTab()\"\r\n contentClass=\"min-h-0 max-[1025px]:p-4 max-[640px]:p-3\"\r\n backButton\r\n (backButtonClick)=\"goBack()\"\r\n (tabChange)=\"onTabChange($event)\"\r\n >\r\n <!-- Declared unconditionally: a content query cannot reach a template that\r\n only exists inside a control-flow block, so the condition lives here. -->\r\n <ng-template #titleEnd>\r\n @if (canView() && readiness(); as state) {\r\n <span class=\"shrink-0\" [mtTooltip]=\"readinessHint()\">\r\n <mt-email-delivery-status kind=\"readiness\" [value]=\"state\" />\r\n </span>\r\n }\r\n </ng-template>\r\n\r\n @if (facade.permissionsLoading()) {\r\n <div class=\"p-6 text-sm text-muted-color\" aria-live=\"polite\">\r\n {{ t(\"access-checking\") }}\r\n </div>\r\n } @else if (canView()) {\r\n <div\r\n class=\"flex min-h-0 flex-col gap-4 [&_.mt-actions-column]:w-[5rem] [&_mt-card]:rounded-md [&_mt-table_:has(>.p-datatable)]:rounded-md\"\r\n >\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems()\"\r\n styleClass=\"mx-1 flex justify-start\"\r\n />\r\n\r\n @if (activeTab() === \"activity\") {\r\n <mt-email-delivery-activity />\r\n } @else {\r\n <mt-email-delivery-connections />\r\n }\r\n </div>\r\n } @else if (facade.permissionsError()) {\r\n <!-- A failed access read is not a denial; say so and offer the retry,\r\n otherwise a transient 500 reads as \"you do not have access\". -->\r\n <mt-empty-state\r\n illustration=\"none\"\r\n [title]=\"t('access-error-title')\"\r\n [description]=\"t('access-error-description')\"\r\n [actionLabel]=\"t('access-retry')\"\r\n actionIcon=\"arrow.refresh-cw-01\"\r\n (actionClick)=\"retryPermissions()\"\r\n />\r\n } @else {\r\n <mt-empty-state\r\n illustration=\"none\"\r\n [title]=\"t('access-denied-title')\"\r\n [description]=\"t('access-denied-description')\"\r\n />\r\n }\r\n </mt-page>\r\n</ng-container>\r\n", dependencies: [{ kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }, { kind: "component", type: EmptyState, selector: "mt-empty-state", inputs: ["variant", "illustration", "title", "description", "actionLabel", "actionIcon"], outputs: ["actionClick"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "component", type: EmailDeliveryStatus, selector: "mt-email-delivery-status", inputs: ["value", "kind"] }, { kind: "component", type: EmailDeliveryConnections, selector: "mt-email-delivery-connections" }, { kind: "component", type: EmailDeliveryActivity, selector: "mt-email-delivery-activity" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2434
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: EmailDeliveryShell, isStandalone: true, selector: "mt-email-delivery-shell", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\n <mt-page\n [title]=\"t('title')\"\n avatarIcon=\"communication.mail-01\"\n [avatarStyle]=\"{\n '--p-avatar-background': 'var(--p-blue-100)',\n '--p-avatar-color': 'var(--p-blue-700)',\n }\"\n [tabs]=\"canView() ? tabs() : []\"\n [activeTab]=\"activeTab()\"\n contentClass=\"min-h-0 max-[1025px]:p-4 max-[640px]:p-3\"\n backButton\n (backButtonClick)=\"goBack()\"\n (tabChange)=\"onTabChange($event)\"\n >\n <!-- Declared unconditionally: a content query cannot reach a template that\n only exists inside a control-flow block, so the condition lives here. -->\n <ng-template #titleEnd>\n @if (canView() && readiness(); as state) {\n <span class=\"shrink-0\" [mtTooltip]=\"readinessHint()\">\n <mt-email-delivery-status kind=\"readiness\" [value]=\"state\" />\n </span>\n }\n </ng-template>\n\n @if (facade.permissionsLoading()) {\n <div class=\"p-6 text-sm text-muted-color\" aria-live=\"polite\">\n {{ t(\"access-checking\") }}\n </div>\n } @else if (canView()) {\n <div\n class=\"flex min-h-0 flex-col gap-4 [&_.mt-actions-column]:w-[5rem] [&_mt-card]:rounded-md [&_mt-table_:has(>.p-datatable)]:rounded-md\"\n >\n <mt-breadcrumb\n [items]=\"breadcrumbItems()\"\n styleClass=\"mx-1 flex justify-start\"\n />\n\n @if (activeTab() === \"activity\") {\n <mt-email-delivery-activity />\n } @else if (activeTab() === \"design\") {\n <mt-email-delivery-design />\n } @else {\n <mt-email-delivery-connections />\n }\n </div>\n } @else if (facade.permissionsError()) {\n <!-- A failed access read is not a denial; say so and offer the retry,\n otherwise a transient 500 reads as \"you do not have access\". -->\n <mt-empty-state\n illustration=\"none\"\n [title]=\"t('access-error-title')\"\n [description]=\"t('access-error-description')\"\n [actionLabel]=\"t('access-retry')\"\n actionIcon=\"arrow.refresh-cw-01\"\n (actionClick)=\"retryPermissions()\"\n />\n } @else {\n <mt-empty-state\n illustration=\"none\"\n [title]=\"t('access-denied-title')\"\n [description]=\"t('access-denied-description')\"\n />\n }\n </mt-page>\n</ng-container>\n", dependencies: [{ kind: "component", type: Page, selector: "mt-page", inputs: ["backButton", "backButtonIcon", "avatarIcon", "avatarStyle", "avatarShape", "title", "tabs", "activeTab", "contentClass", "contentId"], outputs: ["backButtonClick", "tabChange"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }, { kind: "component", type: EmptyState, selector: "mt-empty-state", inputs: ["variant", "illustration", "title", "description", "actionLabel", "actionIcon"], outputs: ["actionClick"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "component", type: EmailDeliveryStatus, selector: "mt-email-delivery-status", inputs: ["value", "kind"] }, { kind: "component", type: EmailDeliveryConnections, selector: "mt-email-delivery-connections" }, { kind: "component", type: EmailDeliveryActivity, selector: "mt-email-delivery-activity" }, { kind: "component", type: EmailDeliveryDesign, selector: "mt-email-delivery-design" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2198
2435
  }
2199
2436
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryShell, decorators: [{
2200
2437
  type: Component,
@@ -2207,9 +2444,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
2207
2444
  EmailDeliveryStatus,
2208
2445
  EmailDeliveryConnections,
2209
2446
  EmailDeliveryActivity,
2210
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\r\n <mt-page\r\n [title]=\"t('title')\"\r\n avatarIcon=\"communication.mail-01\"\r\n [avatarStyle]=\"{\r\n '--p-avatar-background': 'var(--p-blue-100)',\r\n '--p-avatar-color': 'var(--p-blue-700)',\r\n }\"\r\n [tabs]=\"canView() ? tabs() : []\"\r\n [activeTab]=\"activeTab()\"\r\n contentClass=\"min-h-0 max-[1025px]:p-4 max-[640px]:p-3\"\r\n backButton\r\n (backButtonClick)=\"goBack()\"\r\n (tabChange)=\"onTabChange($event)\"\r\n >\r\n <!-- Declared unconditionally: a content query cannot reach a template that\r\n only exists inside a control-flow block, so the condition lives here. -->\r\n <ng-template #titleEnd>\r\n @if (canView() && readiness(); as state) {\r\n <span class=\"shrink-0\" [mtTooltip]=\"readinessHint()\">\r\n <mt-email-delivery-status kind=\"readiness\" [value]=\"state\" />\r\n </span>\r\n }\r\n </ng-template>\r\n\r\n @if (facade.permissionsLoading()) {\r\n <div class=\"p-6 text-sm text-muted-color\" aria-live=\"polite\">\r\n {{ t(\"access-checking\") }}\r\n </div>\r\n } @else if (canView()) {\r\n <div\r\n class=\"flex min-h-0 flex-col gap-4 [&_.mt-actions-column]:w-[5rem] [&_mt-card]:rounded-md [&_mt-table_:has(>.p-datatable)]:rounded-md\"\r\n >\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems()\"\r\n styleClass=\"mx-1 flex justify-start\"\r\n />\r\n\r\n @if (activeTab() === \"activity\") {\r\n <mt-email-delivery-activity />\r\n } @else {\r\n <mt-email-delivery-connections />\r\n }\r\n </div>\r\n } @else if (facade.permissionsError()) {\r\n <!-- A failed access read is not a denial; say so and offer the retry,\r\n otherwise a transient 500 reads as \"you do not have access\". -->\r\n <mt-empty-state\r\n illustration=\"none\"\r\n [title]=\"t('access-error-title')\"\r\n [description]=\"t('access-error-description')\"\r\n [actionLabel]=\"t('access-retry')\"\r\n actionIcon=\"arrow.refresh-cw-01\"\r\n (actionClick)=\"retryPermissions()\"\r\n />\r\n } @else {\r\n <mt-empty-state\r\n illustration=\"none\"\r\n [title]=\"t('access-denied-title')\"\r\n [description]=\"t('access-denied-description')\"\r\n />\r\n }\r\n </mt-page>\r\n</ng-container>\r\n" }]
2447
+ EmailDeliveryDesign,
2448
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *transloco=\"let t; prefix: 'email-delivery'\">\n <mt-page\n [title]=\"t('title')\"\n avatarIcon=\"communication.mail-01\"\n [avatarStyle]=\"{\n '--p-avatar-background': 'var(--p-blue-100)',\n '--p-avatar-color': 'var(--p-blue-700)',\n }\"\n [tabs]=\"canView() ? tabs() : []\"\n [activeTab]=\"activeTab()\"\n contentClass=\"min-h-0 max-[1025px]:p-4 max-[640px]:p-3\"\n backButton\n (backButtonClick)=\"goBack()\"\n (tabChange)=\"onTabChange($event)\"\n >\n <!-- Declared unconditionally: a content query cannot reach a template that\n only exists inside a control-flow block, so the condition lives here. -->\n <ng-template #titleEnd>\n @if (canView() && readiness(); as state) {\n <span class=\"shrink-0\" [mtTooltip]=\"readinessHint()\">\n <mt-email-delivery-status kind=\"readiness\" [value]=\"state\" />\n </span>\n }\n </ng-template>\n\n @if (facade.permissionsLoading()) {\n <div class=\"p-6 text-sm text-muted-color\" aria-live=\"polite\">\n {{ t(\"access-checking\") }}\n </div>\n } @else if (canView()) {\n <div\n class=\"flex min-h-0 flex-col gap-4 [&_.mt-actions-column]:w-[5rem] [&_mt-card]:rounded-md [&_mt-table_:has(>.p-datatable)]:rounded-md\"\n >\n <mt-breadcrumb\n [items]=\"breadcrumbItems()\"\n styleClass=\"mx-1 flex justify-start\"\n />\n\n @if (activeTab() === \"activity\") {\n <mt-email-delivery-activity />\n } @else if (activeTab() === \"design\") {\n <mt-email-delivery-design />\n } @else {\n <mt-email-delivery-connections />\n }\n </div>\n } @else if (facade.permissionsError()) {\n <!-- A failed access read is not a denial; say so and offer the retry,\n otherwise a transient 500 reads as \"you do not have access\". -->\n <mt-empty-state\n illustration=\"none\"\n [title]=\"t('access-error-title')\"\n [description]=\"t('access-error-description')\"\n [actionLabel]=\"t('access-retry')\"\n actionIcon=\"arrow.refresh-cw-01\"\n (actionClick)=\"retryPermissions()\"\n />\n } @else {\n <mt-empty-state\n illustration=\"none\"\n [title]=\"t('access-denied-title')\"\n [description]=\"t('access-denied-description')\"\n />\n }\n </mt-page>\n</ng-container>\n" }]
2211
2449
  }], ctorParameters: () => [] });
2212
2450
  const PRODUCT_SETTINGS_ROUTE = '/control-panel/product-settings';
2451
+ function toTab(value) {
2452
+ return EMAIL_DELIVERY_TABS.includes(value)
2453
+ ? value
2454
+ : 'connections';
2455
+ }
2213
2456
 
2214
2457
  export { EmailDeliveryShell };
2215
- //# sourceMappingURL=masterteam-system-settings-email-delivery-shell-CR2wsIif.mjs.map
2458
+ //# sourceMappingURL=masterteam-system-settings-email-delivery-shell-D9NShoTc.mjs.map