@masterteam/notification 0.0.28 → 0.0.29

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,120 +1,257 @@
1
1
  import * as _angular_core from '@angular/core';
2
+ import { DestroyRef, OnInit } from '@angular/core';
2
3
  import * as _masterteam_notification from '@masterteam/notification';
3
- import { FormGroup, FormControl } from '@angular/forms';
4
- import { LoadingStateShape, CrudStateBase, Response } from '@masterteam/components';
5
- import { ModalService } from '@masterteam/components/modal';
4
+ import { FormControl, FormGroup } from '@angular/forms';
5
+ import { LoadingStateShape } from '@masterteam/components';
6
6
  import * as rxjs from 'rxjs';
7
+ import { Observable } from 'rxjs';
8
+ import { ModalService } from '@masterteam/components/modal';
7
9
  import { StateContext } from '@ngxs/store';
8
10
 
9
11
  declare enum NotificationActionKey {
10
12
  GetEvents = "getEvents",
11
- GetReceivers = "getReceivers",
12
- GetProperties = "getProperties",
13
+ GetContext = "getContext",
14
+ GetDefaults = "getDefaults",
15
+ GetPublications = "getPublications",
16
+ CreateTemplate = "createTemplate",
17
+ SaveTemplate = "saveTemplate",
13
18
  AddReceivers = "addReceivers",
14
19
  DeleteReceiver = "deleteReceiver",
15
- AddTemplate = "addTemplate",
16
- UpdateTemplate = "updateTemplate",
17
- DeleteTemplate = "deleteTemplate"
18
- }
19
- type NotificationChannel = 'email' | 'sms' | 'app';
20
- interface ChannelConfig {
21
- key: NotificationChannel;
22
- labelKey: string;
23
- /** Whether this channel supports bilingual content */
24
- supportsBilingual: boolean;
25
- /** Whether bilingual is optional (true = show toggle) */
26
- bilingualOptional: boolean;
27
- /** Whether this channel uses rich text editor (false = textarea) */
28
- useRichEditor: boolean;
29
- /** Payload key for non-bilingual template (string value) */
30
- templateKey?: string;
31
- /** Payload key for bilingual template (object with en/ar) */
32
- translatableKey?: string;
33
- }
34
- declare const CHANNEL_CONFIGS: ChannelConfig[];
35
- /** Helper to get channel config by key */
36
- declare function getChannelConfig(key: NotificationChannel): ChannelConfig;
37
- /** Build form control name for a channel field */
38
- declare function getFormControlName(ch: ChannelConfig, type: 'single' | 'en' | 'ar'): string | null;
20
+ DeleteTemplate = "deleteTemplate",
21
+ PublishTemplate = "publishTemplate",
22
+ SetActivation = "setActivation",
23
+ RestorePublication = "restorePublication",
24
+ InitializeDefaults = "initializeDefaults",
25
+ RestoreDefault = "restoreDefault"
26
+ }
27
+ /** Writes that must never run twice at once (the contract forbids double submits). */
28
+ declare const NOTIFICATION_MUTATION_KEYS: readonly NotificationActionKey[];
29
+ type NotificationChannel = 'App' | 'Email' | 'Sms';
30
+ type NotificationReceiverType = 'Actor' | 'Role' | 'Group';
31
+ declare const NOTIFICATION_CHANNELS: readonly NotificationChannel[];
39
32
  interface TranslatableContent {
40
33
  en: string;
41
34
  ar: string;
42
35
  }
