@masterteam/notification 0.0.27 → 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,118 +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
- groupName?: string;
83
+ nameEn: string;
84
+ nameAr: string;
66
85
  }
67
- interface PropertyGroup {
86
+ interface NotificationPropertyGroup {
68
87
  groupName: string;
69
88
  properties: NotificationProperty[];
70
89
  }
71
- interface NotificationTemplateItem {
72
- id: string | number;
73
- 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;
74
114
  name: string;
115
+ type: NotificationReceiverType;
75
116
  isSystem: boolean;
76
- channel: NotificationChannel;
77
- receivers: ReceiverItem[];
78
- emailTemplate: string;
79
- emailContentTranslatable: TranslatableContent;
80
- smsTemplate: string;
81
- appTemplate: TranslatableContent;
82
- conditionFormula?: string;
83
117
  }
84
- interface NotificationEvent {
85
- id: string | number;
86
- eventName: string;
118
+ interface NotificationPublishedContent {
119
+ formatVersion: number;
120
+ channel: NotificationChannel;
121
+ sourceDraftRevision: number;
87
122
  name: string;
88
- }
89
- interface NotificationAction {
90
- 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;
91
229
  name: string;
92
- type: string;
93
- }
94
- interface ActionGroup {
95
- activity: string;
96
- items: NotificationAction[];
97
- }
98
- interface ReceiverPayloadItem {
99
- id: string | number;
100
- 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;
101
246
  }
102
- interface AddReceiversPayload {
103
- receivers: ReceiverPayloadItem[];
247
+ interface NotificationReviewFlags {
248
+ recipientsReviewed: boolean;
249
+ languageReviewed: boolean;
104
250
  }
105
- interface AddTemplatePayload {
106
- name: string;
251
+ interface RestoreNotificationDefaultPayload {
107
252
  channel: NotificationChannel;
108
- }
109
- interface UpdateTemplatePayload {
110
- title: string;
111
- emailTemplate?: string;
112
- emailContentTranslatable?: TranslatableContent;
113
- smsTemplate?: string;
114
- appTemplate?: TranslatableContent;
115
- conditionFormula?: string;
253
+ expectedSlotRevision: number;
254
+ recipeHash: string;
116
255
  }
117
256
  interface NotificationStateModel extends LoadingStateShape<NotificationActionKey> {
118
257
  moduleType: string | null;
@@ -120,16 +259,14 @@ interface NotificationStateModel extends LoadingStateShape<NotificationActionKey
120
259
  parentModuleType: string | null;
121
260
  parentModuleId: string | number | null;
122
261
  parentPath: string;
123
- isActionMode: boolean;
124
- selectedActionId: string | number | null;
125
262
  events: NotificationEvent[];
126
263
  templates: NotificationTemplateItem[];
127
- actions: NotificationAction[];
128
- receivers: ReceiverItem[];
129
- receiverGroups: ReceiverOptionGroup[];
130
- propertyGroups: PropertyGroup[];
131
- selectedEventName: string | null;
132
- 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;
133
270
  }
134
271
 
135
272
  declare class SetModuleInfo {
@@ -141,147 +278,272 @@ declare class SetModuleInfo {
141
278
  static readonly type = "[Notification] Set Module Info";
142
279
  constructor(moduleType: string, moduleId: string | number, parentModuleType?: string | undefined, parentModuleId?: string | number | undefined, parentPath?: string | undefined);
143
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. */
144
285
  declare class GetEvents {
145
286
  static readonly type = "[Notification] Get Events";
146
287
  }
147
- declare class GetReceivers {
148
- static readonly type = "[Notification] Get Receivers";
149
- }
150
- declare class GetProperties {
151
- isMultiLang?: boolean | undefined;
152
- static readonly type = "[Notification] Get Properties";
153
- constructor(isMultiLang?: boolean | undefined);
154
- }
155
- declare class SetActionMode {
156
- actionId: string | number | null;
157
- static readonly type = "[Notification] Set Action Mode";
158
- constructor(actionId: string | number | null);
288
+ declare class GetAuthoringContext {
289
+ eventId: number;
290
+ static readonly type = "[Notification] Get Authoring Context";
291
+ constructor(eventId: number);
159
292
  }
160
- declare class GetActionReceivers {
161
- actionId: string | number;
162
- static readonly type = "[Notification] Get Action Receivers";
163
- constructor(actionId: string | number);
293
+ declare class GetDefaultRecipes {
294
+ eventId: number;
295
+ static readonly type = "[Notification] Get Default Recipes";
296
+ constructor(eventId: number);
164
297
  }
165
- declare class GetActionProperties {
166
- actionId: string | number;
167
- isMultiLang?: boolean | undefined;
168
- static readonly type = "[Notification] Get Action Properties";
169
- 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);
170
302
  }
171
303
  declare class SelectEvent {
172
- eventName: string;
304
+ eventId: number;
173
305
  static readonly type = "[Notification] Select Event";
174
- constructor(eventName: string);
306
+ constructor(eventId: number);
175
307
  }
176
308
  declare class SelectTemplate {
177
- templateId: string | number;
309
+ templateId: number;
178
310
  static readonly type = "[Notification] Select Template";
179
- constructor(templateId: string | number);
180
- }
181
- declare class AddReceivers {
182
- templateId: string | number;
183
- payload: AddReceiversPayload;
184
- static readonly type = "[Notification] Add Receivers";
185
- constructor(templateId: string | number, payload: AddReceiversPayload);
186
- }
187
- declare class DeleteReceiver {
188
- receiverId: string | number;
189
- static readonly type = "[Notification] Delete Receiver";
190
- constructor(receiverId: string | number);
311
+ constructor(templateId: number);
191
312
  }
192
- declare class AddTemplate {
193
- eventId: string | number;
194
- payload: AddTemplatePayload;
195
- static readonly type = "[Notification] Add Template";
196
- 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);
197
318
  }
198
- declare class UpdateTemplate {
199
- templateId: string | number;
200
- payload: UpdateTemplatePayload;
201
- static readonly type = "[Notification] Update Template";
202
- 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);
203
324
  }
204
325
  declare class DeleteTemplate {
205
- templateId: string | number;
326
+ templateId: number;
327
+ expectedRevision: number;
206
328
  static readonly type = "[Notification] Delete Template";
207
- constructor(templateId: string | number);
329
+ constructor(templateId: number, expectedRevision: number);
208
330
  }
209
- declare class ResetNotificationState {
210
- 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);
211
377
  }
212
378
 
213
- 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 {
214
386
  private readonly http;
215
387
  private readonly baseUrl;
216
- 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;
217
480
  static getEvents(state: NotificationStateModel): NotificationEvent[];
218
481
  static getTemplates(state: NotificationStateModel): NotificationTemplateItem[];
219
- static getReceivers(state: NotificationStateModel): ReceiverItem[];
220
- static getReceiverGroups(state: NotificationStateModel): ReceiverOptionGroup[];
221
- static getPropertyGroups(state: NotificationStateModel): PropertyGroup[];
222
- static getSelectedEventName(state: NotificationStateModel): string | null;
223
- 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
+ };
224
490
  static getLoadingActive(state: NotificationStateModel): string[];
225
491
  static getErrors(state: NotificationStateModel): Record<string, string | null>;
226
- static getSelectedEvent(events: NotificationEvent[], selectedEventName: string | null): NotificationEvent | null;
227
- static getEventTemplates(templates: NotificationTemplateItem[], selectedEvent: NotificationEvent | null): NotificationTemplateItem[];
228
- static getSelectedTemplate(templates: NotificationTemplateItem[], selectedTemplateId: string | number | null): NotificationTemplateItem | null;
229
- static getAvailableReceivers(receivers: ReceiverItem[], selectedTemplate: NotificationTemplateItem | null): ReceiverItem[];
230
- static getAvailableReceiverGroups(receiverGroups: ReceiverOptionGroup[], selectedTemplate: NotificationTemplateItem | null): ReceiverOptionGroup[];
231
- /** API returns events with nested templates - we flatten for state */
232
- private flattenEventsResponse;
233
- private normalizeReceiverGroups;
234
- private flattenReceiverItems;
235
- private hasOwnField;
236
- 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[];
237
499
  setModuleInfo(ctx: StateContext<NotificationStateModel>, action: SetModuleInfo): void;
238
500
  resetState(ctx: StateContext<NotificationStateModel>): void;
239
- getEvents(ctx: StateContext<NotificationStateModel>): rxjs.Observable<Response<(NotificationEvent & {
240
- templates: NotificationTemplateItem[];
241
- })[]>>;
242
- getReceivers(ctx: StateContext<NotificationStateModel>): rxjs.Observable<Response<ReceiverGroup[]>>;
243
- getProperties(ctx: StateContext<NotificationStateModel>, action: GetProperties): rxjs.Observable<Response<PropertyGroup[]>>;
244
- setActionMode(ctx: StateContext<NotificationStateModel>, action: SetActionMode): void;
245
- getActionReceivers(ctx: StateContext<NotificationStateModel>, action: GetActionReceivers): rxjs.Observable<Response<ReceiverGroup[]>>;
246
- getActionProperties(ctx: StateContext<NotificationStateModel>, action: GetActionProperties): rxjs.Observable<Response<PropertyGroup[]>>;
247
- selectEvent(ctx: StateContext<NotificationStateModel>, action: SelectEvent): void;
248
- selectTemplate(ctx: StateContext<NotificationStateModel>, action: SelectTemplate): void;
249
- addReceivers(ctx: StateContext<NotificationStateModel>, action: AddReceivers): rxjs.Observable<Response<ReceiverItem[]>>;
250
- deleteReceiver(ctx: StateContext<NotificationStateModel>, action: DeleteReceiver): rxjs.Observable<Response<void>> | undefined;
251
- addTemplate(ctx: StateContext<NotificationStateModel>, action: AddTemplate): rxjs.Observable<Response<NotificationTemplateItem>>;
252
- updateTemplate(ctx: StateContext<NotificationStateModel>, action: UpdateTemplate): rxjs.Observable<Response<Partial<NotificationTemplateItem>>>;
253
- deleteTemplate(ctx: StateContext<NotificationStateModel>, action: DeleteTemplate): rxjs.Observable<Response<void>>;
254
- 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>;
255
517
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NotificationState, never>;
256
518
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<NotificationState>;
257
519
  }
258
520
 
259
521
  declare class NotificationFacade {
260
522
  private readonly store;
523
+ private readonly api;
261
524
  readonly events: _angular_core.Signal<_masterteam_notification.NotificationEvent[]>;
525
+ /** Templates of the selected event. */
262
526
  readonly templates: _angular_core.Signal<_masterteam_notification.NotificationTemplateItem[]>;
263
- readonly receivers: _angular_core.Signal<_masterteam_notification.ReceiverItem[]>;
264
- readonly receiverGroups: _angular_core.Signal<_masterteam_notification.ReceiverOptionGroup[]>;
265
- readonly propertyGroups: _angular_core.Signal<_masterteam_notification.PropertyGroup[]>;
266
- readonly selectedEventName: _angular_core.Signal<string | null>;
267
- readonly selectedTemplateId: _angular_core.Signal<string | number | null>;
527
+ readonly selectedEventId: _angular_core.Signal<number | null>;
528
+ readonly selectedTemplateId: _angular_core.Signal<number | null>;
268
529
  readonly selectedEvent: _angular_core.Signal<_masterteam_notification.NotificationEvent | null>;
269
530
  readonly selectedTemplate: _angular_core.Signal<_masterteam_notification.NotificationTemplateItem | null>;
270
- readonly availableReceivers: _angular_core.Signal<_masterteam_notification.ReceiverItem[]>;
271
- 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[]>;
272
534
  private readonly loadingActive;
273
535
  private readonly errors;
274
536
  readonly isLoadingEvents: _angular_core.Signal<boolean>;
275
- readonly isLoadingReceivers: _angular_core.Signal<boolean>;
276
- readonly isLoadingProperties: _angular_core.Signal<boolean>;
277
- readonly isAddingReceivers: _angular_core.Signal<boolean>;
278
- readonly isDeletingReceiver: _angular_core.Signal<boolean>;
279
- readonly isAddingTemplate: _angular_core.Signal<boolean>;
280
- readonly isUpdatingTemplate: _angular_core.Signal<boolean>;
281
- 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>;
282
542
  readonly eventsError: _angular_core.Signal<string | null>;
283
- readonly receiversError: _angular_core.Signal<string | null>;
284
- 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;
285
547
  /**
286
548
  * Scopes this (shared, singleton) state to a module/event context.
287
549
  *
@@ -293,143 +555,367 @@ declare class NotificationFacade {
293
555
  * from the old one. See `workflow-builder.ts` / `escalation-builder.ts`
294
556
  * for the reference call sites.
295
557
  */
296
- setModuleInfo(moduleType: string, moduleId: string | number, parentModuleType?: string, parentModuleId?: string | number, parentPath?: string): rxjs.Observable<void>;
297
- resetState(): rxjs.Observable<void>;
298
- loadEvents(): rxjs.Observable<void>;
299
- loadReceivers(): rxjs.Observable<void>;
300
- loadProperties(isMultiLang?: boolean): rxjs.Observable<void>;
301
- loadPropertiesForCurrentContext(isMultiLang?: boolean): rxjs.Observable<void>;
302
- setActionMode(actionId: string | number | null): rxjs.Observable<void>;
303
- loadActionReceivers(actionId: string | number): rxjs.Observable<void>;
304
- loadActionProperties(actionId: string | number, isMultiLang?: boolean): rxjs.Observable<void>;
305
- selectEvent(eventName: string): rxjs.Observable<void>;
306
- selectTemplate(templateId: string | number): rxjs.Observable<void>;
307
- addReceivers(templateId: string | number, payload: AddReceiversPayload): rxjs.Observable<void>;
308
- deleteReceiver(receiverId: string | number): rxjs.Observable<void>;
309
- addTemplate(eventId: string | number, payload: AddTemplatePayload): rxjs.Observable<void>;
310
- updateTemplate(templateId: string | number, payload: UpdateTemplatePayload): rxjs.Observable<void>;
311
- deleteTemplate(templateId: string | number): rxjs.Observable<void>;
312
- loadBaseData(): void;
313
- /**
314
- * Load all data for normal mode (non-workflow)
315
- */
316
- 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>;
317
580
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NotificationFacade, never>;
318
581
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<NotificationFacade>;
319
582
  }
320
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
+ */
321
625
  declare class NotificationTemplate {
322
- private readonly facade;
626
+ protected readonly facade: NotificationFacade;
627
+ private readonly messages;
323
628
  private readonly transloco;
324
- private readonly confirmationService;
325
- private readonly modalService;
326
- readonly events: _angular_core.Signal<_masterteam_notification.NotificationEvent[]>;
327
- readonly templates: _angular_core.Signal<NotificationTemplateItem[]>;
328
- readonly receivers: _angular_core.Signal<ReceiverItem[]>;
329
- readonly propertyGroups: _angular_core.Signal<_masterteam_notification.PropertyGroup[]>;
330
- readonly selectedEventName: _angular_core.Signal<string | null>;
331
- readonly selectedTemplateId: _angular_core.Signal<string | number | null>;
332
- readonly selectedEvent: _angular_core.Signal<_masterteam_notification.NotificationEvent | null>;
333
- readonly selectedTemplate: _angular_core.Signal<NotificationTemplateItem | null>;
334
- readonly availableReceivers: _angular_core.Signal<ReceiverItem[]>;
335
- readonly availableReceiverGroups: _angular_core.Signal<_masterteam_notification.ReceiverOptionGroup[]>;
336
- readonly isLoadingEvents: _angular_core.Signal<boolean>;
337
- readonly isLoadingReceivers: _angular_core.Signal<boolean>;
338
- readonly isLoadingProperties: _angular_core.Signal<boolean>;
339
- readonly isUpdatingTemplate: _angular_core.Signal<boolean>;
340
- readonly isDeletingTemplate: _angular_core.Signal<boolean>;
341
- readonly isAddingTemplate: _angular_core.Signal<boolean>;
342
- readonly eventsError: _angular_core.Signal<string | null>;
343
- readonly propertiesError: _angular_core.Signal<string | null>;
344
- readonly deletingReceiverIds: _angular_core.WritableSignal<(string | number)[]>;
345
- readonly addingReceiverIds: _angular_core.WritableSignal<(string | number)[]>;
346
- readonly form: FormGroup<any>;
347
- readonly propertyPickerControl: FormControl<string | null>;
348
- readonly receiverPickerControl: FormControl<string | number | null>;
349
- private readonly channelControl;
350
- readonly channelSignal: _angular_core.Signal<NotificationChannel>;
351
- private buildForm;
352
- readonly channelConfigs: ChannelConfig[];
353
- /** Check if channel should show bilingual fields - reads from form */
354
- isBilingual(ch: ChannelConfig): boolean;
355
- readonly channelOptions: _angular_core.Signal<{
356
- name: string;
357
- key: NotificationChannel;
358
- labelKey: string;
359
- supportsBilingual: boolean;
360
- bilingualOptional: boolean;
361
- useRichEditor: boolean;
362
- templateKey?: string;
363
- translatableKey?: string;
629
+ private readonly confirmation;
630
+ private readonly modal;
631
+ private readonly toast;
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
+ }[];
364
698
  }[]>;
365
- /** Templates of the selected event, rendered as tabs. */
366
- 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;
367
708
  label: string;
368
- value: string | number;
369
- isSystem: boolean;
370
709
  }[]>;
371
- readonly activeChannel: _angular_core.Signal<NotificationChannel>;
372
- readonly shouldUseMultilingualProperties: _angular_core.Signal<boolean>;
373
- 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;
374
740
  /**
375
- * Snapshot of the form's default shape, captured before any context ever
376
- * patches it. Used by {@link resetTemplateFields} to clear the form when the
377
- * selected template resolves to nothing, so a component switching context
378
- * never keeps displaying a previous context's values (see
379
- * `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.
380
744
  */
381
- private readonly emptyFormValue;
382
- constructor();
383
- onEventChange(eventName: string): void;
384
- onTemplateTabChange(templateId: string | number): void;
385
- onAddProperty(propertyKey: string): void;
386
- onAddReceiver(receiverId: string | number): void;
387
- onDeleteReceiver(receiver: ReceiverItem): void;
388
- isReceiverDeleting(receiverId: string | number): boolean;
389
- onSaveTemplate(): void;
390
- onDeleteTemplate(): void;
391
- 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;
392
760
  /**
393
- * Clears every template-owned control back to the captured empty shape.
394
- *
395
- * `event` is deliberately excluded, so `patchValue` leaves whatever the event
396
- * effect (or the user's own pick) put in that control. An event with no
397
- * templates yet — a brand-new one, or one whose last template was just
398
- * deleted — resolves `selectedTemplate` to null, and this effect runs after
399
- * the event effect in the same flush: resetting the whole group there would
400
- * blank the event picker the user just used, while `selectedEvent()` stays
401
- * truthy and keeps the tabs/add-template row visible. Leaving the control
402
- * alone also keeps the two effects ordering-independent — on a full
403
- * `ResetNotificationState` the event effect has already nulled it, and that
404
- * null is carried through.
405
- *
406
- * `channel` is restored through {@link channelControl} with events enabled
407
- * because `channelSignal` is a `toSignal` of its `valueChanges` and is the
408
- * only consumer of them on this form — a silent reset would leave
409
- * `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.
410
764
  */
411
- private resetTemplateFields;
412
- private appendToControl;
413
- private patchForm;
414
- private buildUpdatePayload;
415
- private getTemplateControlValue;
416
- 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;
417
777
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<NotificationTemplate, never>;
418
778
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NotificationTemplate, "mt-notification-template", never, {}, {}, never, never, true, never>;
419
779
  }
420
780
 
781
+ interface AddTemplateDialogResult {
782
+ name: string;
783
+ channel: NotificationChannel;
784
+ }
785
+ /** A template's channel is chosen once, here — it cannot change afterwards. */
421
786
  declare class AddTemplateDialog {
422
787
  readonly modalService: ModalService;
423
788
  private readonly ref;
424
- data: _angular_core.InputSignal<any>;
425
- 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
+ }[]>;
426
802
  onSave(): void;
427
803
  onCancel(): void;
428
804
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AddTemplateDialog, never>;
429
- 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>;
430
916
  }
431
917
 
432
918
  declare const APP_STATES: (typeof NotificationState)[];
433
919
 
434
- 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 };
435
- 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 };