@memberjunction/ng-explorer-settings 6.2.0-edge.0 → 6.2.0-edge.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/account-info/account-info.component.d.ts +1 -1
- package/dist/lib/account-info/account-info.component.js +2 -2
- package/dist/lib/account-info/account-info.component.js.map +1 -1
- package/dist/lib/application-management/application-dialog/application-dialog.component.d.ts +92 -10
- package/dist/lib/application-management/application-dialog/application-dialog.component.d.ts.map +1 -1
- package/dist/lib/application-management/application-dialog/application-dialog.component.js +222 -76
- package/dist/lib/application-management/application-dialog/application-dialog.component.js.map +1 -1
- package/dist/lib/application-management/application-management.component.d.ts +92 -10
- package/dist/lib/application-management/application-management.component.d.ts.map +1 -1
- package/dist/lib/application-management/application-management.component.js +229 -65
- package/dist/lib/application-management/application-management.component.js.map +1 -1
- package/dist/lib/application-settings/application-settings.component.d.ts +4 -4
- package/dist/lib/application-settings/application-settings.component.js +10 -10
- package/dist/lib/application-settings/application-settings.component.js.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts +88 -10
- package/dist/lib/entity-permissions/entity-permissions.component.d.ts.map +1 -1
- package/dist/lib/entity-permissions/entity-permissions.component.js +219 -63
- package/dist/lib/entity-permissions/entity-permissions.component.js.map +1 -1
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.d.ts +44 -6
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.d.ts.map +1 -1
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.js +117 -43
- package/dist/lib/entity-permissions/permission-dialog/permission-dialog.component.js.map +1 -1
- package/dist/lib/notification-preferences/notification-preferences.component.d.ts +25 -3
- package/dist/lib/notification-preferences/notification-preferences.component.d.ts.map +1 -1
- package/dist/lib/notification-preferences/notification-preferences.component.js +62 -18
- package/dist/lib/notification-preferences/notification-preferences.component.js.map +1 -1
- package/dist/lib/role-management/role-dialog/role-dialog.component.d.ts +32 -4
- package/dist/lib/role-management/role-dialog/role-dialog.component.d.ts.map +1 -1
- package/dist/lib/role-management/role-dialog/role-dialog.component.js +84 -30
- package/dist/lib/role-management/role-dialog/role-dialog.component.js.map +1 -1
- package/dist/lib/role-management/role-management-agent-context.d.ts +12 -4
- package/dist/lib/role-management/role-management-agent-context.d.ts.map +1 -1
- package/dist/lib/role-management/role-management-agent-context.js +26 -10
- package/dist/lib/role-management/role-management-agent-context.js.map +1 -1
- package/dist/lib/role-management/role-management.component.d.ts +95 -11
- package/dist/lib/role-management/role-management.component.d.ts.map +1 -1
- package/dist/lib/role-management/role-management.component.js +259 -91
- package/dist/lib/role-management/role-management.component.js.map +1 -1
- package/dist/lib/settings/settings.component.d.ts +87 -11
- package/dist/lib/settings/settings.component.d.ts.map +1 -1
- package/dist/lib/settings/settings.component.js +211 -65
- package/dist/lib/settings/settings.component.js.map +1 -1
- package/dist/lib/shared/settings-card.component.d.ts +24 -4
- package/dist/lib/shared/settings-card.component.d.ts.map +1 -1
- package/dist/lib/shared/settings-card.component.js +46 -6
- package/dist/lib/shared/settings-card.component.js.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.d.ts +172 -18
- package/dist/lib/sql-logging/sql-logging.component.d.ts.map +1 -1
- package/dist/lib/sql-logging/sql-logging.component.js +320 -108
- package/dist/lib/sql-logging/sql-logging.component.js.map +1 -1
- package/dist/lib/user-app-config/user-app-config-content.component.js +3 -3
- package/dist/lib/user-app-config/user-app-config-content.component.js.map +1 -1
- package/dist/lib/user-app-config/user-app-config-reorder.d.ts +4 -0
- package/dist/lib/user-app-config/user-app-config-reorder.d.ts.map +1 -1
- package/dist/lib/user-app-config/user-app-config-reorder.js +11 -3
- package/dist/lib/user-app-config/user-app-config-reorder.js.map +1 -1
- package/dist/lib/user-management/transaction-group-refusals.d.ts +4 -2
- package/dist/lib/user-management/transaction-group-refusals.d.ts.map +1 -1
- package/dist/lib/user-management/transaction-group-refusals.js +7 -3
- package/dist/lib/user-management/transaction-group-refusals.js.map +1 -1
- package/dist/lib/user-management/user-dialog/user-dialog.component.d.ts +44 -6
- package/dist/lib/user-management/user-dialog/user-dialog.component.d.ts.map +1 -1
- package/dist/lib/user-management/user-dialog/user-dialog.component.js +121 -47
- package/dist/lib/user-management/user-dialog/user-dialog.component.js.map +1 -1
- package/dist/lib/user-management/user-management-agent-context.d.ts +10 -0
- package/dist/lib/user-management/user-management-agent-context.d.ts.map +1 -1
- package/dist/lib/user-management/user-management-agent-context.js +26 -6
- package/dist/lib/user-management/user-management-agent-context.js.map +1 -1
- package/dist/lib/user-management/user-management.component.d.ts +174 -16
- package/dist/lib/user-management/user-management.component.d.ts.map +1 -1
- package/dist/lib/user-management/user-management.component.js +480 -171
- package/dist/lib/user-management/user-management.component.js.map +1 -1
- package/dist/lib/user-profile-settings/user-profile-settings.component.d.ts +97 -15
- package/dist/lib/user-profile-settings/user-profile-settings.component.d.ts.map +1 -1
- package/dist/lib/user-profile-settings/user-profile-settings.component.js +280 -116
- package/dist/lib/user-profile-settings/user-profile-settings.component.js.map +1 -1
- package/package.json +20 -20
|
@@ -179,10 +179,34 @@ function NotificationPreferencesComponent_Conditional_2_Template(rf, ctx) { if (
|
|
|
179
179
|
*/
|
|
180
180
|
export class NotificationPreferencesComponent extends BaseAngularComponent {
|
|
181
181
|
sharedService;
|
|
182
|
-
|
|
182
|
+
Loading = true;
|
|
183
|
+
/** @deprecated Use {@link Loading}. */
|
|
184
|
+
get loading() {
|
|
185
|
+
return this.Loading;
|
|
186
|
+
}
|
|
187
|
+
/** @deprecated Use {@link Loading}. */
|
|
188
|
+
set loading(value) {
|
|
189
|
+
this.Loading = value;
|
|
190
|
+
}
|
|
183
191
|
saving = false;
|
|
184
|
-
|
|
185
|
-
|
|
192
|
+
ViewModels = [];
|
|
193
|
+
/** @deprecated Use {@link ViewModels}. */
|
|
194
|
+
get viewModels() {
|
|
195
|
+
return this.ViewModels;
|
|
196
|
+
}
|
|
197
|
+
/** @deprecated Use {@link ViewModels}. */
|
|
198
|
+
set viewModels(value) {
|
|
199
|
+
this.ViewModels = value;
|
|
200
|
+
}
|
|
201
|
+
HasChanges = false;
|
|
202
|
+
/** @deprecated Use {@link HasChanges}. */
|
|
203
|
+
get hasChanges() {
|
|
204
|
+
return this.HasChanges;
|
|
205
|
+
}
|
|
206
|
+
/** @deprecated Use {@link HasChanges}. */
|
|
207
|
+
set hasChanges(value) {
|
|
208
|
+
this.HasChanges = value;
|
|
209
|
+
}
|
|
186
210
|
constructor(sharedService) {
|
|
187
211
|
super();
|
|
188
212
|
this.sharedService = sharedService;
|
|
@@ -201,7 +225,7 @@ export class NotificationPreferencesComponent extends BaseAngularComponent {
|
|
|
201
225
|
*/
|
|
202
226
|
async loadData() {
|
|
203
227
|
try {
|
|
204
|
-
this.
|
|
228
|
+
this.Loading = true;
|
|
205
229
|
// UserInfoEngine is auto-configured via @RegisterForStartup()
|
|
206
230
|
// NotificationEngine (server-side) loads notification types into global cache
|
|
207
231
|
// UserInfoEngine provides a getter to access them from both client and server
|
|
@@ -217,7 +241,7 @@ export class NotificationPreferencesComponent extends BaseAngularComponent {
|
|
|
217
241
|
// Get preferences from UserInfoEngine (entity objects - can be mutated)
|
|
218
242
|
const prefs = UserInfoEngine.Instance.NotificationPreferences;
|
|
219
243
|
// Build view models from cached data
|
|
220
|
-
this.
|
|
244
|
+
this.ViewModels = types.map((type) => {
|
|
221
245
|
const existingPref = prefs.find((p) => UUIDsEqual(p.NotificationTypeID, type.ID));
|
|
222
246
|
// Get channel values: user preference > type default
|
|
223
247
|
const inAppEnabled = existingPref?.InAppEnabled ?? type.DefaultInApp ?? true;
|
|
@@ -235,10 +259,10 @@ export class NotificationPreferencesComponent extends BaseAngularComponent {
|
|
|
235
259
|
originalSmsEnabled: smsEnabled,
|
|
236
260
|
};
|
|
237
261
|
});
|
|
238
|
-
this.
|
|
262
|
+
this.Loading = false;
|
|
239
263
|
}
|
|
240
264
|
catch (error) {
|
|
241
|
-
this.
|
|
265
|
+
this.Loading = false;
|
|
242
266
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
243
267
|
this.sharedService.CreateSimpleNotification(`Failed to load notification preferences: ${message}`, 'error', 3000);
|
|
244
268
|
}
|
|
@@ -249,9 +273,13 @@ export class NotificationPreferencesComponent extends BaseAngularComponent {
|
|
|
249
273
|
* Sets the global hasChanges flag to show/hide the save/cancel buttons.
|
|
250
274
|
* @param vm The view model for the notification type being modified
|
|
251
275
|
*/
|
|
252
|
-
|
|
276
|
+
OnChannelChange(vm) {
|
|
253
277
|
vm.changed = vm.inAppEnabled !== vm.originalInAppEnabled || vm.emailEnabled !== vm.originalEmailEnabled || vm.smsEnabled !== vm.originalSmsEnabled;
|
|
254
|
-
this.
|
|
278
|
+
this.HasChanges = this.ViewModels.some((v) => v.changed);
|
|
279
|
+
}
|
|
280
|
+
/** @deprecated Use {@link OnChannelChange}. */
|
|
281
|
+
onChannelChange(vm) {
|
|
282
|
+
return this.OnChannelChange(vm);
|
|
255
283
|
}
|
|
256
284
|
/**
|
|
257
285
|
* Saves all changed notification preferences using a transaction group for batch updates.
|
|
@@ -268,7 +296,7 @@ export class NotificationPreferencesComponent extends BaseAngularComponent {
|
|
|
268
296
|
const transGroup = await md.CreateTransactionGroup();
|
|
269
297
|
// Queue all saves in transaction group - no need to await individual saves
|
|
270
298
|
// Transaction group queues them and submits all in one batch
|
|
271
|
-
for (const vm of this.
|
|
299
|
+
for (const vm of this.ViewModels.filter((v) => v.changed)) {
|
|
272
300
|
let pref = vm.preference;
|
|
273
301
|
if (!pref) {
|
|
274
302
|
// Create new preference record
|
|
@@ -292,13 +320,13 @@ export class NotificationPreferencesComponent extends BaseAngularComponent {
|
|
|
292
320
|
if (success) {
|
|
293
321
|
// Cache refresh happens automatically in MJUserNotificationPreferenceEntityExtended.Save()
|
|
294
322
|
// Update original values
|
|
295
|
-
this.
|
|
323
|
+
this.ViewModels.forEach((vm) => {
|
|
296
324
|
vm.originalInAppEnabled = vm.inAppEnabled;
|
|
297
325
|
vm.originalEmailEnabled = vm.emailEnabled;
|
|
298
326
|
vm.originalSmsEnabled = vm.smsEnabled;
|
|
299
327
|
vm.changed = false;
|
|
300
328
|
});
|
|
301
|
-
this.
|
|
329
|
+
this.HasChanges = false;
|
|
302
330
|
this.sharedService.CreateSimpleNotification('Notification preferences saved successfully', 'success', 2500);
|
|
303
331
|
}
|
|
304
332
|
else {
|
|
@@ -318,13 +346,13 @@ export class NotificationPreferencesComponent extends BaseAngularComponent {
|
|
|
318
346
|
*/
|
|
319
347
|
cancel() {
|
|
320
348
|
// Revert changes
|
|
321
|
-
this.
|
|
349
|
+
this.ViewModels.forEach((vm) => {
|
|
322
350
|
vm.inAppEnabled = vm.originalInAppEnabled;
|
|
323
351
|
vm.emailEnabled = vm.originalEmailEnabled;
|
|
324
352
|
vm.smsEnabled = vm.originalSmsEnabled;
|
|
325
353
|
vm.changed = false;
|
|
326
354
|
});
|
|
327
|
-
this.
|
|
355
|
+
this.HasChanges = false;
|
|
328
356
|
}
|
|
329
357
|
/**
|
|
330
358
|
* Gets the Font Awesome icon class for a notification type.
|
|
@@ -332,36 +360,52 @@ export class NotificationPreferencesComponent extends BaseAngularComponent {
|
|
|
332
360
|
* @param type The notification type entity
|
|
333
361
|
* @returns Font Awesome icon class (e.g., 'fa-bell'), defaults to 'fa-bell' if not specified
|
|
334
362
|
*/
|
|
335
|
-
|
|
363
|
+
GetTypeIcon(type) {
|
|
336
364
|
return type.Icon || 'fa-bell';
|
|
337
365
|
}
|
|
366
|
+
/** @deprecated Use {@link GetTypeIcon}. */
|
|
367
|
+
getTypeIcon(type) {
|
|
368
|
+
return this.GetTypeIcon(type);
|
|
369
|
+
}
|
|
338
370
|
/**
|
|
339
371
|
* Gets the color hex code for a notification type.
|
|
340
372
|
* Used for MD3 dynamic color styling (icon background, border accent).
|
|
341
373
|
* @param type The notification type entity
|
|
342
374
|
* @returns Hex color code (e.g., '#0076B6'), defaults to '#999' if not specified
|
|
343
375
|
*/
|
|
344
|
-
|
|
376
|
+
GetTypeColor(type) {
|
|
345
377
|
return type.Color || '#999';
|
|
346
378
|
}
|
|
379
|
+
/** @deprecated Use {@link GetTypeColor}. */
|
|
380
|
+
getTypeColor(type) {
|
|
381
|
+
return this.GetTypeColor(type);
|
|
382
|
+
}
|
|
347
383
|
/**
|
|
348
384
|
* Gets the auto-expire duration in days for a notification type.
|
|
349
385
|
* Displayed in the metadata row to inform users about automatic read marking.
|
|
350
386
|
* @param type The notification type entity
|
|
351
387
|
* @returns Number of days until auto-expire, or null if not configured
|
|
352
388
|
*/
|
|
353
|
-
|
|
389
|
+
GetTypeAutoExpireDays(type) {
|
|
354
390
|
return type.AutoExpireDays || null;
|
|
355
391
|
}
|
|
392
|
+
/** @deprecated Use {@link GetTypeAutoExpireDays}. */
|
|
393
|
+
getTypeAutoExpireDays(type) {
|
|
394
|
+
return this.GetTypeAutoExpireDays(type);
|
|
395
|
+
}
|
|
356
396
|
/**
|
|
357
397
|
* Checks if users are allowed to customize preferences for this notification type.
|
|
358
398
|
* When false, the delivery channel checkboxes are disabled and an info message is shown.
|
|
359
399
|
* @param type The notification type entity
|
|
360
400
|
* @returns True if user customization is allowed (default), false otherwise
|
|
361
401
|
*/
|
|
362
|
-
|
|
402
|
+
GetAllowUserPreference(type) {
|
|
363
403
|
return type.AllowUserPreference !== false;
|
|
364
404
|
}
|
|
405
|
+
/** @deprecated Use {@link GetAllowUserPreference}. */
|
|
406
|
+
getAllowUserPreference(type) {
|
|
407
|
+
return this.GetAllowUserPreference(type);
|
|
408
|
+
}
|
|
365
409
|
static ɵfac = function NotificationPreferencesComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || NotificationPreferencesComponent)(i0.ɵɵdirectiveInject(i1.SharedService)); };
|
|
366
410
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: NotificationPreferencesComponent, selectors: [["mj-notification-preferences"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 3, vars: 1, consts: [[1, "notification-preferences-container"], ["text", "Loading notification preferences..."], [1, "content"], ["Icon", "fa-solid fa-bell-slash", "Title", "No notification types configured yet."], [1, "notification-types-grid"], ["role", "group", "aria-label", "Save or cancel changes", 1, "actions"], ["role", "region", 1, "notification-type-card", 3, "changed"], ["role", "region", 1, "notification-type-card"], [1, "card-header"], [1, "icon-wrapper"], [1, "header-text"], [1, "type-description"], [1, "card-body"], [1, "preference-row"], [1, "delivery-label"], ["role", "group", 1, "delivery-checkboxes"], [1, "channel-checkbox"], ["type", "checkbox", 3, "ngModelChange", "change", "ngModel", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-bell"], ["aria-hidden", "true", 1, "fa-solid", "fa-envelope"], ["aria-hidden", "true", 1, "fa-solid", "fa-mobile"], ["Variant", "info", 1, "info-message"], [1, "metadata-row"], ["aria-hidden", "true", 1, "fa-solid", "fa-clock"], [1, "metadata-text"], ["type", "button", 1, "btn", "btn-secondary", 3, "click", "disabled"], ["type", "button", 1, "btn", "btn-primary", 3, "click", "disabled"], ["aria-hidden", "true", 1, "fa-solid", "fa-spinner", "fa-spin"], ["aria-hidden", "true", 1, "fa-solid", "fa-save"]], template: function NotificationPreferencesComponent_Template(rf, ctx) { if (rf & 1) {
|
|
367
411
|
i0.ɵɵelementStart(0, "div", 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification-preferences.component.js","sourceRoot":"","sources":["../../../src/lib/notification-preferences/notification-preferences.component.ts","../../../src/lib/notification-preferences/notification-preferences.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, OnInit } from '@angular/core';\nimport { Metadata } from '@memberjunction/core';\nimport { MJUserNotificationTypeEntity, MJUserNotificationPreferenceEntity, UserInfoEngine } from '@memberjunction/core-entities';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { UUIDsEqual } from '@memberjunction/global';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n/**\n * View model for managing notification preferences in the UI.\n * Combines notification type definition with user's current preferences and change tracking.\n */\ninterface NotificationPreferenceViewModel {\n /** The notification type definition (read-only) */\n type: MJUserNotificationTypeEntity;\n /** User's existing preference record, or null if not yet set */\n preference: MJUserNotificationPreferenceEntity | null;\n /** Current state: In-app notifications enabled */\n inAppEnabled: boolean;\n /** Current state: Email notifications enabled */\n emailEnabled: boolean;\n /** Current state: SMS notifications enabled */\n smsEnabled: boolean;\n /** Whether this preference has been modified by the user */\n changed: boolean;\n /** Original value for rollback: In-app enabled state */\n originalInAppEnabled: boolean;\n /** Original value for rollback: Email enabled state */\n originalEmailEnabled: boolean;\n /** Original value for rollback: SMS enabled state */\n originalSmsEnabled: boolean;\n}\n\n/**\n * Component for managing user notification preferences.\n *\n * Displays notification types with configurable delivery channels (in-app, email, SMS).\n * Users can customize which channels they want to receive notifications through,\n * unless the notification type restricts user customization.\n *\n * Uses Material Design 3 (MD3) styling principles for consistent, accessible UI.\n *\n * Data flow:\n * - Loads notification types from UserInfoEngine (global cache)\n * - Loads user preferences from UserInfoEngine (per-user cache)\n * - Saves preferences using transaction groups for batch updates\n */\n@Component({\n standalone: false,\n selector: 'mj-notification-preferences',\n templateUrl: './notification-preferences.component.html',\n styleUrls: ['./notification-preferences.component.css'],\n})\nexport class NotificationPreferencesComponent extends BaseAngularComponent implements OnInit {\n loading = true;\n saving = false;\n viewModels: NotificationPreferenceViewModel[] = [];\n hasChanges = false;\n\n constructor(private sharedService: SharedService) {\n super();}\n\n /**\n * Angular lifecycle hook - initializes the component by loading notification preferences.\n */\n async ngOnInit(): Promise<void> {\n await this.loadData();\n }\n\n /**\n * Loads notification types and user preferences from UserInfoEngine.\n * Builds view models by merging type defaults with user preferences.\n * Sorts types by Priority (ascending), then Name (alphabetical).\n * @private\n */\n private async loadData(): Promise<void> {\n try {\n this.loading = true;\n\n // UserInfoEngine is auto-configured via @RegisterForStartup()\n // NotificationEngine (server-side) loads notification types into global cache\n // UserInfoEngine provides a getter to access them from both client and server\n\n // Get notification types from UserInfoEngine, sorted client-side\n const types = [...UserInfoEngine.Instance.NotificationTypes].sort((a, b) => {\n const priorityA = a.Priority ?? 999;\n const priorityB = b.Priority ?? 999;\n if (priorityA !== priorityB) {\n return priorityA - priorityB;\n }\n return a.Name.localeCompare(b.Name);\n });\n\n // Get preferences from UserInfoEngine (entity objects - can be mutated)\n const prefs = UserInfoEngine.Instance.NotificationPreferences;\n\n // Build view models from cached data\n this.viewModels = types.map((type) => {\n const existingPref = prefs.find((p) => UUIDsEqual(p.NotificationTypeID, type.ID));\n\n // Get channel values: user preference > type default\n const inAppEnabled = existingPref?.InAppEnabled ?? type.DefaultInApp ?? true;\n const emailEnabled = existingPref?.EmailEnabled ?? type.DefaultEmail ?? false;\n const smsEnabled = existingPref?.SMSEnabled ?? type.DefaultSMS ?? false;\n\n return {\n type,\n preference: existingPref || null,\n inAppEnabled,\n emailEnabled,\n smsEnabled,\n changed: false,\n originalInAppEnabled: inAppEnabled,\n originalEmailEnabled: emailEnabled,\n originalSmsEnabled: smsEnabled,\n };\n });\n\n this.loading = false;\n } catch (error: unknown) {\n this.loading = false;\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.sharedService.CreateSimpleNotification(`Failed to load notification preferences: ${message}`, 'error', 3000);\n }\n }\n\n /**\n * Called when a user toggles any delivery channel checkbox.\n * Updates the view model's changed flag by comparing current state to original values.\n * Sets the global hasChanges flag to show/hide the save/cancel buttons.\n * @param vm The view model for the notification type being modified\n */\n onChannelChange(vm: NotificationPreferenceViewModel): void {\n vm.changed = vm.inAppEnabled !== vm.originalInAppEnabled || vm.emailEnabled !== vm.originalEmailEnabled || vm.smsEnabled !== vm.originalSmsEnabled;\n this.hasChanges = this.viewModels.some((v) => v.changed);\n }\n\n /**\n * Saves all changed notification preferences using a transaction group for batch updates.\n * Creates new preference records for types that don't have existing preferences.\n * Updates the Enabled field based on whether any channel is enabled.\n * Shows success/error notification on completion.\n * @returns Promise that resolves when save is complete\n */\n async save(): Promise<void> {\n try {\n this.saving = true;\n const md = this.ProviderToUse;\n const currentUser = md.CurrentUser;\n const transGroup = await md.CreateTransactionGroup();\n\n // Queue all saves in transaction group - no need to await individual saves\n // Transaction group queues them and submits all in one batch\n for (const vm of this.viewModels.filter((v) => v.changed)) {\n let pref = vm.preference;\n\n if (!pref) {\n // Create new preference record\n pref = await md.GetEntityObject<MJUserNotificationPreferenceEntity>('MJ: User Notification Preferences');\n pref.UserID = currentUser.ID;\n pref.NotificationTypeID = vm.type.ID;\n vm.preference = pref;\n }\n\n // Set Enabled based on whether any channel is enabled\n pref.Enabled = vm.inAppEnabled || vm.emailEnabled || vm.smsEnabled;\n\n // Set the boolean channel fields\n pref.InAppEnabled = vm.inAppEnabled;\n pref.EmailEnabled = vm.emailEnabled;\n pref.SMSEnabled = vm.smsEnabled;\n pref.TransactionGroup = transGroup;\n // Don't await - Save() with transaction group queues immediately\n pref.Save();\n }\n\n // Submit transaction group - this is where the actual network call happens\n const success = await transGroup.Submit();\n\n if (success) {\n // Cache refresh happens automatically in MJUserNotificationPreferenceEntityExtended.Save()\n\n // Update original values\n this.viewModels.forEach((vm) => {\n vm.originalInAppEnabled = vm.inAppEnabled;\n vm.originalEmailEnabled = vm.emailEnabled;\n vm.originalSmsEnabled = vm.smsEnabled;\n vm.changed = false;\n });\n\n this.hasChanges = false;\n this.sharedService.CreateSimpleNotification('Notification preferences saved successfully', 'success', 2500);\n } else {\n throw new Error('Failed to save preferences');\n }\n\n this.saving = false;\n } catch (error: unknown) {\n this.saving = false;\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.sharedService.CreateSimpleNotification(`Failed to save preferences: ${message}`, 'error', 3000);\n }\n }\n\n /**\n * Cancels all unsaved changes and reverts to original values.\n * Resets the changed flag on all view models and hides the action buttons.\n */\n cancel(): void {\n // Revert changes\n this.viewModels.forEach((vm) => {\n vm.inAppEnabled = vm.originalInAppEnabled;\n vm.emailEnabled = vm.originalEmailEnabled;\n vm.smsEnabled = vm.originalSmsEnabled;\n vm.changed = false;\n });\n this.hasChanges = false;\n }\n\n /**\n * Gets the Font Awesome icon class for a notification type.\n * Used for MD3 dynamic icon styling in the card header.\n * @param type The notification type entity\n * @returns Font Awesome icon class (e.g., 'fa-bell'), defaults to 'fa-bell' if not specified\n */\n getTypeIcon(type: MJUserNotificationTypeEntity): string {\n return type.Icon || 'fa-bell';\n }\n\n /**\n * Gets the color hex code for a notification type.\n * Used for MD3 dynamic color styling (icon background, border accent).\n * @param type The notification type entity\n * @returns Hex color code (e.g., '#0076B6'), defaults to '#999' if not specified\n */\n getTypeColor(type: MJUserNotificationTypeEntity): string {\n return type.Color || '#999';\n }\n\n /**\n * Gets the auto-expire duration in days for a notification type.\n * Displayed in the metadata row to inform users about automatic read marking.\n * @param type The notification type entity\n * @returns Number of days until auto-expire, or null if not configured\n */\n getTypeAutoExpireDays(type: MJUserNotificationTypeEntity): number | null {\n return type.AutoExpireDays || null;\n }\n\n /**\n * Checks if users are allowed to customize preferences for this notification type.\n * When false, the delivery channel checkboxes are disabled and an info message is shown.\n * @param type The notification type entity\n * @returns True if user customization is allowed (default), false otherwise\n */\n getAllowUserPreference(type: MJUserNotificationTypeEntity): boolean {\n return type.AllowUserPreference !== false;\n }\n}\n","<div class=\"notification-preferences-container\">\n @if (loading) {\n <mj-loading text=\"Loading notification preferences...\"></mj-loading>\n } @else {\n <div class=\"content\">\n @if (viewModels.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-bell-slash\"\n Title=\"No notification types configured yet.\" />\n } @else {\n <!-- Notification type cards -->\n <div class=\"notification-types-grid\">\n @for (vm of viewModels; track vm.type.ID) {\n <article class=\"notification-type-card\"\n [class.changed]=\"vm.changed\"\n role=\"region\"\n [attr.aria-label]=\"'Notification settings for ' + vm.type.Name\">\n\n <div class=\"card-header\" [style.border-left-color]=\"getTypeColor(vm.type)\">\n <div class=\"icon-wrapper\" [style.background-color]=\"getTypeColor(vm.type) + '20'\">\n <i [class]=\"'fa-solid ' + getTypeIcon(vm.type)\" [style.color]=\"getTypeColor(vm.type)\" [attr.aria-hidden]=\"true\"></i>\n </div>\n <div class=\"header-text\">\n <h4>{{ vm.type.Name }}</h4>\n <p class=\"type-description\">{{ vm.type.Description }}</p>\n </div>\n </div>\n\n <div class=\"card-body\">\n <!-- Delivery channel checkboxes -->\n <div class=\"preference-row\">\n <label class=\"delivery-label\" [attr.id]=\"'delivery-label-' + vm.type.ID\">Delivery Channels</label>\n\n <div class=\"delivery-checkboxes\" role=\"group\" [attr.aria-labelledby]=\"'delivery-label-' + vm.type.ID\">\n <label class=\"channel-checkbox\">\n <input type=\"checkbox\"\n [(ngModel)]=\"vm.inAppEnabled\"\n (change)=\"onChannelChange(vm)\"\n [disabled]=\"!getAllowUserPreference(vm.type)\"\n [attr.aria-label]=\"'Enable in-app notifications for ' + vm.type.Name\">\n <i class=\"fa-solid fa-bell\" aria-hidden=\"true\"></i>\n <span>In-App</span>\n </label>\n\n <label class=\"channel-checkbox\">\n <input type=\"checkbox\"\n [(ngModel)]=\"vm.emailEnabled\"\n (change)=\"onChannelChange(vm)\"\n [disabled]=\"!getAllowUserPreference(vm.type)\"\n [attr.aria-label]=\"'Enable email notifications for ' + vm.type.Name\">\n <i class=\"fa-solid fa-envelope\" aria-hidden=\"true\"></i>\n <span>Email</span>\n </label>\n\n <label class=\"channel-checkbox\">\n <input type=\"checkbox\"\n [(ngModel)]=\"vm.smsEnabled\"\n (change)=\"onChannelChange(vm)\"\n [disabled]=\"!getAllowUserPreference(vm.type)\"\n [attr.aria-label]=\"'Enable SMS notifications for ' + vm.type.Name\">\n <i class=\"fa-solid fa-mobile\" aria-hidden=\"true\"></i>\n <span>SMS</span>\n </label>\n </div>\n\n @if (!getAllowUserPreference(vm.type)) {\n <mj-alert Variant=\"info\" class=\"info-message\">This notification type's delivery channels cannot be customized.</mj-alert>\n }\n </div>\n\n <!-- Auto-expire info -->\n @if (getTypeAutoExpireDays(vm.type)) {\n <div class=\"metadata-row\">\n <i class=\"fa-solid fa-clock\" aria-hidden=\"true\"></i>\n <span class=\"metadata-text\">\n Auto-marks as read after {{ getTypeAutoExpireDays(vm.type) }} day(s)\n </span>\n </div>\n }\n </div>\n </article>\n }\n </div>\n }\n\n <!-- Action buttons -->\n @if (hasChanges) {\n <div class=\"actions\" role=\"group\" aria-label=\"Save or cancel changes\">\n <button class=\"btn btn-secondary\"\n type=\"button\"\n (click)=\"cancel()\"\n [disabled]=\"saving\"\n [attr.aria-busy]=\"saving\">\n Cancel\n </button>\n <button class=\"btn btn-primary\"\n type=\"button\"\n (click)=\"save()\"\n [disabled]=\"saving\"\n [attr.aria-busy]=\"saving\">\n @if (saving) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"fa-solid fa-save\" aria-hidden=\"true\"></i>\n }\n <span>{{ saving ? 'Saving...' : 'Save Preferences' }}</span>\n </button>\n </div>\n }\n </div>\n }\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"notification-preferences.component.js","sourceRoot":"","sources":["../../../src/lib/notification-preferences/notification-preferences.component.ts","../../../src/lib/notification-preferences/notification-preferences.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAU,MAAM,eAAe,CAAC;AAElD,OAAO,EAAoE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAEjI,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;;;;;;;ICJ7D,gCAAoE;;;IAI5D,oCAEoD;;;IA0D5B,oCAA8C;IAAA,gFAAgE;IAAA,iBAAW;;;IAM7H,+BAA0B;IACtB,wBAAoD;IACpD,gCAA4B;IACxB,YACJ;IACJ,AADI,iBAAO,EACL;;;;IAFE,eACJ;IADI,yGACJ;;;;IAzDR,AADJ,AALJ,kCAGyE,aAEM,aACW;IAC9E,oBAAoH;IACxH,iBAAM;IAEF,AADJ,+BAAyB,SACjB;IAAA,YAAkB;IAAA,iBAAK;IAC3B,6BAA4B;IAAA,YAAyB;IAE7D,AADI,AADyD,iBAAI,EACvD,EACJ;IAKE,AADJ,AAFJ,+BAAuB,eAES,iBACiD;IAAA,kCAAiB;IAAA,iBAAQ;IAI1F,AADJ,AADJ,gCAAsG,iBAClE,iBAKiD;IAHtE,wUAA6B;IAC7B,mPAAU,6BAAmB,KAAC;IAFrC,iBAI6E;IAC7E,yBAAmD;IACnD,6BAAM;IAAA,uBAAM;IAChB,AADgB,iBAAO,EACf;IAGJ,AADJ,kCAAgC,iBAKgD;IAHrE,wUAA6B;IAC7B,mPAAU,6BAAmB,KAAC;IAFrC,iBAI4E;IAC5E,yBAAuD;IACvD,6BAAM;IAAA,sBAAK;IACf,AADe,iBAAO,EACd;IAGJ,AADJ,kCAAgC,iBAK8C;IAHnE,oUAA2B;IAC3B,mPAAU,6BAAmB,KAAC;IAFrC,iBAI0E;IAC1E,yBAAqD;IACrD,6BAAM;IAAA,oBAAG;IAEjB,AADI,AADa,iBAAO,EACZ,EACN;IAEN,4IAAwC;IAG5C,iBAAM;IAGN,uIAAsC;IAS9C,AADI,iBAAM,EACA;;;;IAlED,wCAA4B;;IAIR,cAAiD;IAAjD,oEAAiD;IAC5C,cAAuD;IAAvD,0EAAuD;IAC1E,cAA4C;IAA5C,2DAA4C;IAAC,wDAAqC;;IAGjF,eAAkB;IAAlB,qCAAkB;IACM,eAAyB;IAAzB,4CAAyB;IAOvB,eAA0C;;IAE1B,eAAuD;;IAGtF,eAA6B;IAA7B,kDAA6B;IAE7B,qEAA6C;;IAQ7C,eAA6B;IAA7B,kDAA6B;IAE7B,qEAA6C;;IAQ7C,eAA2B;IAA3B,gDAA2B;IAE3B,qEAA6C;;IAO5D,eAEC;IAFD,sEAEC;IAIL,cAOC;IAPD,oEAOC;;;IAnEjB,8BAAqC;IACjC,qIAqEC;IACL,iBAAM;;;IAtEF,cAqEC;IArED,gCAqEC;;;IAoBO,wBAA8D;;;IAE9D,wBAAmD;;;;IAf3D,AADJ,8BAAsE,iBAKhC;IAF1B,mNAAS,eAAQ,KAAC;IAGtB,wBACJ;IAAA,iBAAS;IACT,kCAIkC;IAF1B,mNAAS,aAAM,KAAC;IAKlB,AAFF,6HAAc,uGAEL;IAGT,4BAAM;IAAA,YAA+C;IAE7D,AADI,AADyD,iBAAO,EACvD,EACP;;;IAhBM,cAAmB;IAAnB,wCAAmB;;IAOnB,eAAmB;IAAnB,wCAAmB;;IAEvB,cAIC;IAJD,uCAIC;IACK,eAA+C;IAA/C,sEAA+C;;;IArGrE,8BAAqB;IAKf,AAJF,2HAA+B,0FAItB;IA6ET,gHAAkB;IAuBtB,iBAAM;;;IAxGF,cA8EC;IA9ED,wDA8EC;IAGD,eAsBC;IAtBD,4CAsBC;;AD5Eb;;;;;;;;;;;;;GAaG;AAOH,MAAM,OAAO,gCAAiC,SAAQ,oBAAoB;IAiCpD;IAhCpB,OAAO,GAAG,IAAI,CAAC;IAEf,uCAAuC;IACvC,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IACD,uCAAuC;IACvC,IAAI,OAAO,CAAC,KAAK;QACf,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IACD,MAAM,GAAG,KAAK,CAAC;IACf,UAAU,GAAsC,EAAE,CAAC;IAEnD,0CAA0C;IAC1C,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IACD,0CAA0C;IAC1C,IAAI,UAAU,CAAC,KAAwC;QACrD,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IACD,UAAU,GAAG,KAAK,CAAC;IAEnB,0CAA0C;IAC1C,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IACD,0CAA0C;IAC1C,IAAI,UAAU,CAAC,KAAK;QAClB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED,YAAoB,aAA4B;QAC9C,KAAK,EAAE,CAAC;QADU,kBAAa,GAAb,aAAa,CAAe;IACtC,CAAC;IAEX;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,QAAQ;QACpB,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YAEpB,8DAA8D;YAC9D,8EAA8E;YAC9E,8EAA8E;YAE9E,iEAAiE;YACjE,MAAM,KAAK,GAAG,CAAC,GAAG,cAAc,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBACzE,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC;gBACpC,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC;gBACpC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;oBAC5B,OAAO,SAAS,GAAG,SAAS,CAAC;gBAC/B,CAAC;gBACD,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACtC,CAAC,CAAC,CAAC;YAEH,wEAAwE;YACxE,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YAE9D,qCAAqC;YACrC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;gBACnC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAElF,qDAAqD;gBACrD,MAAM,YAAY,GAAG,YAAY,EAAE,YAAY,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC;gBAC7E,MAAM,YAAY,GAAG,YAAY,EAAE,YAAY,IAAI,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC;gBAC9E,MAAM,UAAU,GAAG,YAAY,EAAE,UAAU,IAAI,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC;gBAExE,OAAO;oBACL,IAAI;oBACJ,UAAU,EAAE,YAAY,IAAI,IAAI;oBAChC,YAAY;oBACZ,YAAY;oBACZ,UAAU;oBACV,OAAO,EAAE,KAAK;oBACd,oBAAoB,EAAE,YAAY;oBAClC,oBAAoB,EAAE,YAAY;oBAClC,kBAAkB,EAAE,UAAU;iBAC/B,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YACrB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YACzE,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,4CAA4C,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACpH,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,eAAe,CAAC,EAAmC;QACjD,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,YAAY,KAAK,EAAE,CAAC,oBAAoB,IAAI,EAAE,CAAC,YAAY,KAAK,EAAE,CAAC,oBAAoB,IAAI,EAAE,CAAC,UAAU,KAAK,EAAE,CAAC,kBAAkB,CAAC;QACnJ,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC3D,CAAC;IAED,+CAA+C;IAC/C,eAAe,CAAC,EAAmC;QACjD,OAAO,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;YAC9B,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC;YACnC,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,sBAAsB,EAAE,CAAC;YAErD,2EAA2E;YAC3E,6DAA6D;YAC7D,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1D,IAAI,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC;gBAEzB,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,+BAA+B;oBAC/B,IAAI,GAAG,MAAM,EAAE,CAAC,eAAe,CAAqC,mCAAmC,CAAC,CAAC;oBACzG,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC;oBAC7B,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrC,EAAE,CAAC,UAAU,GAAG,IAAI,CAAC;gBACvB,CAAC;gBAED,sDAAsD;gBACtD,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,YAAY,IAAI,EAAE,CAAC,YAAY,IAAI,EAAE,CAAC,UAAU,CAAC;gBAEnE,iCAAiC;gBACjC,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;gBACpC,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;gBACpC,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC,UAAU,CAAC;gBAChC,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC;gBACnC,iEAAiE;gBACjE,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,CAAC;YAED,2EAA2E;YAC3E,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC;YAE1C,IAAI,OAAO,EAAE,CAAC;gBACZ,2FAA2F;gBAE3F,yBAAyB;gBACzB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;oBAC7B,EAAE,CAAC,oBAAoB,GAAG,EAAE,CAAC,YAAY,CAAC;oBAC1C,EAAE,CAAC,oBAAoB,GAAG,EAAE,CAAC,YAAY,CAAC;oBAC1C,EAAE,CAAC,kBAAkB,GAAG,EAAE,CAAC,UAAU,CAAC;oBACtC,EAAE,CAAC,OAAO,GAAG,KAAK,CAAC;gBACrB,CAAC,CAAC,CAAC;gBAEH,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;gBACxB,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,6CAA6C,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YAC9G,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;YAChD,CAAC;YAED,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACtB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACpB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YACzE,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,+BAA+B,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACvG,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,MAAM;QACJ,iBAAiB;QACjB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;YAC7B,EAAE,CAAC,YAAY,GAAG,EAAE,CAAC,oBAAoB,CAAC;YAC1C,EAAE,CAAC,YAAY,GAAG,EAAE,CAAC,oBAAoB,CAAC;YAC1C,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,kBAAkB,CAAC;YACtC,EAAE,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED;;;;;OAKG;IACH,WAAW,CAAC,IAAkC;QAC5C,OAAO,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC;IAChC,CAAC;IAED,2CAA2C;IAC3C,WAAW,CAAC,IAAkC;QAC5C,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,IAAkC;QAC7C,OAAO,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC;IAC9B,CAAC;IAED,4CAA4C;IAC5C,YAAY,CAAC,IAAkC;QAC7C,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED;;;;;OAKG;IACH,qBAAqB,CAAC,IAAkC;QACtD,OAAO,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC;IACrC,CAAC;IAED,qDAAqD;IACrD,qBAAqB,CAAC,IAAkC;QACtD,OAAO,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACH,sBAAsB,CAAC,IAAkC;QACvD,OAAO,IAAI,CAAC,mBAAmB,KAAK,KAAK,CAAC;IAC5C,CAAC;IAED,sDAAsD;IACtD,sBAAsB,CAAC,IAAkC;QACvD,OAAO,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;0HAhQU,gCAAgC;6DAAhC,gCAAgC;YCpD7C,8BAAgD;YAG1C,AAFF,yGAAe,4EAEN;YA4Gb,iBAAM;;YA9GF,cA6GC;YA7GD,qCA6GC;;;iFD1DQ,gCAAgC;cAN5C,SAAS;6BACI,KAAK,YACP,6BAA6B;;kFAI5B,gCAAgC","sourcesContent":["import { Component, OnInit } from '@angular/core';\nimport { Metadata } from '@memberjunction/core';\nimport { MJUserNotificationTypeEntity, MJUserNotificationPreferenceEntity, UserInfoEngine } from '@memberjunction/core-entities';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { UUIDsEqual } from '@memberjunction/global';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n/**\n * View model for managing notification preferences in the UI.\n * Combines notification type definition with user's current preferences and change tracking.\n */\ninterface NotificationPreferenceViewModel {\n /** The notification type definition (read-only) */\n type: MJUserNotificationTypeEntity;\n /** User's existing preference record, or null if not yet set */\n preference: MJUserNotificationPreferenceEntity | null;\n /** Current state: In-app notifications enabled */\n inAppEnabled: boolean;\n /** Current state: Email notifications enabled */\n emailEnabled: boolean;\n /** Current state: SMS notifications enabled */\n smsEnabled: boolean;\n /** Whether this preference has been modified by the user */\n changed: boolean;\n /** Original value for rollback: In-app enabled state */\n originalInAppEnabled: boolean;\n /** Original value for rollback: Email enabled state */\n originalEmailEnabled: boolean;\n /** Original value for rollback: SMS enabled state */\n originalSmsEnabled: boolean;\n}\n\n/**\n * Component for managing user notification preferences.\n *\n * Displays notification types with configurable delivery channels (in-app, email, SMS).\n * Users can customize which channels they want to receive notifications through,\n * unless the notification type restricts user customization.\n *\n * Uses Material Design 3 (MD3) styling principles for consistent, accessible UI.\n *\n * Data flow:\n * - Loads notification types from UserInfoEngine (global cache)\n * - Loads user preferences from UserInfoEngine (per-user cache)\n * - Saves preferences using transaction groups for batch updates\n */\n@Component({\n standalone: false,\n selector: 'mj-notification-preferences',\n templateUrl: './notification-preferences.component.html',\n styleUrls: ['./notification-preferences.component.css'],\n})\nexport class NotificationPreferencesComponent extends BaseAngularComponent implements OnInit {\n Loading = true;\n\n /** @deprecated Use {@link Loading}. */\n get loading() {\n return this.Loading;\n }\n /** @deprecated Use {@link Loading}. */\n set loading(value) {\n this.Loading = value;\n }\n saving = false;\n ViewModels: NotificationPreferenceViewModel[] = [];\n\n /** @deprecated Use {@link ViewModels}. */\n get viewModels(): NotificationPreferenceViewModel[] {\n return this.ViewModels;\n }\n /** @deprecated Use {@link ViewModels}. */\n set viewModels(value: NotificationPreferenceViewModel[]) {\n this.ViewModels = value;\n }\n HasChanges = false;\n\n /** @deprecated Use {@link HasChanges}. */\n get hasChanges() {\n return this.HasChanges;\n }\n /** @deprecated Use {@link HasChanges}. */\n set hasChanges(value) {\n this.HasChanges = value;\n }\n\n constructor(private sharedService: SharedService) {\n super();}\n\n /**\n * Angular lifecycle hook - initializes the component by loading notification preferences.\n */\n async ngOnInit(): Promise<void> {\n await this.loadData();\n }\n\n /**\n * Loads notification types and user preferences from UserInfoEngine.\n * Builds view models by merging type defaults with user preferences.\n * Sorts types by Priority (ascending), then Name (alphabetical).\n * @private\n */\n private async loadData(): Promise<void> {\n try {\n this.Loading = true;\n\n // UserInfoEngine is auto-configured via @RegisterForStartup()\n // NotificationEngine (server-side) loads notification types into global cache\n // UserInfoEngine provides a getter to access them from both client and server\n\n // Get notification types from UserInfoEngine, sorted client-side\n const types = [...UserInfoEngine.Instance.NotificationTypes].sort((a, b) => {\n const priorityA = a.Priority ?? 999;\n const priorityB = b.Priority ?? 999;\n if (priorityA !== priorityB) {\n return priorityA - priorityB;\n }\n return a.Name.localeCompare(b.Name);\n });\n\n // Get preferences from UserInfoEngine (entity objects - can be mutated)\n const prefs = UserInfoEngine.Instance.NotificationPreferences;\n\n // Build view models from cached data\n this.ViewModels = types.map((type) => {\n const existingPref = prefs.find((p) => UUIDsEqual(p.NotificationTypeID, type.ID));\n\n // Get channel values: user preference > type default\n const inAppEnabled = existingPref?.InAppEnabled ?? type.DefaultInApp ?? true;\n const emailEnabled = existingPref?.EmailEnabled ?? type.DefaultEmail ?? false;\n const smsEnabled = existingPref?.SMSEnabled ?? type.DefaultSMS ?? false;\n\n return {\n type,\n preference: existingPref || null,\n inAppEnabled,\n emailEnabled,\n smsEnabled,\n changed: false,\n originalInAppEnabled: inAppEnabled,\n originalEmailEnabled: emailEnabled,\n originalSmsEnabled: smsEnabled,\n };\n });\n\n this.Loading = false;\n } catch (error: unknown) {\n this.Loading = false;\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.sharedService.CreateSimpleNotification(`Failed to load notification preferences: ${message}`, 'error', 3000);\n }\n }\n\n /**\n * Called when a user toggles any delivery channel checkbox.\n * Updates the view model's changed flag by comparing current state to original values.\n * Sets the global hasChanges flag to show/hide the save/cancel buttons.\n * @param vm The view model for the notification type being modified\n */\n OnChannelChange(vm: NotificationPreferenceViewModel): void {\n vm.changed = vm.inAppEnabled !== vm.originalInAppEnabled || vm.emailEnabled !== vm.originalEmailEnabled || vm.smsEnabled !== vm.originalSmsEnabled;\n this.HasChanges = this.ViewModels.some((v) => v.changed);\n }\n\n /** @deprecated Use {@link OnChannelChange}. */\n onChannelChange(vm: NotificationPreferenceViewModel): void {\n return this.OnChannelChange(vm);\n }\n\n /**\n * Saves all changed notification preferences using a transaction group for batch updates.\n * Creates new preference records for types that don't have existing preferences.\n * Updates the Enabled field based on whether any channel is enabled.\n * Shows success/error notification on completion.\n * @returns Promise that resolves when save is complete\n */\n async save(): Promise<void> {\n try {\n this.saving = true;\n const md = this.ProviderToUse;\n const currentUser = md.CurrentUser;\n const transGroup = await md.CreateTransactionGroup();\n\n // Queue all saves in transaction group - no need to await individual saves\n // Transaction group queues them and submits all in one batch\n for (const vm of this.ViewModels.filter((v) => v.changed)) {\n let pref = vm.preference;\n\n if (!pref) {\n // Create new preference record\n pref = await md.GetEntityObject<MJUserNotificationPreferenceEntity>('MJ: User Notification Preferences');\n pref.UserID = currentUser.ID;\n pref.NotificationTypeID = vm.type.ID;\n vm.preference = pref;\n }\n\n // Set Enabled based on whether any channel is enabled\n pref.Enabled = vm.inAppEnabled || vm.emailEnabled || vm.smsEnabled;\n\n // Set the boolean channel fields\n pref.InAppEnabled = vm.inAppEnabled;\n pref.EmailEnabled = vm.emailEnabled;\n pref.SMSEnabled = vm.smsEnabled;\n pref.TransactionGroup = transGroup;\n // Don't await - Save() with transaction group queues immediately\n pref.Save();\n }\n\n // Submit transaction group - this is where the actual network call happens\n const success = await transGroup.Submit();\n\n if (success) {\n // Cache refresh happens automatically in MJUserNotificationPreferenceEntityExtended.Save()\n\n // Update original values\n this.ViewModels.forEach((vm) => {\n vm.originalInAppEnabled = vm.inAppEnabled;\n vm.originalEmailEnabled = vm.emailEnabled;\n vm.originalSmsEnabled = vm.smsEnabled;\n vm.changed = false;\n });\n\n this.HasChanges = false;\n this.sharedService.CreateSimpleNotification('Notification preferences saved successfully', 'success', 2500);\n } else {\n throw new Error('Failed to save preferences');\n }\n\n this.saving = false;\n } catch (error: unknown) {\n this.saving = false;\n const message = error instanceof Error ? error.message : 'Unknown error';\n this.sharedService.CreateSimpleNotification(`Failed to save preferences: ${message}`, 'error', 3000);\n }\n }\n\n /**\n * Cancels all unsaved changes and reverts to original values.\n * Resets the changed flag on all view models and hides the action buttons.\n */\n cancel(): void {\n // Revert changes\n this.ViewModels.forEach((vm) => {\n vm.inAppEnabled = vm.originalInAppEnabled;\n vm.emailEnabled = vm.originalEmailEnabled;\n vm.smsEnabled = vm.originalSmsEnabled;\n vm.changed = false;\n });\n this.HasChanges = false;\n }\n\n /**\n * Gets the Font Awesome icon class for a notification type.\n * Used for MD3 dynamic icon styling in the card header.\n * @param type The notification type entity\n * @returns Font Awesome icon class (e.g., 'fa-bell'), defaults to 'fa-bell' if not specified\n */\n GetTypeIcon(type: MJUserNotificationTypeEntity): string {\n return type.Icon || 'fa-bell';\n }\n\n /** @deprecated Use {@link GetTypeIcon}. */\n getTypeIcon(type: MJUserNotificationTypeEntity): string {\n return this.GetTypeIcon(type);\n }\n\n /**\n * Gets the color hex code for a notification type.\n * Used for MD3 dynamic color styling (icon background, border accent).\n * @param type The notification type entity\n * @returns Hex color code (e.g., '#0076B6'), defaults to '#999' if not specified\n */\n GetTypeColor(type: MJUserNotificationTypeEntity): string {\n return type.Color || '#999';\n }\n\n /** @deprecated Use {@link GetTypeColor}. */\n getTypeColor(type: MJUserNotificationTypeEntity): string {\n return this.GetTypeColor(type);\n }\n\n /**\n * Gets the auto-expire duration in days for a notification type.\n * Displayed in the metadata row to inform users about automatic read marking.\n * @param type The notification type entity\n * @returns Number of days until auto-expire, or null if not configured\n */\n GetTypeAutoExpireDays(type: MJUserNotificationTypeEntity): number | null {\n return type.AutoExpireDays || null;\n }\n\n /** @deprecated Use {@link GetTypeAutoExpireDays}. */\n getTypeAutoExpireDays(type: MJUserNotificationTypeEntity): number | null {\n return this.GetTypeAutoExpireDays(type);\n }\n\n /**\n * Checks if users are allowed to customize preferences for this notification type.\n * When false, the delivery channel checkboxes are disabled and an info message is shown.\n * @param type The notification type entity\n * @returns True if user customization is allowed (default), false otherwise\n */\n GetAllowUserPreference(type: MJUserNotificationTypeEntity): boolean {\n return type.AllowUserPreference !== false;\n }\n\n /** @deprecated Use {@link GetAllowUserPreference}. */\n getAllowUserPreference(type: MJUserNotificationTypeEntity): boolean {\n return this.GetAllowUserPreference(type);\n }\n}\n","<div class=\"notification-preferences-container\">\n @if (loading) {\n <mj-loading text=\"Loading notification preferences...\"></mj-loading>\n } @else {\n <div class=\"content\">\n @if (viewModels.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-bell-slash\"\n Title=\"No notification types configured yet.\" />\n } @else {\n <!-- Notification type cards -->\n <div class=\"notification-types-grid\">\n @for (vm of viewModels; track vm.type.ID) {\n <article class=\"notification-type-card\"\n [class.changed]=\"vm.changed\"\n role=\"region\"\n [attr.aria-label]=\"'Notification settings for ' + vm.type.Name\">\n\n <div class=\"card-header\" [style.border-left-color]=\"getTypeColor(vm.type)\">\n <div class=\"icon-wrapper\" [style.background-color]=\"getTypeColor(vm.type) + '20'\">\n <i [class]=\"'fa-solid ' + getTypeIcon(vm.type)\" [style.color]=\"getTypeColor(vm.type)\" [attr.aria-hidden]=\"true\"></i>\n </div>\n <div class=\"header-text\">\n <h4>{{ vm.type.Name }}</h4>\n <p class=\"type-description\">{{ vm.type.Description }}</p>\n </div>\n </div>\n\n <div class=\"card-body\">\n <!-- Delivery channel checkboxes -->\n <div class=\"preference-row\">\n <label class=\"delivery-label\" [attr.id]=\"'delivery-label-' + vm.type.ID\">Delivery Channels</label>\n\n <div class=\"delivery-checkboxes\" role=\"group\" [attr.aria-labelledby]=\"'delivery-label-' + vm.type.ID\">\n <label class=\"channel-checkbox\">\n <input type=\"checkbox\"\n [(ngModel)]=\"vm.inAppEnabled\"\n (change)=\"onChannelChange(vm)\"\n [disabled]=\"!getAllowUserPreference(vm.type)\"\n [attr.aria-label]=\"'Enable in-app notifications for ' + vm.type.Name\">\n <i class=\"fa-solid fa-bell\" aria-hidden=\"true\"></i>\n <span>In-App</span>\n </label>\n\n <label class=\"channel-checkbox\">\n <input type=\"checkbox\"\n [(ngModel)]=\"vm.emailEnabled\"\n (change)=\"onChannelChange(vm)\"\n [disabled]=\"!getAllowUserPreference(vm.type)\"\n [attr.aria-label]=\"'Enable email notifications for ' + vm.type.Name\">\n <i class=\"fa-solid fa-envelope\" aria-hidden=\"true\"></i>\n <span>Email</span>\n </label>\n\n <label class=\"channel-checkbox\">\n <input type=\"checkbox\"\n [(ngModel)]=\"vm.smsEnabled\"\n (change)=\"onChannelChange(vm)\"\n [disabled]=\"!getAllowUserPreference(vm.type)\"\n [attr.aria-label]=\"'Enable SMS notifications for ' + vm.type.Name\">\n <i class=\"fa-solid fa-mobile\" aria-hidden=\"true\"></i>\n <span>SMS</span>\n </label>\n </div>\n\n @if (!getAllowUserPreference(vm.type)) {\n <mj-alert Variant=\"info\" class=\"info-message\">This notification type's delivery channels cannot be customized.</mj-alert>\n }\n </div>\n\n <!-- Auto-expire info -->\n @if (getTypeAutoExpireDays(vm.type)) {\n <div class=\"metadata-row\">\n <i class=\"fa-solid fa-clock\" aria-hidden=\"true\"></i>\n <span class=\"metadata-text\">\n Auto-marks as read after {{ getTypeAutoExpireDays(vm.type) }} day(s)\n </span>\n </div>\n }\n </div>\n </article>\n }\n </div>\n }\n\n <!-- Action buttons -->\n @if (hasChanges) {\n <div class=\"actions\" role=\"group\" aria-label=\"Save or cancel changes\">\n <button class=\"btn btn-secondary\"\n type=\"button\"\n (click)=\"cancel()\"\n [disabled]=\"saving\"\n [attr.aria-busy]=\"saving\">\n Cancel\n </button>\n <button class=\"btn btn-primary\"\n type=\"button\"\n (click)=\"save()\"\n [disabled]=\"saving\"\n [attr.aria-busy]=\"saving\">\n @if (saving) {\n <i class=\"fa-solid fa-spinner fa-spin\" aria-hidden=\"true\"></i>\n } @else {\n <i class=\"fa-solid fa-save\" aria-hidden=\"true\"></i>\n }\n <span>{{ saving ? 'Saving...' : 'Save Preferences' }}</span>\n </button>\n </div>\n }\n </div>\n }\n</div>\n"]}
|
|
@@ -12,14 +12,34 @@ export interface RoleDialogResult {
|
|
|
12
12
|
role?: MJRoleEntity;
|
|
13
13
|
}
|
|
14
14
|
export declare class RoleDialogComponent extends BaseAngularComponent implements OnInit, OnDestroy, OnChanges {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
Data: RoleDialogData | null;
|
|
16
|
+
/** @deprecated Use {@link Data}. */
|
|
17
|
+
set data(value: RoleDialogData | null);
|
|
18
|
+
/** @deprecated Use {@link Data}. */
|
|
19
|
+
get data(): RoleDialogData | null;
|
|
20
|
+
Visible: boolean;
|
|
21
|
+
/** @deprecated Use {@link Visible}. */
|
|
22
|
+
set visible(value: RoleDialogComponent['Visible']);
|
|
23
|
+
/** @deprecated Use {@link Visible}. */
|
|
24
|
+
get visible(): RoleDialogComponent['Visible'];
|
|
25
|
+
Result: EventEmitter<RoleDialogResult>;
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated Use {@link Result}.
|
|
28
|
+
*
|
|
29
|
+
* The same emitter under the old binding name, so a template still binding
|
|
30
|
+
* (result) keeps working. Must stay AFTER Result: class fields
|
|
31
|
+
* initialise in order, and the other way round this captures undefined.
|
|
32
|
+
*/
|
|
17
33
|
result: EventEmitter<RoleDialogResult>;
|
|
18
34
|
private fb;
|
|
19
35
|
private cdr;
|
|
20
36
|
private ngZone;
|
|
21
37
|
private get metadata();
|
|
22
|
-
|
|
38
|
+
RoleForm: FormGroup;
|
|
39
|
+
/** @deprecated Use {@link RoleForm}. */
|
|
40
|
+
get roleForm(): FormGroup;
|
|
41
|
+
/** @deprecated Use {@link RoleForm}. */
|
|
42
|
+
set roleForm(value: FormGroup);
|
|
23
43
|
isLoading: boolean;
|
|
24
44
|
error: string | null;
|
|
25
45
|
constructor();
|
|
@@ -28,14 +48,22 @@ export declare class RoleDialogComponent extends BaseAngularComponent implements
|
|
|
28
48
|
ngOnDestroy(): void;
|
|
29
49
|
private resetForm;
|
|
30
50
|
onEscapeKey(event: Event): void;
|
|
51
|
+
get WindowTitle(): string;
|
|
52
|
+
/** @deprecated Use {@link WindowTitle}. */
|
|
31
53
|
get windowTitle(): string;
|
|
54
|
+
get IsEditMode(): boolean;
|
|
55
|
+
/** @deprecated Use {@link IsEditMode}. */
|
|
32
56
|
get isEditMode(): boolean;
|
|
57
|
+
get IsSystemRole(): boolean;
|
|
58
|
+
/** @deprecated Use {@link IsSystemRole}. */
|
|
33
59
|
get isSystemRole(): boolean;
|
|
34
60
|
private loadRoleData;
|
|
61
|
+
OnSubmit(): Promise<void>;
|
|
62
|
+
/** @deprecated Use {@link OnSubmit}. */
|
|
35
63
|
onSubmit(): Promise<void>;
|
|
36
64
|
onCancel(): void;
|
|
37
65
|
private markFormGroupTouched;
|
|
38
66
|
static ɵfac: i0.ɵɵFactoryDeclaration<RoleDialogComponent, never>;
|
|
39
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<RoleDialogComponent, "mj-role-dialog", never, { "data": { "alias": "data"; "required": false; }; "visible": { "alias": "visible"; "required": false; }; }, { "result": "result"; }, never, never, false, never>;
|
|
67
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<RoleDialogComponent, "mj-role-dialog", never, { "Data": { "alias": "Data"; "required": false; }; "data": { "alias": "data"; "required": false; }; "Visible": { "alias": "Visible"; "required": false; }; "visible": { "alias": "visible"; "required": false; }; }, { "Result": "Result"; "result": "result"; }, never, never, false, never>;
|
|
40
68
|
}
|
|
41
69
|
//# sourceMappingURL=role-dialog.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role-dialog.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/role-management/role-dialog/role-dialog.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAsE,MAAM,eAAe,CAAC;AAExL,OAAO,EAAiD,SAAS,EAAc,MAAM,gBAAgB,CAAC;AAEtG,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;AACrE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,IAAI,CAAC,EAAE,YAAY,CAAC;CACrB;AAED,qBAOa,mBAAoB,SAAQ,oBAAqB,YAAW,MAAM,EAAE,SAAS,EAAE,SAAS;IAC1F,IAAI,EAAE,cAAc,GAAG,IAAI,CAAQ;
|
|
1
|
+
{"version":3,"file":"role-dialog.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/role-management/role-dialog/role-dialog.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAsE,MAAM,eAAe,CAAC;AAExL,OAAO,EAAiD,SAAS,EAAc,MAAM,gBAAgB,CAAC;AAEtG,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;AACrE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,IAAI,CAAC,EAAE,YAAY,CAAC;CACrB;AAED,qBAOa,mBAAoB,SAAQ,oBAAqB,YAAW,MAAM,EAAE,SAAS,EAAE,SAAS;IAC1F,IAAI,EAAE,cAAc,GAAG,IAAI,CAAQ;IAE5C,oCAAoC;IACpC,IAAa,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,EAE7C;IACD,oCAAoC;IACpC,IAAI,IAAI,IAAI,cAAc,GAAG,IAAI,CAEhC;IACQ,OAAO,UAAS;IAEzB,uCAAuC;IACvC,IAAa,OAAO,CAAC,KAAK,EAAE,mBAAmB,CAAC,SAAS,CAAC,EAEzD;IACD,uCAAuC;IACvC,IAAI,OAAO,IAAI,mBAAmB,CAAC,SAAS,CAAC,CAE5C;IACS,MAAM,iCAAwC;IAExD;;;;;;OAMG;IACO,MAAM,iCAAe;IAE/B,OAAO,CAAC,EAAE,CAAuB;IACjC,OAAO,CAAC,GAAG,CAA6B;IACxC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,KAAK,QAAQ,GAAiC;IAC9C,QAAQ,EAAE,SAAS,CAAC;IAE3B,wCAAwC;IACxC,IAAW,QAAQ,IAAI,SAAS,CAE/B;IACD,wCAAwC;IACxC,IAAW,QAAQ,CAAC,KAAK,EAAE,SAAS,EAEnC;IACM,SAAS,UAAS;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAQ;;IAWnC,QAAQ,IAAI,IAAI;IAIhB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAWzC,WAAW,IAAI,IAAI;IAInB,OAAO,CAAC,SAAS;IAUjB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAM/B,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED,2CAA2C;IAC3C,IAAW,WAAW,IAAI,MAAM,CAE/B;IAED,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED,0CAA0C;IAC1C,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED,IAAW,YAAY,IAAI,OAAO,CAIjC;IAED,4CAA4C;IAC5C,IAAW,YAAY,IAAI,OAAO,CAEjC;IAED,OAAO,CAAC,YAAY;IAgBP,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAsDtC,wCAAwC;IAC3B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,QAAQ,IAAI,IAAI;IAIvB,OAAO,CAAC,oBAAoB;yCAzMjB,mBAAmB;2CAAnB,mBAAmB;CA+M/B"}
|