43
- interface ReceiverItem {
44
- id: string | number;
45
- identifier: string;
46
- name: any;
47
- type: string;
48
- isSystem?: boolean;
36
+ interface NotificationReceiver {
37
+ /** Saved recipient row id — the id used to remove it. */
38
+ id: number;
39
+ /** Catalog actor / role / group id. */
40
+ identifier: string | null;
41
+ type: NotificationReceiverType;
42
+ templateId: number;
43
+ isSystem: boolean;
44
+ name: {
45
+ display: string | null;
46
+ en: string | null;
47
+ ar: string | null;
48
+ };
49
49
  }
50
- interface ReceiverGroup {
51
- groupName: string;
52
- items: ReceiverItem[] | ReceiverSubGroup[];
50
+ interface NotificationTemplateItem {
51
+ id: number;
52
+ eventId: number;
53
+ channel: NotificationChannel;
54
+ name: string | null;
55
+ subject: string | null;
56
+ emailTemplate: string | null;
57
+ smsTemplate: string | null;
58
+ appTemplate: TranslatableContent | null;
59
+ conditionFormula: string | null;
60
+ /** Aggregate concurrency token for every write on this template. */
61
+ revision: number;
62
+ draftRevision: number;
63
+ publishedRevision: number | null;
64
+ publishedHash: string | null;
65
+ isEnabled: boolean;
66
+ isSystem: boolean;
67
+ receivers: NotificationReceiver[];
68
+ auditState: string | null;
69
+ auditCode: string | null;
53
70
  }
54
- interface ReceiverSubGroup {
55
- subGroupName: string;
56
- items: ReceiverItem[];
71
+ interface NotificationEvent {
72
+ id: number;
73
+ name: string;
74
+ eventName: string;
75
+ moduleId: number;
57
76
  }
58
- interface ReceiverOptionGroup {
59
- groupName: string;
60
- items: ReceiverItem[];
77
+ interface NotificationEventWithTemplates extends NotificationEvent {
78
+ templates: NotificationTemplateItem[];
61
79
  }
62
80
  interface NotificationProperty {
63
81
  key: string;
64
82
  name: string;
65
- nameEn?: string;
66
- nameAr?: string;
67
- groupName?: string;
83
+ nameEn: string;
84
+ nameAr: string;
68
85
  }
69
- interface PropertyGroup {
86
+ interface NotificationPropertyGroup {
70
87
  groupName: string;
71
88
  properties: NotificationProperty[];
72
89
  }
73
- interface NotificationTemplateItem {
74
- id: string | number;
75
- eventId: string | number;
90
+ /** A recipient catalog node: a subgroup when `items` is an array, otherwise a leaf. */
91
+ interface NotificationRecipientNode {
92
+ subGroupName?: string | null;
93
+ items?: NotificationRecipientNode[] | null;
94
+ id?: string | null;
95
+ name?: string | null;
96
+ nameEn?: string | null;
97
+ nameAr?: string | null;
98
+ type?: NotificationReceiverType | null;
99
+ }
100
+ interface NotificationRecipientGroup {
101
+ groupName: string;
102
+ items: NotificationRecipientNode[];
103
+ }
104
+ interface NotificationAuthoringContext {
105
+ eventId: number;
106
+ channels: NotificationChannel[];
107
+ properties: NotificationPropertyGroup[];
108
+ conditionProperties: NotificationProperty[];
109
+ recipients: NotificationRecipientGroup[];
110
+ publicationHistoryLimit: number;
111
+ }
112
+ interface NotificationPublishedRecipient {
113
+ identifier: string;
76
114
  name: string;
115
+ type: NotificationReceiverType;
77
116
  isSystem: boolean;
78
- channel: NotificationChannel;
79
- receivers: ReceiverItem[];
80
- emailTemplate: string;
81
- emailContentTranslatable: TranslatableContent;
82
- smsTemplate: string;
83
- appTemplate: TranslatableContent;
84
- conditionFormula?: string;
85
117
  }
86
- interface NotificationEvent {
87
- id: string | number;
88
- eventName: string;
118
+ interface NotificationPublishedContent {
119
+ formatVersion: number;
120
+ channel: NotificationChannel;
121
+ sourceDraftRevision: number;
89
122
  name: string;
90
- }
91
- interface NotificationAction {
92
- id: string | number;
123
+ subject: string | null;
124
+ emailContent: string | null;
125
+ smsContent: string | null;
126
+ /** Serialized `{ en, ar }` JSON — parse it, never render it as HTML. */
127
+ appContent: string | null;
128
+ conditionFormula: string | null;
129
+ receivers: NotificationPublishedRecipient[];
130
+ }
131
+ interface NotificationPublication {
132
+ revision: number;
133
+ sourceDraftRevision: number;
134
+ hash: string;
135
+ publishedAtUtc: string;
136
+ publishedBy: string;
137
+ content: NotificationPublishedContent;
138
+ }
139
+ type NotificationRecipeCoverage = 'Ready' | 'SafeGeneric' | 'NeedsReview';
140
+ interface NotificationDefaultSlot {
141
+ eventId: number;
142
+ channel: NotificationChannel;
143
+ /** Null means the default was intentionally removed — never recreate it silently. */
144
+ templateId: number | null;
145
+ recipeKey: string;
146
+ recipeVersion: number;
147
+ recipeHash: string;
148
+ revision: number;
149
+ }
150
+ interface NotificationDefaultRecipe {
151
+ key: string;
152
+ version: number;
153
+ channel: NotificationChannel;
154
+ coverage: NotificationRecipeCoverage;
155
+ languageReviewRequired: boolean;
156
+ recipientReviewRequired: boolean;
157
+ autoPublishApp: boolean;
158
+ recipeHash: string;
159
+ /** Unsaved proposal in template shape (`id: 0`). */
160
+ draft: NotificationTemplateItem;
161
+ }
162
+ interface NotificationDefaults {
163
+ eventId: number;
164
+ slots: NotificationDefaultSlot[];
165
+ adoptions: Partial<Record<NotificationChannel, number>>;
166
+ recipes: NotificationDefaultRecipe[];
167
+ }
168
+ interface NotificationInitializeDefaultsResult {
169
+ defaults: NotificationDefaults;
170
+ createdTemplates: NotificationTemplateItem[];
171
+ auditProjectionFailures: number[];
172
+ }
173
+ interface NotificationDiagnostic {
174
+ code: string;
175
+ severity: string;
176
+ path: string;
177
+ }
178
+ interface NotificationPreviewRequest {
179
+ channel: NotificationChannel;
180
+ subject?: string | null;
181
+ emailTemplate?: string | null;
182
+ smsTemplate?: string | null;
183
+ appTemplate?: TranslatableContent | null;
184
+ samples?: Record<string, string>;
185
+ }
186
+ interface NotificationFragmentPreview {
187
+ html: string;
188
+ text: string;
189
+ bodyHash: string;
190
+ layoutHash: string;
191
+ diagnostics: NotificationDiagnostic[];
192
+ }
193
+ interface NotificationEmailPreview {
194
+ channel: 'Email';
195
+ subject: string | null;
196
+ html: string;
197
+ text: string;
198
+ sanitizedBody: string;
199
+ layoutRevision: number;
200
+ layoutSource: string;
201
+ layoutHash: string;
202
+ contentHash: string;
203
+ diagnostics: NotificationDiagnostic[];
204
+ approvedImageOrigins: string[];
205
+ fidelity: 'browser';
206
+ }
207
+ interface NotificationAppPreview {
208
+ channel: 'App';
209
+ english: NotificationFragmentPreview;
210
+ arabic: NotificationFragmentPreview;
211
+ sanitizedBody: TranslatableContent;
212
+ approvedImageOrigins: string[];
213
+ fidelity: 'browser';
214
+ }
215
+ interface NotificationSmsPreview {
216
+ channel: 'Sms';
217
+ text: string;
218
+ sanitizedBody: string;
219
+ diagnostics: NotificationDiagnostic[];
220
+ fidelity: 'text';
221
+ }
222
+ type NotificationPreview = NotificationEmailPreview | NotificationAppPreview | NotificationSmsPreview;
223
+ interface NotificationReceiverRef {
224
+ type: NotificationReceiverType;
225
+ id: string;
226
+ }
227
+ interface CreateNotificationTemplatePayload {
228
+ channel: NotificationChannel;
93
229
  name: string;
94
- type: string;
95
- }
96
- interface ActionGroup {
97
- activity: string;
98
- items: NotificationAction[];
99
- }
100
- interface ReceiverPayloadItem {
101
- id: string | number;
102
- type: string;
230
+ subject: string | null;
231
+ emailTemplate: string | null;
232
+ smsTemplate: string | null;
233
+ appTemplate: TranslatableContent | null;
234
+ conditionFormula: string | null;
235
+ receivers: NotificationReceiverRef[];
236
+ }
237
+ /** Replacement, not a patch: a null field clears its value. */
238
+ interface SaveNotificationTemplatePayload {
239
+ expectedRevision: number;
240
+ name: string;
241
+ subject: string | null;
242
+ emailTemplate: string | null;
243
+ smsTemplate: string | null;
244
+ appTemplate: TranslatableContent | null;
245
+ conditionFormula: string | null;
103
246
  }
104
- interface AddReceiversPayload {
105
- receivers: ReceiverPayloadItem[];
247
+ interface NotificationReviewFlags {
248
+ recipientsReviewed: boolean;
249
+ languageReviewed: boolean;
106
250
  }
107
- interface AddTemplatePayload {
108
- name: string;
251
+ interface RestoreNotificationDefaultPayload {
109
252
  channel: NotificationChannel;
110
- }
111
- interface UpdateTemplatePayload {
112
- title: string;
113
- emailTemplate?: string;
114
- emailContentTranslatable?: TranslatableContent;
115
- smsTemplate?: string;
116
- appTemplate?: TranslatableContent;
117
- conditionFormula?: string;
253
+ expectedSlotRevision: number;
254
+ recipeHash: string;
118
255
  }
119
256
  interface NotificationStateModel extends LoadingStateShape<NotificationActionKey> {
120
257
  moduleType: string | null;
@@ -122,16 +259,14 @@ interface NotificationStateModel extends LoadingStateShape<NotificationActionKey
122
259
  parentModuleType: string | null;
123
260
  parentModuleId: string | number | null;
124
261
  parentPath: string;
125
- isActionMode: boolean;
126
- selectedActionId: string | number | null;
127
262
  events: NotificationEvent[];
128
263
  templates: NotificationTemplateItem[];
129
- actions: NotificationAction[];
130
- receivers: ReceiverItem[];
131
- receiverGroups: ReceiverOptionGroup[];
132
- propertyGroups: PropertyGroup[];
133
- selectedEventName: string | null;
134
- selectedTemplateId: string | number | null;
264
+ context: NotificationAuthoringContext | null;
265
+ defaults: NotificationDefaults | null;
266
+ publications: NotificationPublication[];
267
+ publicationsTemplateId: number | null;
268
+ selectedEventId: number | null;
269
+ selectedTemplateId: number | null;
135
270
  }
136
271
 
137
272
  declare class SetModuleInfo {
@@ -143,147 +278,272 @@ declare class SetModuleInfo {
143
278
  static readonly type = "[Notification] Set Module Info";
144
279
  constructor(moduleType: string, moduleId: string | number, parentModuleType?: string | undefined, parentModuleId?: string | number | undefined, parentPath?: string | undefined);
145
280
  }
281
+ declare class ResetNotificationState {
282
+ static readonly type = "[Notification] Reset State";
283
+ }
284
+ /** Loads (or re-reads) the scope's events, keeping the current selection when it still exists. */
146
285
  declare class GetEvents {
147
286
  static readonly type = "[Notification] Get Events";
148
287
  }
149
- declare class GetReceivers {
150
- static readonly type = "[Notification] Get Receivers";
151
- }
152
- declare class GetProperties {
153
- isMultiLang?: boolean | undefined;
154
- static readonly type = "[Notification] Get Properties";
155
- constructor(isMultiLang?: boolean | undefined);
156
- }
157
- declare class SetActionMode {
158
- actionId: string | number | null;
159
- static readonly type = "[Notification] Set Action Mode";
160
- constructor(actionId: string | number | null);
288
+ declare class GetAuthoringContext {
289
+ eventId: number;
290
+ static readonly type = "[Notification] Get Authoring Context";
291
+ constructor(eventId: number);
161
292
  }
162
- declare class GetActionReceivers {
163
- actionId: string | number;
164
- static readonly type = "[Notification] Get Action Receivers";
165
- constructor(actionId: string | number);
293
+ declare class GetDefaultRecipes {
294
+ eventId: number;
295
+ static readonly type = "[Notification] Get Default Recipes";
296
+ constructor(eventId: number);
166
297
  }
167
- declare class GetActionProperties {
168
- actionId: string | number;
169
- isMultiLang?: boolean | undefined;
170
- static readonly type = "[Notification] Get Action Properties";
171
- constructor(actionId: string | number, isMultiLang?: boolean | undefined);
298
+ declare class GetPublications {
299
+ templateId: number;
300
+ static readonly type = "[Notification] Get Publications";
301
+ constructor(templateId: number);
172
302
  }
173
303
  declare class SelectEvent {
174
- eventName: string;
304
+ eventId: number;
175
305
  static readonly type = "[Notification] Select Event";
176
- constructor(eventName: string);
306
+ constructor(eventId: number);
177
307
  }
178
308
  declare class SelectTemplate {
179
- templateId: string | number;
309
+ templateId: number;
180
310
  static readonly type = "[Notification] Select Template";
181
- constructor(templateId: string | number);
182
- }
183
- declare class AddReceivers {
184
- templateId: string | number;
185
- payload: AddReceiversPayload;
186
- static readonly type = "[Notification] Add Receivers";
187
- constructor(templateId: string | number, payload: AddReceiversPayload);
188
- }
189
- declare class DeleteReceiver {
190
- receiverId: string | number;
191
- static readonly type = "[Notification] Delete Receiver";
192
- constructor(receiverId: string | number);
311
+ constructor(templateId: number);
193
312
  }
194
- declare class AddTemplate {
195
- eventId: string | number;
196
- payload: AddTemplatePayload;
197
- static readonly type = "[Notification] Add Template";
198
- constructor(eventId: string | number, payload: AddTemplatePayload);
313
+ declare class CreateTemplate {
314
+ eventId: number;
315
+ payload: CreateNotificationTemplatePayload;
316
+ static readonly type = "[Notification] Create Template";
317
+ constructor(eventId: number, payload: CreateNotificationTemplatePayload);
199
318
  }
200
- declare class UpdateTemplate {
201
- templateId: string | number;
202
- payload: UpdateTemplatePayload;
203
- static readonly type = "[Notification] Update Template";
204
- constructor(templateId: string | number, payload: UpdateTemplatePayload);
319
+ declare class SaveTemplate {
320
+ templateId: number;
321
+ payload: SaveNotificationTemplatePayload;
322
+ static readonly type = "[Notification] Save Template";
323
+ constructor(templateId: number, payload: SaveNotificationTemplatePayload);
205
324
  }
206
325
  declare class DeleteTemplate {
207
- templateId: string | number;
326
+ templateId: number;
327
+ expectedRevision: number;
208
328
  static readonly type = "[Notification] Delete Template";
209
- constructor(templateId: string | number);
329
+ constructor(templateId: number, expectedRevision: number);
210
330
  }
211
- declare class ResetNotificationState {
212
- static readonly type = "[Notification] Reset State";
331
+ declare class AddReceivers {
332
+ templateId: number;
333
+ expectedRevision: number;
334
+ receivers: NotificationReceiverRef[];
335
+ static readonly type = "[Notification] Add Receivers";
336
+ constructor(templateId: number, expectedRevision: number, receivers: NotificationReceiverRef[]);
337
+ }
338
+ declare class DeleteReceiver {
339
+ receiverId: number;
340
+ expectedRevision: number;
341
+ static readonly type = "[Notification] Delete Receiver";
342
+ constructor(receiverId: number, expectedRevision: number);
343
+ }
344
+ declare class PublishTemplate {
345
+ templateId: number;
346
+ expectedRevision: number;
347
+ reviews: NotificationReviewFlags;
348
+ static readonly type = "[Notification] Publish Template";
349
+ constructor(templateId: number, expectedRevision: number, reviews: NotificationReviewFlags);
350
+ }
351
+ declare class SetTemplateActivation {
352
+ templateId: number;
353
+ expectedRevision: number;
354
+ enabled: boolean;
355
+ reviews: NotificationReviewFlags | null;
356
+ static readonly type = "[Notification] Set Template Activation";
357
+ constructor(templateId: number, expectedRevision: number, enabled: boolean, reviews: NotificationReviewFlags | null);
358
+ }
359
+ declare class RestorePublication {
360
+ templateId: number;
361
+ expectedRevision: number;
362
+ publicationRevision: number;
363
+ reviews: NotificationReviewFlags;
364
+ static readonly type = "[Notification] Restore Publication";
365
+ constructor(templateId: number, expectedRevision: number, publicationRevision: number, reviews: NotificationReviewFlags);
366
+ }
367
+ declare class InitializeDefaults {
368
+ eventId: number;
369
+ static readonly type = "[Notification] Initialize Defaults";
370
+ constructor(eventId: number);
371
+ }
372
+ declare class RestoreDefault {
373
+ eventId: number;
374
+ payload: RestoreNotificationDefaultPayload;
375
+ static readonly type = "[Notification] Restore Default";
376
+ constructor(eventId: number, payload: RestoreNotificationDefaultPayload);
213
377
  }
214
378
 
215
- declare class NotificationState extends CrudStateBase<NotificationTemplateItem, NotificationStateModel, NotificationActionKey> {
379
+ /**
380
+ * Notification authoring endpoints. Every call is marked `noMessage`: the
381
+ * backend answers failures with a bare stable code (`NOTIFICATION_*`) as the
382
+ * message, so the host's message interceptor would toast that raw code. The
383
+ * editor translates the code itself (see `notification.errors.ts`).
384
+ */
385
+ declare class NotificationTemplateApi {
216
386
  private readonly http;
217
387
  private readonly baseUrl;
218
- static getState(state: NotificationStateModel): NotificationStateModel;
388
+ private readonly headers;
389
+ getEvents(scopePath: string): Observable<NotificationEventWithTemplates[]>;
390
+ getContext(eventId: number): Observable<NotificationAuthoringContext>;
391
+ getDefaults(eventId: number): Observable<NotificationDefaults>;
392
+ initializeDefaults(eventId: number): Observable<NotificationInitializeDefaultsResult>;
393
+ restoreDefault(eventId: number, payload: RestoreNotificationDefaultPayload): Observable<NotificationTemplateItem>;
394
+ preview(eventId: number, request: NotificationPreviewRequest): Observable<NotificationPreview>;
395
+ createTemplate(eventId: number, payload: CreateNotificationTemplatePayload): Observable<NotificationTemplateItem>;
396
+ saveTemplate(templateId: number, payload: SaveNotificationTemplatePayload): Observable<NotificationTemplateItem>;
397
+ addReceivers(templateId: number, expectedRevision: number, receivers: NotificationReceiverRef[]): Observable<NotificationTemplateItem>;
398
+ deleteReceiver(receiverId: number, expectedRevision: number): Observable<NotificationTemplateItem>;
399
+ deleteTemplate(templateId: number, expectedRevision: number): Observable<unknown>;
400
+ getPublications(templateId: number): Observable<NotificationPublication[]>;
401
+ publish(templateId: number, expectedRevision: number, reviews: NotificationReviewFlags): Observable<NotificationTemplateItem>;
402
+ setActivation(templateId: number, expectedRevision: number, enabled: boolean, reviews: NotificationReviewFlags | null): Observable<NotificationTemplateItem>;
403
+ restorePublication(templateId: number, expectedRevision: number, publicationRevision: number, reviews: NotificationReviewFlags): Observable<NotificationTemplateItem>;
404
+ private get;
405
+ private post;
406
+ private delete;
407
+ private url;
408
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NotificationTemplateApi, never>;
409
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<NotificationTemplateApi>;
410
+ }
411
+
412
+ interface NotificationFailure {
413
+ /** Stable domain code (`NOTIFICATION_*` / `EMAIL_*`), or null for transport failures. */
414
+ code: string | null;
415
+ status: number | null;
416
+ diagnostics: NotificationDiagnostic[];
417
+ }
418
+ /**
419
+ * Reads the stable code out of a failed notification request.
420
+ *
421
+ * Domain failures arrive as `errors: { code, message, details }`. Command
422
+ * endpoints can wrap the domain code in `errors.details.code` under an outer
423
+ * validation code (e.g. `VAL_002`), so the nested code wins when present.
424
+ * Display text is never matched — only codes.
425
+ */
426
+ declare function readNotificationFailure(error: unknown): NotificationFailure;
427
+ declare function isRevisionConflict(failure: NotificationFailure): boolean;
428
+
429
+ interface NotificationRecipientOption {
430
+ /** `${type}:${id}` — selections are keyed by type and id, never id alone. */
431
+ key: string;
432
+ id: string;
433
+ type: NotificationReceiverType;
434
+ name: string;
435
+ }
436
+ interface NotificationRecipientOptionGroup {
437
+ groupName: string;
438
+ items: NotificationRecipientOption[];
439
+ }
440
+ type NotificationTemplateStatus = 'on' | 'off' | 'draft';
441
+ declare function recipientKey(type: NotificationReceiverType | null | undefined, id: string | null | undefined): string;
442
+ /**
443
+ * Flattens the recipient catalog into select-ready groups. A node is a
444
+ * subgroup only when its `items` is an array — leaves carry `items: null` —
445
+ * and subgroup names are kept as a prefix so two "Manager" roles from
446
+ * different levels stay distinguishable.
447
+ */
448
+ declare function flattenRecipientCatalog(groups: NotificationRecipientGroup[], lang: string): NotificationRecipientOptionGroup[];
449
+ declare function templateStatus(template: NotificationTemplateItem): NotificationTemplateStatus;
450
+ /** The retained publication that is live right now, if it is in the loaded history. */
451
+ declare function currentPublication(template: NotificationTemplateItem, publications: NotificationPublication[]): NotificationPublication | null;
452
+ /**
453
+ * Whether the saved draft differs from what is published. Comparing
454
+ * `draftRevision` to `publishedRevision` is wrong (they count different
455
+ * things); the contract compares the draft against the `sourceDraftRevision`
456
+ * of the current publication. Null when that cannot be known yet.
457
+ */
458
+ declare function hasUnpublishedChanges(template: NotificationTemplateItem, publications: NotificationPublication[]): boolean | null;
459
+ /** The default slot that owns this template, if any (owned defaults need a language review). */
460
+ declare function isDefaultOwned(template: NotificationTemplateItem, defaults: NotificationDefaults | null): boolean;
461
+ /** Channels with a recipe but no slot at all — the only ones "Add defaults" may create. */
462
+ declare function missingDefaultChannels(defaults: NotificationDefaults | null): NotificationChannel[];
463
+ declare function parseAppContent(value: string | null | undefined): TranslatableContent;
464
+ /** Maps retained publication content onto the event preview request. */
465
+ declare function publicationPreviewRequest(content: NotificationPublishedContent): NotificationPreviewRequest;
466
+ declare function templatePreviewRequest(template: Pick<NotificationTemplateItem, 'channel' | 'subject' | 'emailTemplate' | 'smsTemplate' | 'appTemplate'>): NotificationPreviewRequest;
467
+ /**
468
+ * Readable sample values for a preview: every field the content uses shows as
469
+ * its own name in brackets (`[Item name]`) instead of the server's "-", and the
470
+ * record link resolves. Only keys in the event's catalog are sent — the server
471
+ * rejects any other sample key — and never more than the server's limit.
472
+ */
473
+ declare function previewSamples(request: NotificationPreviewRequest, properties: readonly NotificationPropertyGroup[], lang: string): Record<string, string>;
474
+ declare function withPreviewSamples(request: NotificationPreviewRequest, properties: readonly NotificationPropertyGroup[], lang: string): NotificationPreviewRequest;
475
+ /** The system principal that publishes generated App defaults. */
476
+ declare const DEFAULTS_PUBLISHER = "system:notification-defaults";
477
+
478
+ declare class NotificationState {
479
+ private readonly api;
219
480
  static getEvents(state: NotificationStateModel): NotificationEvent[];
220
481
  static getTemplates(state: NotificationStateModel): NotificationTemplateItem[];
221
- static getReceivers(state: NotificationStateModel): ReceiverItem[];
222
- static getReceiverGroups(state: NotificationStateModel): ReceiverOptionGroup[];
223
- static getPropertyGroups(state: NotificationStateModel): PropertyGroup[];
224
- static getSelectedEventName(state: NotificationStateModel): string | null;
225
- static getSelectedTemplateId(state: NotificationStateModel): string | number | null;
482
+ static getSelectedEventId(state: NotificationStateModel): number | null;
483
+ static getSelectedTemplateId(state: NotificationStateModel): number | null;
484
+ static getContext(state: NotificationStateModel): NotificationAuthoringContext | null;
485
+ static getDefaults(state: NotificationStateModel): NotificationDefaults | null;
486
+ static getPublicationsSlice(state: NotificationStateModel): {
487
+ templateId: number | null;
488
+ items: NotificationPublication[];
489
+ };
226
490
  static getLoadingActive(state: NotificationStateModel): string[];
227
491
  static getErrors(state: NotificationStateModel): Record<string, string | null>;
228
- static getSelectedEvent(events: NotificationEvent[], selectedEventName: string | null): NotificationEvent | null;
229
- static getEventTemplates(templates: NotificationTemplateItem[], selectedEvent: NotificationEvent | null): NotificationTemplateItem[];
230
- static getSelectedTemplate(templates: NotificationTemplateItem[], selectedTemplateId: string | number | null): NotificationTemplateItem | null;
231
- static getAvailableReceivers(receivers: ReceiverItem[], selectedTemplate: NotificationTemplateItem | null): ReceiverItem[];
232
- static getAvailableReceiverGroups(receiverGroups: ReceiverOptionGroup[], selectedTemplate: NotificationTemplateItem | null): ReceiverOptionGroup[];
233
- /** API returns events with nested templates - we flatten for state */
234
- private flattenEventsResponse;
235
- private normalizeReceiverGroups;
236
- private flattenReceiverItems;
237
- private hasOwnField;
238
- private mergeUpdatedTemplate;
492
+ static getSelectedEvent(events: NotificationEvent[], selectedEventId: number | null): NotificationEvent | null;
493
+ static getEventTemplates(templates: NotificationTemplateItem[], selectedEventId: number | null): NotificationTemplateItem[];
494
+ static getSelectedTemplate(templates: NotificationTemplateItem[], selectedTemplateId: number | null): NotificationTemplateItem | null;
495
+ static getPublications(slice: {
496
+ templateId: number | null;
497
+ items: NotificationPublication[];
498
+ }, selectedTemplateId: number | null): NotificationPublication[];
239
499
  setModuleInfo(ctx: StateContext<NotificationStateModel>, action: SetModuleInfo): void;
240
500
  resetState(ctx: StateContext<NotificationStateModel>): void;
241
- getEvents(ctx: StateContext<NotificationStateModel>): rxjs.Observable<Response<(NotificationEvent & {
242
- templates: NotificationTemplateItem[];
243
- })[]>>;
244
- getReceivers(ctx: StateContext<NotificationStateModel>): rxjs.Observable<Response<ReceiverGroup[]>>;
245
- getProperties(ctx: StateContext<NotificationStateModel>, action: GetProperties): rxjs.Observable<Response<PropertyGroup[]>>;
246
- setActionMode(ctx: StateContext<NotificationStateModel>, action: SetActionMode): void;
247
- getActionReceivers(ctx: StateContext<NotificationStateModel>, action: GetActionReceivers): rxjs.Observable<Response<ReceiverGroup[]>>;
248
- getActionProperties(ctx: StateContext<NotificationStateModel>, action: GetActionProperties): rxjs.Observable<Response<PropertyGroup[]>>;
249
- selectEvent(ctx: StateContext<NotificationStateModel>, action: SelectEvent): void;
250
- selectTemplate(ctx: StateContext<NotificationStateModel>, action: SelectTemplate): void;
251
- addReceivers(ctx: StateContext<NotificationStateModel>, action: AddReceivers): rxjs.Observable<Response<ReceiverItem[]>>;
252
- deleteReceiver(ctx: StateContext<NotificationStateModel>, action: DeleteReceiver): rxjs.Observable<Response<void>> | undefined;
253
- addTemplate(ctx: StateContext<NotificationStateModel>, action: AddTemplate): rxjs.Observable<Response<NotificationTemplateItem>>;
254
- updateTemplate(ctx: StateContext<NotificationStateModel>, action: UpdateTemplate): rxjs.Observable<Response<Partial<NotificationTemplateItem>>>;
255
- deleteTemplate(ctx: StateContext<NotificationStateModel>, action: DeleteTemplate): rxjs.Observable<Response<void>>;
256
- private getSelectedTemplate;
501
+ getEvents(ctx: StateContext<NotificationStateModel>): rxjs.Observable<NotificationEventWithTemplates[]> | undefined;
502
+ getAuthoringContext(ctx: StateContext<NotificationStateModel>, action: GetAuthoringContext): rxjs.Observable<NotificationAuthoringContext>;
503
+ getDefaultRecipes(ctx: StateContext<NotificationStateModel>, action: GetDefaultRecipes): rxjs.Observable<NotificationDefaults>;
504
+ getPublications(ctx: StateContext<NotificationStateModel>, action: GetPublications): rxjs.Observable<NotificationPublication[]> | undefined;
505
+ selectEvent(ctx: StateContext<NotificationStateModel>, action: SelectEvent): rxjs.Observable<void> | undefined;
506
+ selectTemplate(ctx: StateContext<NotificationStateModel>, action: SelectTemplate): rxjs.Observable<void> | undefined;
507
+ createTemplate(ctx: StateContext<NotificationStateModel>, action: CreateTemplate): rxjs.Observable<NotificationTemplateItem>;
508
+ saveTemplate(ctx: StateContext<NotificationStateModel>, action: SaveTemplate): rxjs.Observable<NotificationTemplateItem>;
509
+ addReceivers(ctx: StateContext<NotificationStateModel>, action: AddReceivers): rxjs.Observable<NotificationTemplateItem>;
510
+ deleteReceiver(ctx: StateContext<NotificationStateModel>, action: DeleteReceiver): rxjs.Observable<NotificationTemplateItem>;
511
+ deleteTemplate(ctx: StateContext<NotificationStateModel>, action: DeleteTemplate): rxjs.Observable<unknown>;
512
+ publishTemplate(ctx: StateContext<NotificationStateModel>, action: PublishTemplate): rxjs.Observable<NotificationTemplateItem>;
513
+ setTemplateActivation(ctx: StateContext<NotificationStateModel>, action: SetTemplateActivation): rxjs.Observable<NotificationTemplateItem>;
514
+ restorePublication(ctx: StateContext<NotificationStateModel>, action: RestorePublication): rxjs.Observable<NotificationTemplateItem>;
515
+ initializeDefaults(ctx: StateContext<NotificationStateModel>, action: InitializeDefaults): rxjs.Observable<_masterteam_notification.NotificationInitializeDefaultsResult>;
516
+ restoreDefault(ctx: StateContext<NotificationStateModel>, action: RestoreDefault): rxjs.Observable<NotificationTemplateItem>;
257
517
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NotificationState, never>;
258
518
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<NotificationState>;
259
519
  }
260
520
 
261
521
  declare class NotificationFacade {
262
522
  private readonly store;
523
+ private readonly api;
263
524
  readonly events: _angular_core.Signal<_masterteam_notification.NotificationEvent[]>;
525
+ /** Templates of the selected event. */
264
526
  readonly templates: _angular_core.Signal<_masterteam_notification.NotificationTemplateItem[]>;
265
- readonly receivers: _angular_core.Signal<_masterteam_notification.ReceiverItem[]>;
266
- readonly receiverGroups: _angular_core.Signal<_masterteam_notification.ReceiverOptionGroup[]>;
267
- readonly propertyGroups: _angular_core.Signal<_masterteam_notification.PropertyGroup[]>;
268
- readonly selectedEventName: _angular_core.Signal<string | null>;
269
- readonly selectedTemplateId: _angular_core.Signal<string | number | null>;
527
+ readonly selectedEventId: _angular_core.Signal<number | null>;
528
+ readonly selectedTemplateId: _angular_core.Signal<number | null>;
270
529
  readonly selectedEvent: _angular_core.Signal<_masterteam_notification.NotificationEvent | null>;
271
530
  readonly selectedTemplate: _angular_core.Signal<_masterteam_notification.NotificationTemplateItem | null>;
272
- readonly availableReceivers: _angular_core.Signal<_masterteam_notification.ReceiverItem[]>;
273
- readonly availableReceiverGroups: _angular_core.Signal<_masterteam_notification.ReceiverOptionGroup[]>;
531
+ readonly context: _angular_core.Signal<_masterteam_notification.NotificationAuthoringContext | null>;
532
+ readonly defaults: _angular_core.Signal<_masterteam_notification.NotificationDefaults | null>;
533
+ readonly publications: _angular_core.Signal<_masterteam_notification.NotificationPublication[]>;
274
534
  private readonly loadingActive;
275
535
  private readonly errors;
276
536
  readonly isLoadingEvents: _angular_core.Signal<boolean>;
277
- readonly isLoadingReceivers: _angular_core.Signal<boolean>;
278
- readonly isLoadingProperties: _angular_core.Signal<boolean>;
279
- readonly isAddingReceivers: _angular_core.Signal<boolean>;
280
- readonly isDeletingReceiver: _angular_core.Signal<boolean>;
281
- readonly isAddingTemplate: _angular_core.Signal<boolean>;
282
- readonly isUpdatingTemplate: _angular_core.Signal<boolean>;
283
- readonly isDeletingTemplate: _angular_core.Signal<boolean>;
537
+ readonly isLoadingContext: _angular_core.Signal<boolean>;
538
+ readonly isLoadingDefaults: _angular_core.Signal<boolean>;
539
+ readonly isLoadingPublications: _angular_core.Signal<boolean>;
540
+ /** True while any write is in flight — every write control disables on it. */
541
+ readonly isMutating: _angular_core.Signal<boolean>;
284
542
  readonly eventsError: _angular_core.Signal<string | null>;
285
- readonly receiversError: _angular_core.Signal<string | null>;
286
- readonly propertiesError: _angular_core.Signal<string | null>;
543
+ readonly contextError: _angular_core.Signal<string | null>;
544
+ readonly defaultsError: _angular_core.Signal<string | null>;
545
+ /** Accepts the enum or its string value, so templates can name the key. */
546
+ isLoading(key: NotificationActionKey | `${NotificationActionKey}`): boolean;
287
547
  /**
288
548
  * Scopes this (shared, singleton) state to a module/event context.
289
549
  *
@@ -295,145 +555,367 @@ declare class NotificationFacade {
295
555
  * from the old one. See `workflow-builder.ts` / `escalation-builder.ts`
296
556
  * for the reference call sites.
297
557
  */
298
- setModuleInfo(moduleType: string, moduleId: string | number, parentModuleType?: string, parentModuleId?: string | number, parentPath?: string): rxjs.Observable<void>;
299
- resetState(): rxjs.Observable<void>;
300
- loadEvents(): rxjs.Observable<void>;
301
- loadReceivers(): rxjs.Observable<void>;
302
- loadProperties(isMultiLang?: boolean): rxjs.Observable<void>;
303
- loadPropertiesForCurrentContext(isMultiLang?: boolean): rxjs.Observable<void>;
304
- setActionMode(actionId: string | number | null): rxjs.Observable<void>;
305
- loadActionReceivers(actionId: string | number): rxjs.Observable<void>;
306
- loadActionProperties(actionId: string | number, isMultiLang?: boolean): rxjs.Observable<void>;
307
- selectEvent(eventName: string): rxjs.Observable<void>;
308
- selectTemplate(templateId: string | number): rxjs.Observable<void>;
309
- addReceivers(templateId: string | number, payload: AddReceiversPayload): rxjs.Observable<void>;
310
- deleteReceiver(receiverId: string | number): rxjs.Observable<void>;
311
- addTemplate(eventId: string | number, payload: AddTemplatePayload): rxjs.Observable<void>;
312
- updateTemplate(templateId: string | number, payload: UpdateTemplatePayload): rxjs.Observable<void>;
313
- deleteTemplate(templateId: string | number): rxjs.Observable<void>;
314
- loadBaseData(): void;
315
- /**
316
- * Load all data for normal mode (non-workflow)
317
- */
318
- loadAllData(isMultiLang?: boolean): void;
558
+ setModuleInfo(moduleType: string, moduleId: string | number, parentModuleType?: string, parentModuleId?: string | number, parentPath?: string): Observable<void>;
559
+ resetState(): Observable<void>;
560
+ /** Loads the scope's events; the selected event's catalog and defaults follow. */
561
+ loadEvents(): Observable<void>;
562
+ /** Kept for existing hosts — both load the scope's events and what follows from them. */
563
+ loadBaseData(): Observable<void>;
564
+ loadAllData(): Observable<void>;
565
+ selectEvent(eventId: number): Observable<void>;
566
+ selectTemplate(templateId: number): Observable<void>;
567
+ reloadDefaults(eventId: number): Observable<void>;
568
+ createTemplate(eventId: number, payload: CreateNotificationTemplatePayload): Observable<void>;
569
+ saveTemplate(templateId: number, payload: SaveNotificationTemplatePayload): Observable<void>;
570
+ deleteTemplate(templateId: number, expectedRevision: number): Observable<void>;
571
+ addReceivers(templateId: number, expectedRevision: number, receivers: NotificationReceiverRef[]): Observable<void>;
572
+ deleteReceiver(receiverId: number, expectedRevision: number): Observable<void>;
573
+ publish(templateId: number, expectedRevision: number, reviews: NotificationReviewFlags): Observable<void>;
574
+ turnOn(templateId: number, expectedRevision: number, reviews: NotificationReviewFlags): Observable<void>;
575
+ turnOff(templateId: number, expectedRevision: number): Observable<void>;
576
+ restorePublication(templateId: number, expectedRevision: number, publicationRevision: number, reviews: NotificationReviewFlags): Observable<void>;
577
+ initializeDefaults(eventId: number): Observable<void>;
578
+ restoreDefault(eventId: number, payload: RestoreNotificationDefaultPayload): Observable<void>;
579
+ preview(eventId: number, request: NotificationPreviewRequest): Observable<NotificationPreview>;
319
580
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NotificationFacade, never>;
320
581
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<NotificationFacade>;
321
582
  }
322
583
 
584
+ type NotificationPreviewStatus = 'idle' | 'updating' | 'ready' | 'error';
585
+ /**
586
+ * One preview pipeline per editor. A new request cancels the one in flight,
587
+ * a late answer for an older draft is dropped (`switchMap`), and Retry
588
+ * re-renders the exact request that failed — so retrying a history preview
589
+ * keeps the selected publication instead of falling back to the draft.
590
+ * Previews never save, publish, enable or send anything.
591
+ */
592
+ declare class NotificationPreviewRunner {
593
+ readonly status: _angular_core.WritableSignal<NotificationPreviewStatus>;
594
+ /** Last successful render — kept on screen while the next one runs. */
595
+ readonly result: _angular_core.WritableSignal<NotificationPreview | null>;
596
+ readonly errorCode: _angular_core.WritableSignal<string | null>;
597
+ /** Identity of the request `result` was rendered from. */
598
+ readonly resultKey: _angular_core.WritableSignal<string | null>;
599
+ private readonly immediate$;
600
+ private readonly debounced$;
601
+ private readonly retry$;
602
+ private lastJob;
603
+ /** Bumped by every request, so a pending debounced one can tell it was overtaken. */
604
+ private seq;
605
+ constructor(facade: NotificationFacade, destroyRef: DestroyRef, debounceMs?: number);
606
+ /** Render now (opening a review, selecting a history entry). */
607
+ run(eventId: number, request: NotificationPreviewRequest): void;
608
+ /** Render after a typing pause. */
609
+ schedule(eventId: number, request: NotificationPreviewRequest): void;
610
+ retry(): void;
611
+ clear(): void;
612
+ isCurrent(eventId: number, request: NotificationPreviewRequest): boolean;
613
+ }
614
+
615
+ type ContentField = 'emailTemplate' | 'smsTemplate' | 'appEn' | 'appAr';
616
+ /**
617
+ * Control Panel notification templates for one scope (Level, Module,
618
+ * LevelModule, Request/Step, EscalationRequest/Step, Delegation).
619
+ *
620
+ * Saving and previewing a draft never changes what is sent: only Publish
621
+ * (snapshot the saved draft) and Turn on/off do, and both go through an
622
+ * explicit review of the actual content and recipients first. Every write
623
+ * carries the last revision read; a conflict keeps the user's edits.
624
+ */
323
625
  declare class NotificationTemplate {
324
- private readonly facade;
626
+ protected readonly facade: NotificationFacade;
627
+ private readonly messages;
325
628
  private readonly transloco;
326
- private readonly confirmationService;
327
- private readonly modalService;
629
+ private readonly confirmation;
630
+ private readonly modal;
328
631
  private readonly toast;
329
- readonly events: _angular_core.Signal<_masterteam_notification.NotificationEvent[]>;
330
- readonly templates: _angular_core.Signal<NotificationTemplateItem[]>;
331
- readonly receivers: _angular_core.Signal<ReceiverItem[]>;
332
- readonly propertyGroups: _angular_core.Signal<_masterteam_notification.PropertyGroup[]>;
333
- readonly selectedEventName: _angular_core.Signal<string | null>;
334
- readonly selectedTemplateId: _angular_core.Signal<string | number | null>;
335
- readonly selectedEvent: _angular_core.Signal<_masterteam_notification.NotificationEvent | null>;
336
- readonly selectedTemplate: _angular_core.Signal<NotificationTemplateItem | null>;
337
- readonly availableReceivers: _angular_core.Signal<ReceiverItem[]>;
338
- readonly availableReceiverGroups: _angular_core.Signal<_masterteam_notification.ReceiverOptionGroup[]>;
339
- readonly isLoadingEvents: _angular_core.Signal<boolean>;
340
- readonly isLoadingReceivers: _angular_core.Signal<boolean>;
341
- readonly isLoadingProperties: _angular_core.Signal<boolean>;
342
- readonly isUpdatingTemplate: _angular_core.Signal<boolean>;
343
- readonly isDeletingTemplate: _angular_core.Signal<boolean>;
344
- readonly isAddingTemplate: _angular_core.Signal<boolean>;
345
- readonly eventsError: _angular_core.Signal<string | null>;
346
- readonly propertiesError: _angular_core.Signal<string | null>;
347
- readonly deletingReceiverIds: _angular_core.WritableSignal<(string | number)[]>;
348
- readonly addingReceiverIds: _angular_core.WritableSignal<(string | number)[]>;
349
- readonly form: FormGroup<any>;
350
- readonly propertyPickerControl: FormControl<string | null>;
351
- readonly receiverPickerControl: FormControl<string | number | null>;
352
- private readonly channelControl;
353
- readonly channelSignal: _angular_core.Signal<NotificationChannel>;
354
- private buildForm;
355
- readonly channelConfigs: ChannelConfig[];
356
- /** Check if channel should show bilingual fields - reads from form */
357
- isBilingual(ch: ChannelConfig): boolean;
358
- readonly channelOptions: _angular_core.Signal<{
359
- name: string;
360
- key: NotificationChannel;
361
- labelKey: string;
362
- supportsBilingual: boolean;
363
- bilingualOptional: boolean;
364
- useRichEditor: boolean;
365
- templateKey?: string;
366
- translatableKey?: string;
632
+ private readonly destroyRef;
633
+ private readonly lang;
634
+ protected readonly events: _angular_core.Signal<_masterteam_notification.NotificationEvent[]>;
635
+ protected readonly templates: _angular_core.Signal<NotificationTemplateItem[]>;
636
+ protected readonly selectedEvent: _angular_core.Signal<_masterteam_notification.NotificationEvent | null>;
637
+ protected readonly selectedEventId: _angular_core.Signal<number | null>;
638
+ protected readonly selectedTemplateId: _angular_core.Signal<number | null>;
639
+ protected readonly template: _angular_core.Signal<NotificationTemplateItem | null>;
640
+ protected readonly context: _angular_core.Signal<_masterteam_notification.NotificationAuthoringContext | null>;
641
+ protected readonly defaults: _angular_core.Signal<_masterteam_notification.NotificationDefaults | null>;
642
+ protected readonly publications: _angular_core.Signal<NotificationPublication[]>;
643
+ protected readonly isMutating: _angular_core.Signal<boolean>;
644
+ protected readonly eventControl: FormControl<number | null>;
645
+ protected readonly placeholderControl: FormControl<string | null>;
646
+ protected readonly recipientControl: FormControl<string | null>;
647
+ protected readonly form: FormGroup<{
648
+ name: FormControl<string>;
649
+ subject: FormControl<string>;
650
+ emailTemplate: FormControl<string>;
651
+ smsTemplate: FormControl<string>;
652
+ appEn: FormControl<string>;
653
+ appAr: FormControl<string>;
654
+ conditionFormula: FormControl<string>;
655
+ }>;
656
+ private readonly formValue;
657
+ /** Mirrors the tabs' own `active` model so a cancelled switch can snap back. */
658
+ protected readonly activeTab: _angular_core.WritableSignal<number | null>;
659
+ protected readonly activeField: _angular_core.WritableSignal<ContentField>;
660
+ protected readonly channelIcons: {
661
+ readonly App: "alert.bell-01";
662
+ readonly Email: "communication.mail-01";
663
+ readonly Sms: "communication.message-text-square-01";
664
+ };
665
+ private readonly host;
666
+ /** Editor and preview side by side; see the ResizeObserver in the constructor. */
667
+ protected readonly wide: _angular_core.WritableSignal<boolean>;
668
+ protected readonly conflict: _angular_core.WritableSignal<boolean>;
669
+ protected readonly writeDiagnostics: _angular_core.WritableSignal<NotificationDiagnostic[]>;
670
+ protected readonly preview: NotificationPreviewRunner;
671
+ /** A newly opened template renders at once instead of after the typing pause. */
672
+ private renderPreviewNow;
673
+ private readonly emailEditor;
674
+ private readonly appEnEditor;
675
+ private readonly appArEditor;
676
+ private readonly smsSource;
677
+ protected readonly channel: _angular_core.Signal<NotificationChannel | null>;
678
+ protected readonly status: _angular_core.Signal<_masterteam_notification.NotificationTemplateStatus | null>;
679
+ protected readonly unpublishedChanges: _angular_core.Signal<boolean | null>;
680
+ protected readonly defaultOwned: _angular_core.Signal<boolean>;
681
+ /** Unsaved edits, measured against the server baseline rather than control flags. */
682
+ protected readonly dirty: _angular_core.Signal<boolean>;
683
+ protected readonly nameValid: _angular_core.Signal<boolean>;
684
+ protected readonly canSave: _angular_core.Signal<boolean>;
685
+ protected readonly canPublish: _angular_core.Signal<boolean>;
686
+ protected readonly canTurnOn: _angular_core.Signal<boolean>;
687
+ protected readonly templateTabs: _angular_core.Signal<{
688
+ label: string;
689
+ value: number;
690
+ icon: "alert.bell-01" | "communication.mail-01" | "communication.message-text-square-01";
691
+ }[]>;
692
+ protected readonly placeholderGroups: _angular_core.Signal<{
693
+ groupName: string;
694
+ properties: {
695
+ key: string;
696
+ label: string;
697
+ }[];
367
698
  }[]>;
368
- /** Templates of the selected event, rendered as tabs. */
369
- readonly templateTabs: _angular_core.Signal<{
699
+ /** Image hosts the server accepts, as reported by the latest email preview. */
700
+ protected readonly emailImageOrigins: _angular_core.Signal<string[]>;
701
+ /** Image hosts the server accepts in App messages, from the latest App preview. */
702
+ protected readonly appImageOrigins: _angular_core.Signal<string[]>;
703
+ /** Catalog names by `type:id`, so saved receivers read as the catalog does. */
704
+ private readonly receiverLabels;
705
+ /** Field labels for the email editor's placeholder chips. */
706
+ protected readonly placeholderOptions: _angular_core.Signal<{
707
+ key: string;
370
708
  label: string;
371
- value: string | number;
372
- isSystem: boolean;
373
709
  }[]>;
374
- readonly activeChannel: _angular_core.Signal<NotificationChannel>;
375
- readonly shouldUseMultilingualProperties: _angular_core.Signal<boolean>;
376
- readonly isFormInvalid: _angular_core.Signal<boolean>;
710
+ protected readonly recipientGroups: _angular_core.Signal<{
711
+ items: _masterteam_notification.NotificationRecipientOption[];
712
+ groupName: string;
713
+ }[]>;
714
+ protected readonly conditionPropertiesByPath: _angular_core.Signal<{
715
+ Current: {
716
+ key: string;
717
+ name: string;
718
+ }[];
719
+ }>;
720
+ protected readonly conditionKeys: _angular_core.Signal<string[]>;
721
+ protected readonly missingDefaults: _angular_core.Signal<NotificationChannel[]>;
722
+ protected readonly removedDefaults: _angular_core.Signal<{
723
+ slot: NotificationDefaultSlot;
724
+ recipe: NotificationDefaultRecipe;
725
+ }[]>;
726
+ /** What the live preview renders: the editor's current, unsaved values. */
727
+ private readonly previewRequest;
728
+ protected readonly previewStale: _angular_core.Signal<boolean>;
729
+ constructor();
730
+ protected onBeforeUnload(event: BeforeUnloadEvent): void;
731
+ protected onEventChange(eventId: number | null): void;
732
+ protected onTemplateTabChange(templateId: number): void;
733
+ protected retryLoad(): void;
734
+ protected setActiveField(field: ContentField): void;
735
+ protected onInsertPlaceholder(key: string | null): void;
736
+ protected onSave(): void;
737
+ protected onDiscard(): void;
738
+ protected onAddRecipient(key: string | null): void;
739
+ protected onRemoveRecipient(receiver: NotificationReceiver): void;
377
740
  /**
378
- * Snapshot of the form's default shape, captured before any context ever
379
- * patches it. Used by {@link resetTemplateFields} to clear the form when the
380
- * selected template resolves to nothing, so a component switching context
381
- * never keeps displaying a previous context's values (see
382
- * `ResetNotificationState` for the store-level counterpart of this reset).
741
+ * The catalog's name first: ready-made templates come back with the raw
742
+ * identifier as their saved name (`creator`), while the catalog has the
743
+ * localized label.
383
744
  */
384
- private readonly emptyFormValue;
385
- constructor();
386
- onEventChange(eventName: string): void;
387
- onTemplateTabChange(templateId: string | number): void;
388
- onAddProperty(propertyKey: string): void;
389
- onAddReceiver(receiverId: string | number): void;
390
- onDeleteReceiver(receiver: ReceiverItem): void;
391
- isReceiverDeleting(receiverId: string | number): boolean;
392
- onSaveTemplate(): void;
393
- onTestEmail(): void;
394
- onDeleteTemplate(): void;
395
- onAddTemplateClick(): void;
745
+ protected receiverName(receiver: {
746
+ type: NotificationReceiver['type'];
747
+ identifier: string | null;
748
+ name?: NotificationReceiver['name'] | string | null;
749
+ }): string;
750
+ protected onPublish(): void;
751
+ protected onTurnOn(): void;
752
+ protected onTurnOff(): void;
753
+ protected onOpenHistory(): void;
754
+ private restoreVersion;
755
+ protected onDelete(): void;
756
+ protected onAddTemplate(): void;
757
+ protected onTestEmail(): void;
758
+ protected onInitializeDefaults(): void;
759
+ protected onRestoreDefault(channel: NotificationChannel): void;
396
760
  /**
397
- * Clears every template-owned control back to the captured empty shape.
398
- *
399
- * `event` is deliberately excluded, so `patchValue` leaves whatever the event
400
- * effect (or the user's own pick) put in that control. An event with no
401
- * templates yet — a brand-new one, or one whose last template was just
402
- * deleted — resolves `selectedTemplate` to null, and this effect runs after
403
- * the event effect in the same flush: resetting the whole group there would
404
- * blank the event picker the user just used, while `selectedEvent()` stays
405
- * truthy and keeps the tabs/add-template row visible. Leaving the control
406
- * alone also keeps the two effects ordering-independent — on a full
407
- * `ResetNotificationState` the event effect has already nulled it, and that
408
- * null is carried through.
409
- *
410
- * `channel` is restored through {@link channelControl} with events enabled
411
- * because `channelSignal` is a `toSignal` of its `valueChanges` and is the
412
- * only consumer of them on this form — a silent reset would leave
413
- * `activeChannel()` reporting the previous template's channel.
761
+ * After a revision conflict: re-read the latest version, then either keep
762
+ * the user's edits on top of it (the next Save applies them deliberately)
763
+ * or replace them with what is saved.
414
764
  */
415
- private resetTemplateFields;
416
- private appendToControl;
417
- private patchForm;
418
- private buildUpdatePayload;
419
- private getTemplateControlValue;
420
- private getTranslatableControlValue;
765
+ protected resolveConflict(keepMine: boolean): void;
766
+ protected describeDiagnostic(diagnostic: NotificationDiagnostic): string;
767
+ protected channelLabel(channel: NotificationChannel): string;
768
+ private afterWrite;
769
+ private onWriteError;
770
+ private review;
771
+ /** Readable sample values for every field the previewed content uses. */
772
+ private withSamples;
773
+ private confirmDiscard;
774
+ private patchFromTemplate;
775
+ /** Replacement semantics: only the template's own channel field is sent. */
776
+ private buildSavePayload;
421
777
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NotificationTemplate, never>;
422
778
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NotificationTemplate, "mt-notification-template", never, {}, {}, never, never, true, never>;
423
779
  }
424
780
 
781
+ interface AddTemplateDialogResult {
782
+ name: string;
783
+ channel: NotificationChannel;
784
+ }
785
+ /** A template's channel is chosen once, here — it cannot change afterwards. */
425
786
  declare class AddTemplateDialog {
426
787
  readonly modalService: ModalService;
427
788
  private readonly ref;
428
- data: _angular_core.InputSignal<any>;
429
- templateNameControl: FormControl<string | null>;
789
+ private readonly transloco;
790
+ /** Channels the event's authoring context allows. */
791
+ readonly channels: _angular_core.InputSignal<readonly NotificationChannel[]>;
792
+ readonly form: FormGroup<{
793
+ name: FormControl<string>;
794
+ channel: FormControl<NotificationChannel | null>;
795
+ }>;
796
+ protected readonly channelOptions: _angular_core.Signal<{
797
+ id: NotificationChannel;
798
+ name: string;
799
+ description: string;
800
+ icon: "alert.bell-01" | "communication.mail-01" | "communication.message-text-square-01";
801
+ }[]>;
430
802
  onSave(): void;
431
803
  onCancel(): void;
432
804
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AddTemplateDialog, never>;
433
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AddTemplateDialog, "mt-add-template-dialog", never, { "data": { "alias": "data"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
805
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AddTemplateDialog, "mt-add-template-dialog", never, { "channels": { "alias": "channels"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
806
+ }
807
+
808
+ type NotificationReviewMode = 'publish' | 'enable' | 'restore' | 'restoreDefault';
809
+ interface NotificationReviewRecipient {
810
+ name: string;
811
+ type: NotificationReceiverType;
812
+ }
813
+ /**
814
+ * The explicit review before a write changes what is sent: the actual content
815
+ * (rendered by the server) and the actual receivers of the version being
816
+ * published / turned on / restored — never the working draft's.
817
+ *
818
+ * The review flags are not separate tick boxes (product decision, 2026-10-27):
819
+ * confirming this dialog, which only enables once the server preview has
820
+ * rendered, is the acknowledgement. They are sent only from that confirmation,
821
+ * never as defaults elsewhere.
822
+ */
823
+ declare class TemplateReviewDialog implements OnInit {
824
+ protected readonly modalService: ModalService;
825
+ private readonly ref;
826
+ private readonly facade;
827
+ private readonly destroyRef;
828
+ readonly mode: _angular_core.InputSignal<NotificationReviewMode>;
829
+ readonly eventId: _angular_core.InputSignal<number>;
830
+ readonly request: _angular_core.InputSignal<NotificationPreviewRequest>;
831
+ readonly recipients: _angular_core.InputSignal<NotificationReviewRecipient[]>;
832
+ readonly condition: _angular_core.InputSignal<string | null>;
833
+ /** Templates a ready-made slot owns also need `languageReviewed`. */
834
+ readonly requireLanguageReview: _angular_core.InputSignal<boolean>;
835
+ /** Publishing an enabled template changes live sending at once. */
836
+ readonly isEnabled: _angular_core.InputSignal<boolean>;
837
+ protected readonly runner: NotificationPreviewRunner;
838
+ protected readonly previewReady: _angular_core.Signal<boolean>;
839
+ ngOnInit(): void;
840
+ protected confirm(): void;
841
+ protected cancel(): void;
842
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<TemplateReviewDialog, never>;
843
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TemplateReviewDialog, "mt-notification-template-review-dialog", never, { "mode": { "alias": "mode"; "required": true; "isSignal": true; }; "eventId": { "alias": "eventId"; "required": true; "isSignal": true; }; "request": { "alias": "request"; "required": true; "isSignal": true; }; "recipients": { "alias": "recipients"; "required": false; "isSignal": true; }; "condition": { "alias": "condition"; "required": false; "isSignal": true; }; "requireLanguageReview": { "alias": "requireLanguageReview"; "required": false; "isSignal": true; }; "isEnabled": { "alias": "isEnabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
844
+ }
845
+
846
+ interface NotificationHistoryRestoreRequest {
847
+ publication: NotificationPublication;
848
+ }
849
+ /**
850
+ * Retained publications of one template, newest first. Selecting a version
851
+ * previews that version's own content and recipients — never the draft's.
852
+ * "Restore" hands the chosen version back to the editor, which runs the
853
+ * review before anything is written.
854
+ */
855
+ declare class TemplateHistoryDrawer {
856
+ protected readonly modalService: ModalService;
857
+ private readonly ref;
858
+ protected readonly facade: NotificationFacade;
859
+ private readonly destroyRef;
860
+ private readonly transloco;
861
+ readonly eventId: _angular_core.InputSignal<number>;
862
+ /** Restoring is blocked while the editor holds unsaved edits. */
863
+ readonly draftDirty: _angular_core.InputSignal<boolean>;
864
+ readonly historyLimit: _angular_core.InputSignal<number | null>;
865
+ protected readonly runner: NotificationPreviewRunner;
866
+ protected readonly publications: _angular_core.Signal<NotificationPublication[]>;
867
+ protected readonly template: _angular_core.Signal<_masterteam_notification.NotificationTemplateItem | null>;
868
+ protected readonly selectedRevision: _angular_core.WritableSignal<number | null>;
869
+ protected readonly selected: _angular_core.Signal<NotificationPublication | null>;
870
+ protected readonly defaultsPublisher = "system:notification-defaults";
871
+ private readonly receiverLabels;
872
+ constructor();
873
+ protected select(publication: NotificationPublication): void;
874
+ /** The catalog's (localized) name; the snapshot keeps whatever was saved. */
875
+ protected recipientName(recipient: NotificationPublishedRecipient): string;
876
+ protected restore(): void;
877
+ protected close(): void;
878
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<TemplateHistoryDrawer, never>;
879
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<TemplateHistoryDrawer, "mt-notification-template-history-drawer", never, { "eventId": { "alias": "eventId"; "required": true; "isSignal": true; }; "draftDirty": { "alias": "draftDirty"; "required": false; "isSignal": true; }; "historyLimit": { "alias": "historyLimit"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
880
+ }
881
+
882
+ /**
883
+ * Renders a notification preview the way it will be read: Email inside the
884
+ * shared design, App as its English and Arabic messages, SMS as plain text.
885
+ * HTML only ever goes into the sandboxed `mt-email-html-preview` frame.
886
+ * Images always show: the server strips every image outside
887
+ * `approvedImageOrigins`, and the frame's CSP allows only those origins.
888
+ */
889
+ declare class NotificationContentPreview {
890
+ private readonly messages;
891
+ readonly preview: _angular_core.InputSignal<NotificationPreview | null>;
892
+ readonly status: _angular_core.InputSignal<NotificationPreviewStatus>;
893
+ readonly errorCode: _angular_core.InputSignal<string | null>;
894
+ /** The shown render is from an older version of the content. */
895
+ readonly stale: _angular_core.InputSignal<boolean>;
896
+ readonly height: _angular_core.InputSignal<string>;
897
+ /**
898
+ * Stretch the rendered frames to the height the host gives this preview
899
+ * (beside the editor), with `height` as the minimum.
900
+ */
901
+ readonly fill: _angular_core.InputSignal<boolean>;
902
+ /** Off beside the editor, where the Subject field is in view. */
903
+ readonly showSubject: _angular_core.InputSignal<boolean>;
904
+ readonly retry: _angular_core.OutputEmitterRef<void>;
905
+ protected readonly narrow: FormControl<boolean>;
906
+ protected readonly narrowValue: _angular_core.WritableSignal<boolean>;
907
+ protected readonly email: _angular_core.Signal<_masterteam_notification.NotificationEmailPreview | null>;
908
+ protected readonly app: _angular_core.Signal<_masterteam_notification.NotificationAppPreview | null>;
909
+ protected readonly sms: _angular_core.Signal<_masterteam_notification.NotificationSmsPreview | null>;
910
+ protected readonly diagnostics: _angular_core.Signal<NotificationDiagnostic[]>;
911
+ constructor();
912
+ protected describe(diagnostic: NotificationDiagnostic): string;
913
+ protected describeCode(code: string | null): string;
914
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<NotificationContentPreview, never>;
915
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<NotificationContentPreview, "mt-notification-content-preview", never, { "preview": { "alias": "preview"; "required": false; "isSignal": true; }; "status": { "alias": "status"; "required": false; "isSignal": true; }; "errorCode": { "alias": "errorCode"; "required": false; "isSignal": true; }; "stale": { "alias": "stale"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "fill": { "alias": "fill"; "required": false; "isSignal": true; }; "showSubject": { "alias": "showSubject"; "required": false; "isSignal": true; }; }, { "retry": "retry"; }, never, never, true, never>;
434
916
  }
435
917
 
436
918
  declare const APP_STATES: (typeof NotificationState)[];
437
919
 
438
- export { APP_STATES, AddReceivers, AddTemplate, AddTemplateDialog, CHANNEL_CONFIGS, DeleteReceiver, DeleteTemplate, GetActionProperties, GetActionReceivers, GetEvents, GetProperties, GetReceivers, NotificationActionKey, NotificationFacade, NotificationState, NotificationTemplate, ResetNotificationState, SelectEvent, SelectTemplate, SetActionMode, SetModuleInfo, UpdateTemplate, getChannelConfig, getFormControlName };
439
- export type { ActionGroup, AddReceiversPayload, AddTemplatePayload, ChannelConfig, NotificationAction, NotificationChannel, NotificationEvent, NotificationProperty, NotificationStateModel, NotificationTemplateItem, PropertyGroup, ReceiverGroup, ReceiverItem, ReceiverOptionGroup, ReceiverPayloadItem, ReceiverSubGroup, TranslatableContent, UpdateTemplatePayload };
920
+ export { APP_STATES, AddReceivers, AddTemplateDialog, CreateTemplate, DEFAULTS_PUBLISHER, DeleteReceiver, DeleteTemplate, GetAuthoringContext, GetDefaultRecipes, GetEvents, GetPublications, InitializeDefaults, NOTIFICATION_CHANNELS, NOTIFICATION_MUTATION_KEYS, NotificationActionKey, NotificationContentPreview, NotificationFacade, NotificationState, NotificationTemplate, NotificationTemplateApi, PublishTemplate, ResetNotificationState, RestoreDefault, RestorePublication, SaveTemplate, SelectEvent, SelectTemplate, SetModuleInfo, SetTemplateActivation, TemplateHistoryDrawer, TemplateReviewDialog, currentPublication, flattenRecipientCatalog, hasUnpublishedChanges, isDefaultOwned, isRevisionConflict, missingDefaultChannels, parseAppContent, previewSamples, publicationPreviewRequest, readNotificationFailure, recipientKey, templatePreviewRequest, templateStatus, withPreviewSamples };
921
+ export type { AddTemplateDialogResult, CreateNotificationTemplatePayload, NotificationAppPreview, NotificationAuthoringContext, NotificationChannel, NotificationDefaultRecipe, NotificationDefaultSlot, NotificationDefaults, NotificationDiagnostic, NotificationEmailPreview, NotificationEvent, NotificationEventWithTemplates, NotificationFailure, NotificationFragmentPreview, NotificationHistoryRestoreRequest, NotificationInitializeDefaultsResult, NotificationPreview, NotificationPreviewRequest, NotificationProperty, NotificationPropertyGroup, NotificationPublication, NotificationPublishedContent, NotificationPublishedRecipient, NotificationReceiver, NotificationReceiverRef, NotificationReceiverType, NotificationRecipeCoverage, NotificationRecipientGroup, NotificationRecipientNode, NotificationRecipientOption, NotificationRecipientOptionGroup, NotificationReviewFlags, NotificationReviewMode, NotificationReviewRecipient, NotificationSmsPreview, NotificationStateModel, NotificationTemplateItem, NotificationTemplateStatus, RestoreNotificationDefaultPayload, SaveNotificationTemplatePayload, TranslatableContent };