@masterteam/system-settings 0.0.17 → 0.0.18
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.
- package/README.md +23 -6
- package/assets/i18n/ar.json +29 -0
- package/assets/i18n/en.json +29 -0
- package/assets/system-settings.css +1 -1
- package/fesm2022/{masterteam-system-settings-email-delivery-shell-C_AhRF1f.mjs → masterteam-system-settings-email-delivery-shell-Bjwtj5Ic.mjs} +36 -759
- package/fesm2022/masterteam-system-settings-email-delivery-shell-Bjwtj5Ic.mjs.map +1 -0
- package/fesm2022/masterteam-system-settings-masterteam-system-settings-CO-XTJOi.mjs +2707 -0
- package/fesm2022/masterteam-system-settings-masterteam-system-settings-CO-XTJOi.mjs.map +1 -0
- package/fesm2022/masterteam-system-settings.mjs +1 -1588
- package/fesm2022/masterteam-system-settings.mjs.map +1 -1
- package/package.json +4 -4
- package/types/masterteam-system-settings.d.ts +210 -2
- package/fesm2022/masterteam-system-settings-email-delivery-shell-C_AhRF1f.mjs.map +0 -1
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, inject,
|
|
2
|
+
import { input, inject, ChangeDetectionStrategy, Component, signal, computed, DestroyRef } from '@angular/core';
|
|
3
3
|
import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
4
|
import { ActivatedRoute, Router } from '@angular/router';
|
|
5
|
-
import {
|
|
5
|
+
import { TranslocoDirective, TranslocoService } from '@jsverse/transloco';
|
|
6
6
|
import { Breadcrumb } from '@masterteam/components/breadcrumb';
|
|
7
7
|
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 {
|
|
12
|
-
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
|
13
|
-
import { mergeMap, of, throwError, catchError, map, switchMap, forkJoin, tap, finalize, take, timer } from 'rxjs';
|
|
14
|
-
import { EMAIL_DELIVERY_OPTIONS, isConnectionTestAttempt, EMAIL_DELIVERY_ATTEMPT_STATUSES, EMAIL_DELIVERY_PERMISSIONS } from './masterteam-system-settings.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 } from './masterteam-system-settings-masterteam-system-settings-CO-XTJOi.mjs';
|
|
15
12
|
import { Icon } from '@masterteam/icons';
|
|
16
13
|
import { Button } from '@masterteam/components/button';
|
|
17
14
|
import { ModalService } from '@masterteam/components/modal';
|
|
18
15
|
import { Table } from '@masterteam/components/table';
|
|
16
|
+
import { take, timer, of, map, switchMap, finalize } from 'rxjs';
|
|
19
17
|
import { DatePipe, DOCUMENT } from '@angular/common';
|
|
20
18
|
import { ModalRef } from '@masterteam/components/dialog';
|
|
21
19
|
import * as i1 from '@angular/forms';
|
|
@@ -23,724 +21,13 @@ import { FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
|
23
21
|
import { TextareaField } from '@masterteam/components/textarea-field';
|
|
24
22
|
import { ToggleField } from '@masterteam/components/toggle-field';
|
|
25
23
|
import { ConfirmationService } from '@masterteam/components/confirmation';
|
|
24
|
+
import { HttpErrorResponse } from '@angular/common/http';
|
|
26
25
|
import { TextareaFieldConfig, TextFieldConfig, ToggleFieldConfig, ValidatorConfig, SelectFieldConfig, RadioButtonFieldConfig, NumberFieldConfig } from '@masterteam/components';
|
|
27
26
|
import { DynamicForm } from '@masterteam/forms/dynamic-form';
|
|
28
27
|
import { Menu } from '@masterteam/components/menu';
|
|
29
28
|
import * as i1$1 from 'primeng/skeleton';
|
|
30
29
|
import { SkeletonModule } from 'primeng/skeleton';
|
|
31
30
|
|
|
32
|
-
const STATUS_DEFINITIONS = {
|
|
33
|
-
// Page-level readiness, shown as a chip beside the page title instead of the
|
|
34
|
-
// banner that used to sit above every tab.
|
|
35
|
-
readiness: {
|
|
36
|
-
ready: status('#059669', 'readiness.ready'),
|
|
37
|
-
'needs-setup': status('#D97706', 'readiness.needs-setup'),
|
|
38
|
-
unavailable: status('#64748B', 'readiness.unavailable'),
|
|
39
|
-
},
|
|
40
|
-
connection: {
|
|
41
|
-
Draft: status('#64748B', 'connection.draft'),
|
|
42
|
-
PendingAuthorization: status('#D97706', 'connection.pending-authorization'),
|
|
43
|
-
PendingValidation: status('#0284C7', 'connection.pending-validation'),
|
|
44
|
-
Ready: status('#059669', 'connection.ready'),
|
|
45
|
-
ReauthorizationRequired: status('#EA580C', 'connection.reauthorization-required'),
|
|
46
|
-
ConditionalAccessRequired: status('#EA580C', 'connection.conditional-access-required'),
|
|
47
|
-
Disabled: status('#64748B', 'connection.disabled'),
|
|
48
|
-
Invalid: status('#DC2626', 'connection.invalid'),
|
|
49
|
-
},
|
|
50
|
-
attempt: {
|
|
51
|
-
Reserved: status('#64748B', 'attempt.reserved'),
|
|
52
|
-
ProviderInvocationStarted: status('#0284C7', 'attempt.provider-invocation-started'),
|
|
53
|
-
ProviderInvocationDefinitivelyNotAccepted: status('#DC2626', 'attempt.provider-not-accepted'),
|
|
54
|
-
ProviderResultReconciliation: status('#7C3AED', 'attempt.reconciling'),
|
|
55
|
-
ProviderResultPending: status('#0284C7', 'attempt.pending'),
|
|
56
|
-
Blocked: status('#DC2626', 'attempt.blocked'),
|
|
57
|
-
RetryScheduled: status('#D97706', 'attempt.retry-scheduled'),
|
|
58
|
-
Accepted: status('#059669', 'attempt.accepted'),
|
|
59
|
-
OutcomeUnknown: status('#EA580C', 'attempt.outcome-unknown'),
|
|
60
|
-
FailedPermanent: status('#DC2626', 'attempt.failed-permanent'),
|
|
61
|
-
DeadLettered: status('#BE123C', 'attempt.dead-lettered'),
|
|
62
|
-
},
|
|
63
|
-
support: {
|
|
64
|
-
active: status('#D97706', 'support.active'),
|
|
65
|
-
'repair-pending': status('#0284C7', 'support.repair-pending'),
|
|
66
|
-
repaired: status('#059669', 'support.repaired'),
|
|
67
|
-
'acknowledgement-pending': status('#0284C7', 'support.acknowledgement-pending'),
|
|
68
|
-
acknowledged: status('#64748B', 'support.acknowledged'),
|
|
69
|
-
Owned: status('#059669', 'support.owned'),
|
|
70
|
-
owned: status('#059669', 'support.owned'),
|
|
71
|
-
Missing: status('#D97706', 'support.missing'),
|
|
72
|
-
missing: status('#D97706', 'support.missing'),
|
|
73
|
-
Conflict: status('#DC2626', 'support.conflict'),
|
|
74
|
-
conflict: status('#DC2626', 'support.conflict'),
|
|
75
|
-
IdentityIncomplete: status('#EA580C', 'support.identity-incomplete'),
|
|
76
|
-
identityincomplete: status('#EA580C', 'support.identity-incomplete'),
|
|
77
|
-
'retryable-after-upgrade': status('#0284C7', 'support.retryable-after-upgrade'),
|
|
78
|
-
'repair-required': status('#EA580C', 'support.repair-required'),
|
|
79
|
-
'acknowledge-only': status('#64748B', 'support.acknowledge-only'),
|
|
80
|
-
configured: status('#059669', 'support.configured'),
|
|
81
|
-
'not-configured': status('#64748B', 'support.not-configured'),
|
|
82
|
-
},
|
|
83
|
-
};
|
|
84
|
-
class EmailDeliveryStatus {
|
|
85
|
-
value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
86
|
-
kind = input('support', ...(ngDevMode ? [{ debugName: "kind" }] : /* istanbul ignore next */ []));
|
|
87
|
-
transloco = inject(TranslocoService);
|
|
88
|
-
activeLanguage = toSignal(this.transloco.langChanges$, {
|
|
89
|
-
initialValue: this.transloco.getActiveLang(),
|
|
90
|
-
});
|
|
91
|
-
statusData = computed(() => {
|
|
92
|
-
this.activeLanguage();
|
|
93
|
-
return {
|
|
94
|
-
name: '',
|
|
95
|
-
viewType: 'Status',
|
|
96
|
-
value: getEmailDeliveryStatusValue(this.kind(), this.value(), (key) => this.transloco.translate(key)),
|
|
97
|
-
configuration: { hideName: true },
|
|
98
|
-
};
|
|
99
|
-
}, ...(ngDevMode ? [{ debugName: "statusData" }] : /* istanbul ignore next */ []));
|
|
100
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryStatus, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
101
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.8", type: EmailDeliveryStatus, isStandalone: true, selector: "mt-email-delivery-status", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, kind: { classPropertyName: "kind", publicName: "kind", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "inline-flex" }, ngImport: i0, template: `<mt-entity-status [data]="statusData()" />`, isInline: true, dependencies: [{ kind: "component", type: EntityStatus, selector: "mt-entity-status", inputs: ["data", "name", "value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
102
|
-
}
|
|
103
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryStatus, decorators: [{
|
|
104
|
-
type: Component,
|
|
105
|
-
args: [{
|
|
106
|
-
selector: 'mt-email-delivery-status',
|
|
107
|
-
imports: [EntityStatus],
|
|
108
|
-
template: `<mt-entity-status [data]="statusData()" />`,
|
|
109
|
-
host: { class: 'inline-flex' },
|
|
110
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
111
|
-
}]
|
|
112
|
-
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], kind: [{ type: i0.Input, args: [{ isSignal: true, alias: "kind", required: false }] }] } });
|
|
113
|
-
function getEmailDeliveryStatusMap(kind, translate) {
|
|
114
|
-
return Object.fromEntries(Object.entries(STATUS_DEFINITIONS[kind]).map(([key, definition]) => [
|
|
115
|
-
key,
|
|
116
|
-
{
|
|
117
|
-
color: definition.color,
|
|
118
|
-
display: translate(`email-delivery.status.${definition.translationKey}`),
|
|
119
|
-
},
|
|
120
|
-
]));
|
|
121
|
-
}
|
|
122
|
-
function getEmailDeliveryStatusValue(kind, value, translate) {
|
|
123
|
-
const definition = STATUS_DEFINITIONS[kind][value];
|
|
124
|
-
return {
|
|
125
|
-
key: value,
|
|
126
|
-
display: definition
|
|
127
|
-
? translate(`email-delivery.status.${definition.translationKey}`)
|
|
128
|
-
: value,
|
|
129
|
-
color: definition?.color ?? '#64748B',
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
function status(color, translationKey) {
|
|
133
|
-
return { color, translationKey };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
class EmailDeliveryApi {
|
|
137
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryApi, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
138
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryApi });
|
|
139
|
-
}
|
|
140
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryApi, decorators: [{
|
|
141
|
-
type: Injectable
|
|
142
|
-
}] });
|
|
143
|
-
|
|
144
|
-
class EmailDeliveryHttpApi extends EmailDeliveryApi {
|
|
145
|
-
http = inject(HttpClient);
|
|
146
|
-
options = inject(EMAIL_DELIVERY_OPTIONS);
|
|
147
|
-
getStatus() {
|
|
148
|
-
return this.http.get(this.url('status'));
|
|
149
|
-
}
|
|
150
|
-
getProviders() {
|
|
151
|
-
return this.http.get(this.url('providers'));
|
|
152
|
-
}
|
|
153
|
-
getErrorCatalog() {
|
|
154
|
-
return this.http.get(this.url('error-catalog'));
|
|
155
|
-
}
|
|
156
|
-
getConnections() {
|
|
157
|
-
return this.http.get(this.url('connections'));
|
|
158
|
-
}
|
|
159
|
-
getConnection(id) {
|
|
160
|
-
return this.http.get(this.url(`connections/${encodeURIComponent(id)}`));
|
|
161
|
-
}
|
|
162
|
-
createConnection(request) {
|
|
163
|
-
return this.http.post(this.url('connections'), request);
|
|
164
|
-
}
|
|
165
|
-
updateConnection(id, request) {
|
|
166
|
-
return this.http.put(this.url(`connections/${encodeURIComponent(id)}`), request);
|
|
167
|
-
}
|
|
168
|
-
deleteConnection(id, concurrencyToken) {
|
|
169
|
-
return this.http.delete(this.url(`connections/${encodeURIComponent(id)}`), { params: { concurrencyToken } });
|
|
170
|
-
}
|
|
171
|
-
bindConnection(purpose, connectionId) {
|
|
172
|
-
return this.http.put(this.url(`bindings/${encodeURIComponent(purpose)}`), { connectionId });
|
|
173
|
-
}
|
|
174
|
-
unbindConnection(purpose) {
|
|
175
|
-
return this.http.delete(this.url(`bindings/${encodeURIComponent(purpose)}`));
|
|
176
|
-
}
|
|
177
|
-
validateConnection(id) {
|
|
178
|
-
return this.http.post(this.url(`connections/${encodeURIComponent(id)}/validate`), null);
|
|
179
|
-
}
|
|
180
|
-
sendConnectionTest(id, request) {
|
|
181
|
-
return this.http
|
|
182
|
-
.post(this.url(`connections/${encodeURIComponent(id)}/test`), request, { observe: 'response' })
|
|
183
|
-
.pipe(mergeMap((response) => {
|
|
184
|
-
const body = response.body;
|
|
185
|
-
if (isEmailSendResult(body))
|
|
186
|
-
return of(body);
|
|
187
|
-
// 202 means the send was accepted without a result on this response.
|
|
188
|
-
// The caller must read the outcome back with the same testRequestId;
|
|
189
|
-
// posting again would deliver a second real email.
|
|
190
|
-
if (response.status === 202) {
|
|
191
|
-
return throwError(() => body ?? PENDING_CONTROLLED_TEST);
|
|
192
|
-
}
|
|
193
|
-
return throwError(() => body);
|
|
194
|
-
}), catchError((error) => {
|
|
195
|
-
const body = error instanceof HttpErrorResponse ? error.error : error;
|
|
196
|
-
return isEmailSendResult(body) ? of(body) : throwError(() => error);
|
|
197
|
-
}));
|
|
198
|
-
}
|
|
199
|
-
getConnectionTest(connectionId, testRequestId) {
|
|
200
|
-
return this.http.get(this.url(`connections/${encodeURIComponent(connectionId)}/test-requests/${encodeURIComponent(testRequestId)}`));
|
|
201
|
-
}
|
|
202
|
-
startDelegatedAuthorization(id) {
|
|
203
|
-
return this.http.post(this.url(`connections/${encodeURIComponent(id)}/authorization/delegated/start`), null);
|
|
204
|
-
}
|
|
205
|
-
startAdminConsent(id) {
|
|
206
|
-
return this.http.post(this.url(`connections/${encodeURIComponent(id)}/authorization/admin-consent/start`), null);
|
|
207
|
-
}
|
|
208
|
-
revokeAuthorization(id) {
|
|
209
|
-
return this.http.post(this.url(`connections/${encodeURIComponent(id)}/authorization/revoke`), null);
|
|
210
|
-
}
|
|
211
|
-
getAttempts(status, cursor, take) {
|
|
212
|
-
return this.http.get(this.url('attempts'), {
|
|
213
|
-
params: withoutNulls({ status, cursor, take }),
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
replayAttempt(id, request) {
|
|
217
|
-
return this.http.post(this.url(`attempts/${encodeURIComponent(id)}/replay`), request);
|
|
218
|
-
}
|
|
219
|
-
getTestRequest(testRequestId) {
|
|
220
|
-
return this.http.get(this.url(`test-requests/${encodeURIComponent(testRequestId)}`));
|
|
221
|
-
}
|
|
222
|
-
sendTemplateTest(eventId, templateId, request) {
|
|
223
|
-
return this.http
|
|
224
|
-
.post(this.notificationUrl(`events/${eventId}/templates/${templateId}/test-email`), request)
|
|
225
|
-
.pipe(map((response) => response.data));
|
|
226
|
-
}
|
|
227
|
-
sendAlertTest(alertId, request) {
|
|
228
|
-
return this.http
|
|
229
|
-
.post(this.notificationUrl(`alerts/${alertId}/test`), request)
|
|
230
|
-
.pipe(map((response) => response.data));
|
|
231
|
-
}
|
|
232
|
-
getFinalizationQuarantine(cursor, take) {
|
|
233
|
-
return this.http.get(this.url('finalization-quarantine'), { params: withoutNulls({ cursor, take }) });
|
|
234
|
-
}
|
|
235
|
-
retryFinalizationQuarantine(itemId, reason) {
|
|
236
|
-
return this.http.post(this.url(`finalization-quarantine/${encodeURIComponent(itemId)}/retry`), { reason });
|
|
237
|
-
}
|
|
238
|
-
acknowledgeFinalizationQuarantine(itemId, reason) {
|
|
239
|
-
return this.http.post(this.url(`finalization-quarantine/${encodeURIComponent(itemId)}/acknowledge`), { reason });
|
|
240
|
-
}
|
|
241
|
-
getHostEvidence(cursor, take) {
|
|
242
|
-
return this.http.get(this.url('orphan-evidence'), {
|
|
243
|
-
params: withoutNulls({ cursor, take }),
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
exportHostEvidence(evidenceOccurrenceId) {
|
|
247
|
-
return this.http
|
|
248
|
-
.get(this.url(`orphan-evidence/${encodeURIComponent(evidenceOccurrenceId)}/export`), {
|
|
249
|
-
observe: 'response',
|
|
250
|
-
responseType: 'blob',
|
|
251
|
-
})
|
|
252
|
-
.pipe(map((response) => toEvidenceDownload(response)));
|
|
253
|
-
}
|
|
254
|
-
repairHostEvidence(evidenceOccurrenceId, request) {
|
|
255
|
-
return this.http.post(this.url(`orphan-evidence/${encodeURIComponent(evidenceOccurrenceId)}/repair`), request);
|
|
256
|
-
}
|
|
257
|
-
acknowledgeHostEvidence(evidenceOccurrenceId, request) {
|
|
258
|
-
return this.http.post(this.url(`orphan-evidence/${encodeURIComponent(evidenceOccurrenceId)}/acknowledge`), request);
|
|
259
|
-
}
|
|
260
|
-
rebuildEvidenceIndex(take) {
|
|
261
|
-
return this.http.post(this.url('orphan-evidence/rebuild-index'), null, { params: { take } });
|
|
262
|
-
}
|
|
263
|
-
url(path) {
|
|
264
|
-
return joinPath(this.options.apiBasePath, path);
|
|
265
|
-
}
|
|
266
|
-
notificationUrl(path) {
|
|
267
|
-
return joinPath(this.options.notificationApiBasePath, path);
|
|
268
|
-
}
|
|
269
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryHttpApi, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
270
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryHttpApi, providedIn: 'root' });
|
|
271
|
-
}
|
|
272
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryHttpApi, decorators: [{
|
|
273
|
-
type: Injectable,
|
|
274
|
-
args: [{ providedIn: 'root' }]
|
|
275
|
-
}] });
|
|
276
|
-
/**
|
|
277
|
-
* Stand-in problem for a bare `202 Accepted` that carries no body of its own —
|
|
278
|
-
* it routes the dialog to retrieval by GUID, the same as the catalog problem
|
|
279
|
-
* the backend normally returns for an in-flight test.
|
|
280
|
-
*/
|
|
281
|
-
const PENDING_CONTROLLED_TEST = {
|
|
282
|
-
code: 'email.delivery_test_request_in_progress',
|
|
283
|
-
category: 'pending',
|
|
284
|
-
retryable: true,
|
|
285
|
-
action: 'poll_status',
|
|
286
|
-
readinessImpact: 'unchanged',
|
|
287
|
-
correlationId: '',
|
|
288
|
-
retryAfterSeconds: null,
|
|
289
|
-
};
|
|
290
|
-
function isEmailSendResult(value) {
|
|
291
|
-
return (typeof value === 'object' &&
|
|
292
|
-
value !== null &&
|
|
293
|
-
typeof value.outcome === 'string' &&
|
|
294
|
-
typeof value.code === 'string');
|
|
295
|
-
}
|
|
296
|
-
function withoutNulls(values) {
|
|
297
|
-
return Object.fromEntries(Object.entries(values).filter((entry) => entry[1] !== null));
|
|
298
|
-
}
|
|
299
|
-
function joinPath(base, path) {
|
|
300
|
-
return `${base.replace(/\/$/, '')}/${path.replace(/^\//, '')}`;
|
|
301
|
-
}
|
|
302
|
-
function toEvidenceDownload(response) {
|
|
303
|
-
const contentDisposition = response.headers.get('content-disposition') ?? '';
|
|
304
|
-
const fileNameMatch = /filename\*?=(?:UTF-8''|\")?([^\";]+)/i.exec(contentDisposition);
|
|
305
|
-
return {
|
|
306
|
-
blob: response.body ?? new Blob(),
|
|
307
|
-
fileName: fileNameMatch
|
|
308
|
-
? decodeURIComponent(fileNameMatch[1].replace(/\"$/, ''))
|
|
309
|
-
: 'email-delivery-evidence.json',
|
|
310
|
-
contentType: response.headers.get('content-type') ?? 'application/octet-stream',
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const PENDING_AUTHORIZATION_STORAGE_KEY = 'mt.email-delivery.pending-authorization-connection';
|
|
315
|
-
class EmailDeliveryFacade {
|
|
316
|
-
options = inject(EMAIL_DELIVERY_OPTIONS);
|
|
317
|
-
api = inject(EmailDeliveryHttpApi);
|
|
318
|
-
transloco = inject(TranslocoService);
|
|
319
|
-
toast = inject(ToastService);
|
|
320
|
-
initialized = false;
|
|
321
|
-
attemptsLoaded = false;
|
|
322
|
-
status = signal(null, ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
|
|
323
|
-
providers = signal([], ...(ngDevMode ? [{ debugName: "providers" }] : /* istanbul ignore next */ []));
|
|
324
|
-
errorCatalog = signal([], ...(ngDevMode ? [{ debugName: "errorCatalog" }] : /* istanbul ignore next */ []));
|
|
325
|
-
connections = signal([], ...(ngDevMode ? [{ debugName: "connections" }] : /* istanbul ignore next */ []));
|
|
326
|
-
selectedConnection = signal(null, ...(ngDevMode ? [{ debugName: "selectedConnection" }] : /* istanbul ignore next */ []));
|
|
327
|
-
attempts = signal([], ...(ngDevMode ? [{ debugName: "attempts" }] : /* istanbul ignore next */ []));
|
|
328
|
-
attemptsNextCursor = signal(null, ...(ngDevMode ? [{ debugName: "attemptsNextCursor" }] : /* istanbul ignore next */ []));
|
|
329
|
-
attemptStatusFilter = signal(null, ...(ngDevMode ? [{ debugName: "attemptStatusFilter" }] : /* istanbul ignore next */ []));
|
|
330
|
-
quarantine = signal([], ...(ngDevMode ? [{ debugName: "quarantine" }] : /* istanbul ignore next */ []));
|
|
331
|
-
quarantineNextCursor = signal(null, ...(ngDevMode ? [{ debugName: "quarantineNextCursor" }] : /* istanbul ignore next */ []));
|
|
332
|
-
hostEvidence = signal([], ...(ngDevMode ? [{ debugName: "hostEvidence" }] : /* istanbul ignore next */ []));
|
|
333
|
-
hostEvidenceNextCursor = signal(null, ...(ngDevMode ? [{ debugName: "hostEvidenceNextCursor" }] : /* istanbul ignore next */ []));
|
|
334
|
-
hostEvidenceScanBudgetExhausted = signal(false, ...(ngDevMode ? [{ debugName: "hostEvidenceScanBudgetExhausted" }] : /* istanbul ignore next */ []));
|
|
335
|
-
statusLoading = signal(false, ...(ngDevMode ? [{ debugName: "statusLoading" }] : /* istanbul ignore next */ []));
|
|
336
|
-
catalogLoading = signal(false, ...(ngDevMode ? [{ debugName: "catalogLoading" }] : /* istanbul ignore next */ []));
|
|
337
|
-
connectionsLoading = signal(false, ...(ngDevMode ? [{ debugName: "connectionsLoading" }] : /* istanbul ignore next */ []));
|
|
338
|
-
detailsLoading = signal(false, ...(ngDevMode ? [{ debugName: "detailsLoading" }] : /* istanbul ignore next */ []));
|
|
339
|
-
attemptsLoading = signal(false, ...(ngDevMode ? [{ debugName: "attemptsLoading" }] : /* istanbul ignore next */ []));
|
|
340
|
-
quarantineLoading = signal(false, ...(ngDevMode ? [{ debugName: "quarantineLoading" }] : /* istanbul ignore next */ []));
|
|
341
|
-
hostEvidenceLoading = signal(false, ...(ngDevMode ? [{ debugName: "hostEvidenceLoading" }] : /* istanbul ignore next */ []));
|
|
342
|
-
mutationLoading = signal(null, ...(ngDevMode ? [{ debugName: "mutationLoading" }] : /* istanbul ignore next */ []));
|
|
343
|
-
lastConnectionTest = signal(null, ...(ngDevMode ? [{ debugName: "lastConnectionTest" }] : /* istanbul ignore next */ []));
|
|
344
|
-
lastConnectionTestRequestId = signal(null, ...(ngDevMode ? [{ debugName: "lastConnectionTestRequestId" }] : /* istanbul ignore next */ []));
|
|
345
|
-
activeConnection = computed(() => this.connections().find((connection) => connection.bindings.includes('notifications')), ...(ngDevMode ? [{ debugName: "activeConnection" }] : /* istanbul ignore next */ []));
|
|
346
|
-
managementAvailable = computed(() => this.status()?.managementEnabled === true, ...(ngDevMode ? [{ debugName: "managementAvailable" }] : /* istanbul ignore next */ []));
|
|
347
|
-
initialize() {
|
|
348
|
-
if (this.initialized)
|
|
349
|
-
return;
|
|
350
|
-
this.initialized = true;
|
|
351
|
-
this.loadStatus()
|
|
352
|
-
.pipe(switchMap(() => forkJoin({
|
|
353
|
-
catalog: this.loadCatalog(),
|
|
354
|
-
connections: this.loadConnections(),
|
|
355
|
-
})))
|
|
356
|
-
.subscribe({ error: (error) => this.showError(error) });
|
|
357
|
-
}
|
|
358
|
-
hasPermission(permission) {
|
|
359
|
-
return this.options.permissionKeys.includes(permission);
|
|
360
|
-
}
|
|
361
|
-
loadStatus() {
|
|
362
|
-
this.statusLoading.set(true);
|
|
363
|
-
return this.api.getStatus().pipe(tap((status) => this.status.set(status)), finalize(() => this.statusLoading.set(false)));
|
|
364
|
-
}
|
|
365
|
-
loadCatalog() {
|
|
366
|
-
this.catalogLoading.set(true);
|
|
367
|
-
return forkJoin({
|
|
368
|
-
providers: this.api.getProviders(),
|
|
369
|
-
errors: this.api.getErrorCatalog(),
|
|
370
|
-
}).pipe(tap(({ providers, errors }) => {
|
|
371
|
-
this.providers.set(providers);
|
|
372
|
-
this.errorCatalog.set(errors);
|
|
373
|
-
}), finalize(() => this.catalogLoading.set(false)));
|
|
374
|
-
}
|
|
375
|
-
loadConnections() {
|
|
376
|
-
this.connectionsLoading.set(true);
|
|
377
|
-
return this.api.getConnections().pipe(tap((connections) => this.connections.set(connections)), finalize(() => this.connectionsLoading.set(false)));
|
|
378
|
-
}
|
|
379
|
-
loadConnection(id) {
|
|
380
|
-
this.detailsLoading.set(true);
|
|
381
|
-
return this.api.getConnection(id).pipe(tap((details) => this.selectedConnection.set(details)), finalize(() => this.detailsLoading.set(false)));
|
|
382
|
-
}
|
|
383
|
-
createConnection(request) {
|
|
384
|
-
this.mutationLoading.set('create');
|
|
385
|
-
return this.api.createConnection(request).pipe(switchMap((details) => this.refreshConnection(details.connection.id)), tap(() => this.showSuccess('connection-created')), finalize(() => this.mutationLoading.set(null)));
|
|
386
|
-
}
|
|
387
|
-
updateConnection(id, request) {
|
|
388
|
-
this.mutationLoading.set('update');
|
|
389
|
-
return this.api.updateConnection(id, request).pipe(this.reloadOnConflict(id), switchMap(() => this.refreshConnection(id)), tap(() => this.showSuccess('connection-updated')), finalize(() => this.mutationLoading.set(null)));
|
|
390
|
-
}
|
|
391
|
-
deleteConnection(id, concurrencyToken) {
|
|
392
|
-
this.mutationLoading.set('delete');
|
|
393
|
-
return this.api.deleteConnection(id, concurrencyToken).pipe(this.reloadOnConflict(id), switchMap(() => forkJoin({
|
|
394
|
-
connections: this.loadConnections(),
|
|
395
|
-
status: this.loadStatus(),
|
|
396
|
-
})), tap(() => {
|
|
397
|
-
this.selectedConnection.set(null);
|
|
398
|
-
this.showSuccess('connection-deleted');
|
|
399
|
-
}), map(() => undefined), finalize(() => this.mutationLoading.set(null)));
|
|
400
|
-
}
|
|
401
|
-
validateConnection(id) {
|
|
402
|
-
this.mutationLoading.set('validate');
|
|
403
|
-
return this.api.validateConnection(id).pipe(this.reloadOnConflict(id), switchMap((result) => this.refreshConnection(id).pipe(map(() => result))), tap((result) => result.isValid
|
|
404
|
-
? this.toast.success(this.describeCode(result.code))
|
|
405
|
-
: this.toast.warn(this.describeCode(result.code))), finalize(() => this.mutationLoading.set(null)));
|
|
406
|
-
}
|
|
407
|
-
sendConnectionTest(id, request) {
|
|
408
|
-
this.mutationLoading.set('test');
|
|
409
|
-
this.lastConnectionTestRequestId.set(request.testRequestId);
|
|
410
|
-
return this.api.sendConnectionTest(id, request).pipe(tap((result) => {
|
|
411
|
-
this.lastConnectionTest.set(result);
|
|
412
|
-
this.refreshActivity();
|
|
413
|
-
}), switchMap((result) => this.refreshConnection(id).pipe(map(() => result))), finalize(() => this.mutationLoading.set(null)));
|
|
414
|
-
}
|
|
415
|
-
retrieveConnectionTest(connectionId, testRequestId) {
|
|
416
|
-
this.mutationLoading.set('retrieve-test');
|
|
417
|
-
return this.api.getConnectionTest(connectionId, testRequestId).pipe(tap((stored) => {
|
|
418
|
-
this.lastConnectionTest.set(stored.result);
|
|
419
|
-
if (stored.result)
|
|
420
|
-
this.refreshActivity();
|
|
421
|
-
}), finalize(() => this.mutationLoading.set(null)));
|
|
422
|
-
}
|
|
423
|
-
bindConnection(id, purpose = 'notifications') {
|
|
424
|
-
this.mutationLoading.set('bind');
|
|
425
|
-
return this.api.bindConnection(purpose, id).pipe(this.reloadOnConflict(id), switchMap(() => this.refreshConnection(id)), tap(() => this.showSuccess('connection-bound')), map(() => undefined), finalize(() => this.mutationLoading.set(null)));
|
|
426
|
-
}
|
|
427
|
-
unbindConnection(id, purpose = 'notifications') {
|
|
428
|
-
this.mutationLoading.set('unbind');
|
|
429
|
-
return this.api.unbindConnection(purpose).pipe(this.reloadOnConflict(id), switchMap(() => this.refreshConnection(id)), tap(() => this.showSuccess('connection-unbound')), map(() => undefined), finalize(() => this.mutationLoading.set(null)));
|
|
430
|
-
}
|
|
431
|
-
startAuthorization(id, kind) {
|
|
432
|
-
this.mutationLoading.set(kind);
|
|
433
|
-
const request = kind === 'delegated'
|
|
434
|
-
? this.api.startDelegatedAuthorization(id)
|
|
435
|
-
: this.api.startAdminConsent(id);
|
|
436
|
-
return request.pipe(tap(() => this.rememberAuthorizationConnection(id)), finalize(() => this.mutationLoading.set(null)));
|
|
437
|
-
}
|
|
438
|
-
revokeAuthorization(id) {
|
|
439
|
-
this.mutationLoading.set('revoke');
|
|
440
|
-
return this.api.revokeAuthorization(id).pipe(this.reloadOnConflict(id), switchMap(() => this.refreshConnection(id)), tap(() => this.showSuccess('authorization-revoked')), map(() => undefined), finalize(() => this.mutationLoading.set(null)));
|
|
441
|
-
}
|
|
442
|
-
loadAttempts(reset = true, status = this.attemptStatusFilter()) {
|
|
443
|
-
this.attemptsLoading.set(true);
|
|
444
|
-
const cursor = reset ? null : this.attemptsNextCursor();
|
|
445
|
-
if (reset)
|
|
446
|
-
this.attemptStatusFilter.set(status);
|
|
447
|
-
return this.api.getAttempts(status, cursor, 50).pipe(tap((page) => {
|
|
448
|
-
this.attemptsLoaded = true;
|
|
449
|
-
this.attempts.set(reset ? page.items : mergeById(this.attempts(), page.items, 'id'));
|
|
450
|
-
this.attemptsNextCursor.set(page.nextCursor);
|
|
451
|
-
}), finalize(() => this.attemptsLoading.set(false)));
|
|
452
|
-
}
|
|
453
|
-
replayAttempt(attempt, request) {
|
|
454
|
-
this.mutationLoading.set(`replay-${attempt.id}`);
|
|
455
|
-
return this.api.replayAttempt(attempt.id, request).pipe(tap((result) => {
|
|
456
|
-
this.attempts.update((items) => replaceById(items, result.attempt, 'id'));
|
|
457
|
-
this.showSuccess('attempt-replay-scheduled');
|
|
458
|
-
}), finalize(() => this.mutationLoading.set(null)));
|
|
459
|
-
}
|
|
460
|
-
loadQuarantine(reset = true) {
|
|
461
|
-
this.quarantineLoading.set(true);
|
|
462
|
-
const cursor = reset ? null : this.quarantineNextCursor();
|
|
463
|
-
return this.api.getFinalizationQuarantine(cursor, 100).pipe(tap((page) => {
|
|
464
|
-
this.quarantine.set(reset
|
|
465
|
-
? page.items
|
|
466
|
-
: mergeById(this.quarantine(), page.items, 'itemId'));
|
|
467
|
-
this.quarantineNextCursor.set(page.nextCursor);
|
|
468
|
-
}), finalize(() => this.quarantineLoading.set(false)));
|
|
469
|
-
}
|
|
470
|
-
decideQuarantine(item, action, reason) {
|
|
471
|
-
this.mutationLoading.set(`${action}-${item.itemId}`);
|
|
472
|
-
const request = action === 'retry'
|
|
473
|
-
? this.api.retryFinalizationQuarantine(item.itemId, reason)
|
|
474
|
-
: this.api.acknowledgeFinalizationQuarantine(item.itemId, reason);
|
|
475
|
-
return request.pipe(switchMap((updated) => this.loadQuarantine(true).pipe(map(() => updated))), tap(() => {
|
|
476
|
-
this.showSuccess(action === 'retry'
|
|
477
|
-
? 'quarantine-retry-submitted'
|
|
478
|
-
: 'quarantine-acknowledged');
|
|
479
|
-
}), finalize(() => this.mutationLoading.set(null)));
|
|
480
|
-
}
|
|
481
|
-
loadHostEvidence(reset = true) {
|
|
482
|
-
this.hostEvidenceLoading.set(true);
|
|
483
|
-
const cursor = reset ? null : this.hostEvidenceNextCursor();
|
|
484
|
-
return this.api.getHostEvidence(cursor, 100).pipe(tap((page) => {
|
|
485
|
-
this.hostEvidence.set(reset
|
|
486
|
-
? page.items
|
|
487
|
-
: mergeHostEvidence(this.hostEvidence(), page.items));
|
|
488
|
-
this.hostEvidenceNextCursor.set(page.nextCursor);
|
|
489
|
-
this.hostEvidenceScanBudgetExhausted.set(page.scanBudgetExhausted);
|
|
490
|
-
}), finalize(() => this.hostEvidenceLoading.set(false)));
|
|
491
|
-
}
|
|
492
|
-
exportHostEvidence(evidenceOccurrenceId) {
|
|
493
|
-
this.mutationLoading.set(`export-${evidenceOccurrenceId}`);
|
|
494
|
-
return this.api
|
|
495
|
-
.exportHostEvidence(evidenceOccurrenceId)
|
|
496
|
-
.pipe(finalize(() => this.mutationLoading.set(null)));
|
|
497
|
-
}
|
|
498
|
-
decideHostEvidence(item, action, reason) {
|
|
499
|
-
if (!item.evidenceOccurrenceId || !item.decisionToken) {
|
|
500
|
-
return throwError(() => new Error('Incomplete evidence identity'));
|
|
501
|
-
}
|
|
502
|
-
this.mutationLoading.set(`${action}-${item.evidenceOccurrenceId}`);
|
|
503
|
-
const request = {
|
|
504
|
-
reason,
|
|
505
|
-
decisionToken: item.decisionToken,
|
|
506
|
-
};
|
|
507
|
-
const operation = action === 'repair'
|
|
508
|
-
? this.api.repairHostEvidence(item.evidenceOccurrenceId, request)
|
|
509
|
-
: this.api.acknowledgeHostEvidence(item.evidenceOccurrenceId, request);
|
|
510
|
-
return operation.pipe(switchMap((updated) => this.loadHostEvidence(true).pipe(map(() => updated))), tap(() => {
|
|
511
|
-
this.showSuccess(action === 'repair' ? 'evidence-repaired' : 'evidence-acknowledged');
|
|
512
|
-
}), finalize(() => this.mutationLoading.set(null)));
|
|
513
|
-
}
|
|
514
|
-
rebuildEvidenceIndex(take) {
|
|
515
|
-
this.mutationLoading.set('rebuild-evidence');
|
|
516
|
-
return this.api.rebuildEvidenceIndex(take).pipe(switchMap((result) => this.loadHostEvidence(true).pipe(map(() => result))), tap(() => this.showSuccess('evidence-index-rebuilt')), finalize(() => this.mutationLoading.set(null)));
|
|
517
|
-
}
|
|
518
|
-
providerByKey(key) {
|
|
519
|
-
return this.providers().find((provider) => provider.key === key) ?? null;
|
|
520
|
-
}
|
|
521
|
-
describeCode(code) {
|
|
522
|
-
if (!code)
|
|
523
|
-
return this.transloco.translate('email-delivery.unknown-code');
|
|
524
|
-
const key = `email-delivery.codes.${code}`;
|
|
525
|
-
const translated = this.transloco.translate(key);
|
|
526
|
-
if (translated !== key)
|
|
527
|
-
return translated;
|
|
528
|
-
const contract = this.errorCatalog().find((entry) => entry.code === code);
|
|
529
|
-
if (!contract || contract.frontendAction === 'none')
|
|
530
|
-
return code;
|
|
531
|
-
const actionKey = `email-delivery.error-actions.${contract.frontendAction}`;
|
|
532
|
-
const action = this.transloco.translate(actionKey);
|
|
533
|
-
return action === actionKey ? code : `${action} (${code})`;
|
|
534
|
-
}
|
|
535
|
-
/**
|
|
536
|
-
* The one sentence every failure path shows. It prefers the backend's stable
|
|
537
|
-
* code, falls back to the transport status the contract assigns a required
|
|
538
|
-
* behaviour to (401/403/409/422/428/429/503), and always appends the
|
|
539
|
-
* correlation ID when one is available — that identifier is what support asks
|
|
540
|
-
* for, and it is the only part of a failure this page is allowed to keep.
|
|
541
|
-
*/
|
|
542
|
-
describeError(error) {
|
|
543
|
-
const failure = extractFailure(error);
|
|
544
|
-
const parts = [this.describeFailure(failure)];
|
|
545
|
-
if (failure.retryAfterSeconds !== null) {
|
|
546
|
-
parts.push(this.transloco.translate('email-delivery.retry-after', {
|
|
547
|
-
seconds: failure.retryAfterSeconds,
|
|
548
|
-
}));
|
|
549
|
-
}
|
|
550
|
-
if (failure.correlationId) {
|
|
551
|
-
parts.push(`${this.transloco.translate('email-delivery.correlation-id')}: ${failure.correlationId}`);
|
|
552
|
-
}
|
|
553
|
-
return parts.join(' ');
|
|
554
|
-
}
|
|
555
|
-
errorAction(error) {
|
|
556
|
-
const failure = extractFailure(error);
|
|
557
|
-
return (failure.action ||
|
|
558
|
-
this.errorCatalog().find((entry) => entry.code === failure.code)
|
|
559
|
-
?.frontendAction ||
|
|
560
|
-
statusAction(failure.status));
|
|
561
|
-
}
|
|
562
|
-
showError(error) {
|
|
563
|
-
this.toast.error(this.describeError(error));
|
|
564
|
-
}
|
|
565
|
-
/**
|
|
566
|
-
* A `409` means the server state moved under this page. The contract requires
|
|
567
|
-
* reloading it rather than re-submitting over it, so every mutation that can
|
|
568
|
-
* conflict routes through here: the latest connection, list and readiness are
|
|
569
|
-
* pulled back before the caller sees the rejection.
|
|
570
|
-
*/
|
|
571
|
-
reloadOnConflict(id) {
|
|
572
|
-
return catchError((error) => {
|
|
573
|
-
if (statusOf(error) !== 409)
|
|
574
|
-
return throwError(() => error);
|
|
575
|
-
const reload = id
|
|
576
|
-
? this.refreshConnection(id).pipe(map(() => undefined))
|
|
577
|
-
: forkJoin({
|
|
578
|
-
connections: this.loadConnections(),
|
|
579
|
-
status: this.loadStatus(),
|
|
580
|
-
}).pipe(map(() => undefined));
|
|
581
|
-
reload.subscribe({ error: () => undefined });
|
|
582
|
-
return throwError(() => error);
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
describeFailure(failure) {
|
|
586
|
-
const translated = failure.code ? this.translateCode(failure.code) : null;
|
|
587
|
-
if (translated)
|
|
588
|
-
return translated;
|
|
589
|
-
const statusMessage = this.translateStatus(failure.status);
|
|
590
|
-
if (statusMessage) {
|
|
591
|
-
return failure.code
|
|
592
|
-
? `${statusMessage} (${failure.code})`
|
|
593
|
-
: statusMessage;
|
|
594
|
-
}
|
|
595
|
-
return this.describeCode(failure.code);
|
|
596
|
-
}
|
|
597
|
-
translateCode(code) {
|
|
598
|
-
const key = `email-delivery.codes.${code}`;
|
|
599
|
-
const translated = this.transloco.translate(key);
|
|
600
|
-
return translated === key ? null : translated;
|
|
601
|
-
}
|
|
602
|
-
translateStatus(status) {
|
|
603
|
-
const key = status === null ? null : HTTP_STATUS_KEYS[status];
|
|
604
|
-
if (!key)
|
|
605
|
-
return null;
|
|
606
|
-
const fullKey = `email-delivery.http.${key}`;
|
|
607
|
-
const translated = this.transloco.translate(fullKey);
|
|
608
|
-
return translated === fullKey ? null : translated;
|
|
609
|
-
}
|
|
610
|
-
takePendingAuthorizationConnection() {
|
|
611
|
-
if (typeof sessionStorage === 'undefined')
|
|
612
|
-
return null;
|
|
613
|
-
const id = sessionStorage.getItem(PENDING_AUTHORIZATION_STORAGE_KEY);
|
|
614
|
-
sessionStorage.removeItem(PENDING_AUTHORIZATION_STORAGE_KEY);
|
|
615
|
-
return id;
|
|
616
|
-
}
|
|
617
|
-
/**
|
|
618
|
-
* A completed controlled test writes its own Activity row, so the loaded
|
|
619
|
-
* Activity list is stale the moment the test returns. Only refresh once
|
|
620
|
-
* Activity has been opened — before that its own load covers it, and the
|
|
621
|
-
* status filter in effect there is preserved.
|
|
622
|
-
*/
|
|
623
|
-
refreshActivity() {
|
|
624
|
-
if (!this.attemptsLoaded)
|
|
625
|
-
return;
|
|
626
|
-
this.loadAttempts(true).subscribe({ error: () => undefined });
|
|
627
|
-
}
|
|
628
|
-
refreshConnection(id) {
|
|
629
|
-
return forkJoin({
|
|
630
|
-
details: this.api.getConnection(id),
|
|
631
|
-
connections: this.api.getConnections(),
|
|
632
|
-
status: this.api.getStatus(),
|
|
633
|
-
}).pipe(tap(({ details, connections, status }) => {
|
|
634
|
-
this.selectedConnection.set(details);
|
|
635
|
-
this.connections.set(connections);
|
|
636
|
-
this.status.set(status);
|
|
637
|
-
}), map(({ details }) => details));
|
|
638
|
-
}
|
|
639
|
-
rememberAuthorizationConnection(id) {
|
|
640
|
-
if (typeof sessionStorage === 'undefined')
|
|
641
|
-
return;
|
|
642
|
-
sessionStorage.setItem(PENDING_AUTHORIZATION_STORAGE_KEY, id);
|
|
643
|
-
}
|
|
644
|
-
showSuccess(key) {
|
|
645
|
-
this.toast.success(this.transloco.translate(`email-delivery.${key}`));
|
|
646
|
-
}
|
|
647
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
648
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryFacade, providedIn: 'root' });
|
|
649
|
-
}
|
|
650
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryFacade, decorators: [{
|
|
651
|
-
type: Injectable,
|
|
652
|
-
args: [{ providedIn: 'root' }]
|
|
653
|
-
}] });
|
|
654
|
-
const HTTP_STATUS_KEYS = {
|
|
655
|
-
401: 'unauthorized',
|
|
656
|
-
403: 'forbidden',
|
|
657
|
-
409: 'conflict',
|
|
658
|
-
422: 'unprocessable',
|
|
659
|
-
428: 'authorization-required',
|
|
660
|
-
429: 'too-many-requests',
|
|
661
|
-
503: 'unavailable',
|
|
662
|
-
};
|
|
663
|
-
const HTTP_STATUS_ACTIONS = {
|
|
664
|
-
403: 'request_permission',
|
|
665
|
-
409: 'refresh',
|
|
666
|
-
422: 'correct_configuration',
|
|
667
|
-
428: 'authorize',
|
|
668
|
-
429: 'retry_later',
|
|
669
|
-
503: 'retry_later',
|
|
670
|
-
};
|
|
671
|
-
function statusAction(status) {
|
|
672
|
-
return status === null ? null : (HTTP_STATUS_ACTIONS[status] ?? null);
|
|
673
|
-
}
|
|
674
|
-
function statusOf(error) {
|
|
675
|
-
return error instanceof HttpErrorResponse ? error.status : null;
|
|
676
|
-
}
|
|
677
|
-
function extractFailure(error) {
|
|
678
|
-
const body = error instanceof HttpErrorResponse ? error.error : error;
|
|
679
|
-
const problem = readProblem(body);
|
|
680
|
-
const headers = error instanceof HttpErrorResponse ? error.headers : null;
|
|
681
|
-
return {
|
|
682
|
-
code: problem?.code ?? null,
|
|
683
|
-
action: problem?.action || null,
|
|
684
|
-
correlationId: problem?.correlationId ||
|
|
685
|
-
headers?.get('x-correlation-id') ||
|
|
686
|
-
headers?.get('correlation-id') ||
|
|
687
|
-
null,
|
|
688
|
-
retryAfterSeconds: problem?.retryAfterSeconds ??
|
|
689
|
-
parseRetryAfter(headers?.get('retry-after') ?? null),
|
|
690
|
-
status: statusOf(error),
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
/**
|
|
694
|
-
* Two envelopes reach this page. The email contract answers with the problem
|
|
695
|
-
* shape itself; Gateway rejects a request before it ever gets there and answers
|
|
696
|
-
* with `{ errors: { code, message } }` — that is how `REQ_001` arrives, and
|
|
697
|
-
* reading only the first shape lost it.
|
|
698
|
-
*/
|
|
699
|
-
function readProblem(body) {
|
|
700
|
-
if (typeof body !== 'object' || body === null)
|
|
701
|
-
return null;
|
|
702
|
-
const problem = body;
|
|
703
|
-
if (typeof problem.code === 'string')
|
|
704
|
-
return problem;
|
|
705
|
-
const gatewayCode = problem.errors?.code;
|
|
706
|
-
if (typeof gatewayCode === 'string') {
|
|
707
|
-
return { ...problem, code: gatewayCode };
|
|
708
|
-
}
|
|
709
|
-
return null;
|
|
710
|
-
}
|
|
711
|
-
/**
|
|
712
|
-
* `Retry-After` is either a delay in seconds or an HTTP date. Both become a
|
|
713
|
-
* whole number of seconds so the message can say when to try again; anything
|
|
714
|
-
* unparseable or already in the past is simply not offered.
|
|
715
|
-
*/
|
|
716
|
-
function parseRetryAfter(value) {
|
|
717
|
-
if (!value)
|
|
718
|
-
return null;
|
|
719
|
-
const seconds = Number(value);
|
|
720
|
-
if (Number.isFinite(seconds)) {
|
|
721
|
-
return seconds > 0 ? Math.ceil(seconds) : null;
|
|
722
|
-
}
|
|
723
|
-
const date = Date.parse(value);
|
|
724
|
-
if (Number.isNaN(date))
|
|
725
|
-
return null;
|
|
726
|
-
const delta = Math.ceil((date - Date.now()) / 1000);
|
|
727
|
-
return delta > 0 ? delta : null;
|
|
728
|
-
}
|
|
729
|
-
function mergeById(current, incoming, key) {
|
|
730
|
-
const values = new Map(current.map((item) => [item[key], item]));
|
|
731
|
-
incoming.forEach((item) => values.set(item[key], item));
|
|
732
|
-
return Array.from(values.values());
|
|
733
|
-
}
|
|
734
|
-
function replaceById(current, replacement, key) {
|
|
735
|
-
return current.map((item) => item[key] === replacement[key] ? replacement : item);
|
|
736
|
-
}
|
|
737
|
-
function mergeHostEvidence(current, incoming) {
|
|
738
|
-
const identity = (item) => item.evidenceOccurrenceId ?? item.itemId;
|
|
739
|
-
const values = new Map(current.map((item) => [identity(item), item]));
|
|
740
|
-
incoming.forEach((item) => values.set(identity(item), item));
|
|
741
|
-
return Array.from(values.values());
|
|
742
|
-
}
|
|
743
|
-
|
|
744
31
|
class EmailOperatorReasonDialog {
|
|
745
32
|
itemName = input.required(...(ngDevMode ? [{ debugName: "itemName" }] : /* istanbul ignore next */ []));
|
|
746
33
|
consequence = input.required(...(ngDevMode ? [{ debugName: "consequence" }] : /* istanbul ignore next */ []));
|
|
@@ -1400,7 +687,7 @@ class EmailControlledTestDialog {
|
|
|
1400
687
|
destroyRef = inject(DestroyRef);
|
|
1401
688
|
transloco = inject(TranslocoService);
|
|
1402
689
|
facade = inject(EmailDeliveryFacade);
|
|
1403
|
-
testRequestId = signal(
|
|
690
|
+
testRequestId = signal(createEmailTestRequestId(), ...(ngDevMode ? [{ debugName: "testRequestId" }] : /* istanbul ignore next */ []));
|
|
1404
691
|
timedOut = signal(false, ...(ngDevMode ? [{ debugName: "timedOut" }] : /* istanbul ignore next */ []));
|
|
1405
692
|
polling = signal(false, ...(ngDevMode ? [{ debugName: "polling" }] : /* istanbul ignore next */ []));
|
|
1406
693
|
result = signal(null, ...(ngDevMode ? [{ debugName: "result" }] : /* istanbul ignore next */ []));
|
|
@@ -1433,7 +720,7 @@ class EmailControlledTestDialog {
|
|
|
1433
720
|
rows: 3,
|
|
1434
721
|
validators: [
|
|
1435
722
|
ValidatorConfig.required(),
|
|
1436
|
-
ValidatorConfig.custom((value) =>
|
|
723
|
+
ValidatorConfig.custom((value) => areEmailTestRecipientsValid(value, 100, 320), this.t('test.recipients-invalid')),
|
|
1437
724
|
],
|
|
1438
725
|
}),
|
|
1439
726
|
new TextFieldConfig({
|
|
@@ -1480,7 +767,7 @@ class EmailControlledTestDialog {
|
|
|
1480
767
|
this.facade
|
|
1481
768
|
.sendConnectionTest(this.connectionId(), {
|
|
1482
769
|
testRequestId: this.testRequestId(),
|
|
1483
|
-
recipients:
|
|
770
|
+
recipients: parseEmailTestRecipients(value.recipients),
|
|
1484
771
|
subject: value.subject?.trim() ?? '',
|
|
1485
772
|
body: value.body ?? '',
|
|
1486
773
|
isHtml: value.isHtml,
|
|
@@ -1502,7 +789,7 @@ class EmailControlledTestDialog {
|
|
|
1502
789
|
// reusing it can never succeed, so the dialog mints a fresh one and
|
|
1503
790
|
// waits for the operator to press Send again — it never resends.
|
|
1504
791
|
if (isTestRequestConflict(error)) {
|
|
1505
|
-
this.testRequestId.set(
|
|
792
|
+
this.testRequestId.set(createEmailTestRequestId());
|
|
1506
793
|
}
|
|
1507
794
|
},
|
|
1508
795
|
});
|
|
@@ -1523,7 +810,7 @@ class EmailControlledTestDialog {
|
|
|
1523
810
|
this.polling.set(false);
|
|
1524
811
|
return;
|
|
1525
812
|
}
|
|
1526
|
-
if (fromPoll &&
|
|
813
|
+
if (fromPoll && isEmailTestRequestPending(stored.requestState)) {
|
|
1527
814
|
this.schedulePoll();
|
|
1528
815
|
}
|
|
1529
816
|
else {
|
|
@@ -1588,35 +875,10 @@ function isTestRequestConflict(error) {
|
|
|
1588
875
|
body.code === TEST_REQUEST_CONFLICT_CODE);
|
|
1589
876
|
}
|
|
1590
877
|
const POLL_DELAYS_MS = [1_000, 2_000, 4_000, 5_000, 5_000, 5_000];
|
|
1591
|
-
function isPendingRequestState(value) {
|
|
1592
|
-
return !['Completed', 'Failed', 'Cancelled', 'Canceled'].includes(value);
|
|
1593
|
-
}
|
|
1594
|
-
function parseRecipients(value) {
|
|
1595
|
-
return Array.from(new Set((value ?? '')
|
|
1596
|
-
.split(/[\s,;]+/)
|
|
1597
|
-
.map((item) => item.trim())
|
|
1598
|
-
.filter(Boolean)));
|
|
1599
|
-
}
|
|
1600
|
-
function validRecipients(value) {
|
|
1601
|
-
const recipients = parseRecipients(String(value ?? ''));
|
|
1602
|
-
return (recipients.length >= 1 &&
|
|
1603
|
-
recipients.length <= 100 &&
|
|
1604
|
-
recipients.every((recipient) => recipient.length <= 320 && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(recipient)));
|
|
1605
|
-
}
|
|
1606
878
|
function isAmbiguousTimeout(error) {
|
|
1607
879
|
return (error instanceof HttpErrorResponse &&
|
|
1608
880
|
(error.status === 0 || error.status === 408 || error.status === 504));
|
|
1609
881
|
}
|
|
1610
|
-
function createId() {
|
|
1611
|
-
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
|
|
1612
|
-
return crypto.randomUUID();
|
|
1613
|
-
}
|
|
1614
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (character) => {
|
|
1615
|
-
const random = Math.floor(Math.random() * 16);
|
|
1616
|
-
const value = character === 'x' ? random : (random & 0x3) | 0x8;
|
|
1617
|
-
return value.toString(16);
|
|
1618
|
-
});
|
|
1619
|
-
}
|
|
1620
882
|
|
|
1621
883
|
function getApplicableProviderFields(provider, authenticationMode) {
|
|
1622
884
|
if (!provider || !authenticationMode) {
|
|
@@ -2561,9 +1823,9 @@ class EmailDeliveryConnections {
|
|
|
2561
1823
|
activeLanguage = toSignal(this.transloco.langChanges$, {
|
|
2562
1824
|
initialValue: this.transloco.getActiveLang(),
|
|
2563
1825
|
});
|
|
2564
|
-
canConfigure = this.facade.hasPermission(EMAIL_DELIVERY_PERMISSIONS.controlPanel);
|
|
1826
|
+
canConfigure = computed(() => this.facade.hasPermission(EMAIL_DELIVERY_PERMISSIONS.controlPanel), ...(ngDevMode ? [{ debugName: "canConfigure" }] : /* istanbul ignore next */ []));
|
|
2565
1827
|
/** One rule for both the caption action and the empty-state action. */
|
|
2566
|
-
canCreate = computed(() => this.canConfigure &&
|
|
1828
|
+
canCreate = computed(() => this.canConfigure() &&
|
|
2567
1829
|
this.facade.managementAvailable() &&
|
|
2568
1830
|
this.facade.providers().length > 0, ...(ngDevMode ? [{ debugName: "canCreate" }] : /* istanbul ignore next */ []));
|
|
2569
1831
|
rows = computed(() => this.facade.connections().map((connection) => ({
|
|
@@ -2750,7 +2012,7 @@ class EmailDeliveryConnections {
|
|
|
2750
2012
|
});
|
|
2751
2013
|
}
|
|
2752
2014
|
canMutate() {
|
|
2753
|
-
return this.canConfigure && this.facade.managementAvailable();
|
|
2015
|
+
return this.canConfigure() && this.facade.managementAvailable();
|
|
2754
2016
|
}
|
|
2755
2017
|
openEditor(connectionId) {
|
|
2756
2018
|
const drawer = this.modal.openModal(EmailConnectionEditor, 'drawer', {
|
|
@@ -2807,7 +2069,7 @@ class EmailDeliveryShell {
|
|
|
2807
2069
|
initialValue: this.transloco.getActiveLang(),
|
|
2808
2070
|
});
|
|
2809
2071
|
activeTab = signal('connections', ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
|
|
2810
|
-
canView = this.facade.hasPermission(EMAIL_DELIVERY_PERMISSIONS.controlPanel);
|
|
2072
|
+
canView = computed(() => this.facade.hasPermission(EMAIL_DELIVERY_PERMISSIONS.controlPanel), ...(ngDevMode ? [{ debugName: "canView" }] : /* istanbul ignore next */ []));
|
|
2811
2073
|
tabs = computed(() => {
|
|
2812
2074
|
this.activeLanguage();
|
|
2813
2075
|
return [
|
|
@@ -2858,11 +2120,26 @@ class EmailDeliveryShell {
|
|
|
2858
2120
|
});
|
|
2859
2121
|
}
|
|
2860
2122
|
ngOnInit() {
|
|
2861
|
-
|
|
2123
|
+
this.resolveAccess();
|
|
2124
|
+
}
|
|
2125
|
+
retryPermissions() {
|
|
2126
|
+
if (this.facade.permissionsLoading())
|
|
2862
2127
|
return;
|
|
2863
|
-
this.
|
|
2864
|
-
|
|
2865
|
-
|
|
2128
|
+
this.resolveAccess();
|
|
2129
|
+
}
|
|
2130
|
+
resolveAccess() {
|
|
2131
|
+
this.facade.loadPermissions(true).subscribe({
|
|
2132
|
+
next: () => {
|
|
2133
|
+
if (!this.canView())
|
|
2134
|
+
return;
|
|
2135
|
+
this.facade.initialize();
|
|
2136
|
+
this.syncTabFromUrl();
|
|
2137
|
+
this.handleAuthorizationCallback();
|
|
2138
|
+
},
|
|
2139
|
+
// The error is already on the facade; the template turns it into the
|
|
2140
|
+
// retryable state rather than the access-denied one.
|
|
2141
|
+
error: () => undefined,
|
|
2142
|
+
});
|
|
2866
2143
|
}
|
|
2867
2144
|
onTabChange(value) {
|
|
2868
2145
|
if (value !== 'connections' && value !== 'activity')
|
|
@@ -2915,7 +2192,7 @@ class EmailDeliveryShell {
|
|
|
2915
2192
|
return this.transloco.translate(`email-delivery.${key}`);
|
|
2916
2193
|
}
|
|
2917
2194
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2918
|
-
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 (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 {\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 });
|
|
2195
|
+
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 });
|
|
2919
2196
|
}
|
|
2920
2197
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: EmailDeliveryShell, decorators: [{
|
|
2921
2198
|
type: Component,
|
|
@@ -2928,9 +2205,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
2928
2205
|
EmailDeliveryStatus,
|
|
2929
2206
|
EmailDeliveryConnections,
|
|
2930
2207
|
EmailDeliveryActivity,
|
|
2931
|
-
], 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 (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 {\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" }]
|
|
2208
|
+
], 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" }]
|
|
2932
2209
|
}], ctorParameters: () => [] });
|
|
2933
2210
|
const PRODUCT_SETTINGS_ROUTE = '/control-panel/product-settings';
|
|
2934
2211
|
|
|
2935
2212
|
export { EmailDeliveryShell };
|
|
2936
|
-
//# sourceMappingURL=masterteam-system-settings-email-delivery-shell-
|
|
2213
|
+
//# sourceMappingURL=masterteam-system-settings-email-delivery-shell-Bjwtj5Ic.mjs.map
|