@memberjunction/ng-explorer-core 5.44.0 → 5.45.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.
@@ -1,88 +1,102 @@
1
- import { Component, ViewChild } from '@angular/core';
1
+ import { Component, ChangeDetectorRef, inject } from '@angular/core';
2
2
  import { SharedService } from '@memberjunction/ng-shared';
3
3
  import { MJUserNotificationEntity, UserInfoEngine } from '@memberjunction/core-entities';
4
- import { TransactionVariable, CompositeKey } from '@memberjunction/core';
4
+ import { CompositeKey } from '@memberjunction/core';
5
5
  import { SafeJSONParse, UUIDsEqual } from '@memberjunction/global';
6
6
  import { MJNotificationService } from '@memberjunction/ng-notifications';
7
7
  import { BaseAngularComponent } from '@memberjunction/ng-base-types';
8
8
  import * as i0 from "@angular/core";
9
9
  import * as i1 from "@memberjunction/ng-shared";
10
10
  import * as i2 from "@memberjunction/ng-base-application";
11
- import * as i3 from "@angular/forms";
12
- import * as i4 from "@memberjunction/ng-ui-components";
13
- import * as i5 from "@angular/common";
14
- const _c0 = ["allRadio"];
15
- const _c1 = ["unreadRadio"];
16
- const _c2 = ["readRadio"];
11
+ import * as i3 from "@memberjunction/ng-markdown";
12
+ import * as i4 from "@angular/forms";
13
+ import * as i5 from "@memberjunction/ng-ui-components";
14
+ import * as i6 from "@angular/common";
17
15
  const _forTrack0 = ($index, $item) => $item.ID;
18
- function UserNotificationsComponent_Conditional_8_Conditional_1_Template(rf, ctx) { if (rf & 1) {
19
- const _r1 = i0.ɵɵgetCurrentView();
20
- i0.ɵɵelementStart(0, "button", 17);
21
- i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_8_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.markAllAsRead()); });
22
- i0.ɵɵelement(1, "i", 18);
23
- i0.ɵɵelementStart(2, "span", 19);
16
+ const _forTrack1 = ($index, $item) => $item.Key;
17
+ const _forTrack2 = ($index, $item) => $item.N.ID;
18
+ function UserNotificationsComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
19
+ i0.ɵɵelementStart(0, "span", 7);
20
+ i0.ɵɵtext(1);
21
+ i0.ɵɵelementEnd();
22
+ } if (rf & 2) {
23
+ const ctx_r0 = i0.ɵɵnextContext();
24
+ i0.ɵɵadvance();
25
+ i0.ɵɵtextInterpolate1("", ctx_r0.UnreadNotifications.length, " unread");
26
+ } }
27
+ function UserNotificationsComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
28
+ i0.ɵɵelementStart(0, "span", 7);
29
+ i0.ɵɵtext(1, "All caught up");
30
+ i0.ɵɵelementEnd();
31
+ } }
32
+ function UserNotificationsComponent_Conditional_11_Conditional_1_Template(rf, ctx) { if (rf & 1) {
33
+ const _r2 = i0.ɵɵgetCurrentView();
34
+ i0.ɵɵelementStart(0, "button", 16);
35
+ i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_11_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.markAllAsRead()); });
36
+ i0.ɵɵelement(1, "i", 17);
37
+ i0.ɵɵelementStart(2, "span", 18);
24
38
  i0.ɵɵtext(3, "Mark All Read");
25
39
  i0.ɵɵelementEnd()();
26
40
  } }
27
- function UserNotificationsComponent_Conditional_8_Conditional_2_Template(rf, ctx) { if (rf & 1) {
41
+ function UserNotificationsComponent_Conditional_11_Conditional_2_Template(rf, ctx) { if (rf & 1) {
28
42
  const _r3 = i0.ɵɵgetCurrentView();
29
- i0.ɵɵelementStart(0, "button", 20);
30
- i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_8_Conditional_2_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.markAllAsUnread()); });
31
- i0.ɵɵelement(1, "i", 21);
32
- i0.ɵɵelementStart(2, "span", 19);
43
+ i0.ɵɵelementStart(0, "button", 19);
44
+ i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_11_Conditional_2_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.markAllAsUnread()); });
45
+ i0.ɵɵelement(1, "i", 20);
46
+ i0.ɵɵelementStart(2, "span", 18);
33
47
  i0.ɵɵtext(3, "Mark All Unread");
34
48
  i0.ɵɵelementEnd()();
35
49
  } }
36
- function UserNotificationsComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
37
- i0.ɵɵelementStart(0, "div", 9);
38
- i0.ɵɵconditionalCreate(1, UserNotificationsComponent_Conditional_8_Conditional_1_Template, 4, 0, "button", 15);
39
- i0.ɵɵconditionalCreate(2, UserNotificationsComponent_Conditional_8_Conditional_2_Template, 4, 0, "button", 16);
50
+ function UserNotificationsComponent_Conditional_11_Template(rf, ctx) { if (rf & 1) {
51
+ i0.ɵɵelementStart(0, "div", 8);
52
+ i0.ɵɵconditionalCreate(1, UserNotificationsComponent_Conditional_11_Conditional_1_Template, 4, 0, "button", 14);
53
+ i0.ɵɵconditionalCreate(2, UserNotificationsComponent_Conditional_11_Conditional_2_Template, 4, 0, "button", 15);
40
54
  i0.ɵɵelementEnd();
41
55
  } if (rf & 2) {
42
- const ctx_r1 = i0.ɵɵnextContext();
56
+ const ctx_r0 = i0.ɵɵnextContext();
43
57
  i0.ɵɵadvance();
44
- i0.ɵɵconditional(ctx_r1.UnreadNotifications.length > 0 ? 1 : -1);
58
+ i0.ɵɵconditional(ctx_r0.UnreadNotifications.length > 0 ? 1 : -1);
45
59
  i0.ɵɵadvance();
46
- i0.ɵɵconditional(ctx_r1.ReadNotifications.length > 0 ? 2 : -1);
60
+ i0.ɵɵconditional(ctx_r0.ReadNotifications.length > 0 ? 2 : -1);
47
61
  } }
48
- function UserNotificationsComponent_Conditional_9_Conditional_13_Template(rf, ctx) { if (rf & 1) {
62
+ function UserNotificationsComponent_Conditional_12_Conditional_9_Template(rf, ctx) { if (rf & 1) {
49
63
  const _r5 = i0.ɵɵgetCurrentView();
50
- i0.ɵɵelementStart(0, "button", 39);
51
- i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_9_Conditional_13_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.selectReadOption("Unread")); });
52
- i0.ɵɵelementStart(1, "span", 27);
64
+ i0.ɵɵelementStart(0, "button", 35);
65
+ i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_12_Conditional_9_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.selectReadOption("Unread")); });
66
+ i0.ɵɵelementStart(1, "span", 25);
53
67
  i0.ɵɵtext(2, "Unread");
54
68
  i0.ɵɵelementEnd();
55
- i0.ɵɵelementStart(3, "span", 28);
69
+ i0.ɵɵelementStart(3, "span", 26);
56
70
  i0.ɵɵtext(4);
57
71
  i0.ɵɵelementEnd()();
58
72
  } if (rf & 2) {
59
- const ctx_r1 = i0.ɵɵnextContext(2);
60
- i0.ɵɵclassProp("active", ctx_r1.radioSelected === "Unread")("has-unread", ctx_r1.UnreadNotifications.length > 0);
61
- i0.ɵɵattribute("aria-pressed", ctx_r1.radioSelected === "Unread");
73
+ const ctx_r0 = i0.ɵɵnextContext(2);
74
+ i0.ɵɵclassProp("active", ctx_r0.radioSelected === "Unread")("has-unread", ctx_r0.UnreadNotifications.length > 0);
75
+ i0.ɵɵattribute("aria-pressed", ctx_r0.radioSelected === "Unread");
62
76
  i0.ɵɵadvance(3);
63
- i0.ɵɵclassProp("unread-count", ctx_r1.UnreadNotifications.length > 0);
77
+ i0.ɵɵclassProp("unread-count", ctx_r0.UnreadNotifications.length > 0);
64
78
  i0.ɵɵadvance();
65
- i0.ɵɵtextInterpolate1(" ", ctx_r1.UnreadNotifications.length, " ");
79
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.UnreadNotifications.length, " ");
66
80
  } }
67
- function UserNotificationsComponent_Conditional_9_Conditional_16_Template(rf, ctx) { if (rf & 1) {
81
+ function UserNotificationsComponent_Conditional_12_Conditional_10_Template(rf, ctx) { if (rf & 1) {
68
82
  const _r6 = i0.ɵɵgetCurrentView();
69
- i0.ɵɵelementStart(0, "button", 40);
70
- i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_9_Conditional_16_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.selectReadOption("Read")); });
71
- i0.ɵɵelementStart(1, "span", 27);
83
+ i0.ɵɵelementStart(0, "button", 36);
84
+ i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_12_Conditional_10_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.selectReadOption("Read")); });
85
+ i0.ɵɵelementStart(1, "span", 25);
72
86
  i0.ɵɵtext(2, "Read");
73
87
  i0.ɵɵelementEnd();
74
- i0.ɵɵelementStart(3, "span", 28);
88
+ i0.ɵɵelementStart(3, "span", 26);
75
89
  i0.ɵɵtext(4);
76
90
  i0.ɵɵelementEnd()();
77
91
  } if (rf & 2) {
78
- const ctx_r1 = i0.ɵɵnextContext(2);
79
- i0.ɵɵclassProp("active", ctx_r1.radioSelected === "Read");
80
- i0.ɵɵattribute("aria-pressed", ctx_r1.radioSelected === "Read");
92
+ const ctx_r0 = i0.ɵɵnextContext(2);
93
+ i0.ɵɵclassProp("active", ctx_r0.radioSelected === "Read");
94
+ i0.ɵɵattribute("aria-pressed", ctx_r0.radioSelected === "Read");
81
95
  i0.ɵɵadvance(4);
82
- i0.ɵɵtextInterpolate(ctx_r1.ReadNotifications.length);
96
+ i0.ɵɵtextInterpolate(ctx_r0.ReadNotifications.length);
83
97
  } }
84
- function UserNotificationsComponent_Conditional_9_Conditional_17_For_5_Template(rf, ctx) { if (rf & 1) {
85
- i0.ɵɵelementStart(0, "option", 42);
98
+ function UserNotificationsComponent_Conditional_12_Conditional_11_For_5_Template(rf, ctx) { if (rf & 1) {
99
+ i0.ɵɵelementStart(0, "option", 38);
86
100
  i0.ɵɵtext(1);
87
101
  i0.ɵɵelementEnd();
88
102
  } if (rf & 2) {
@@ -91,189 +105,281 @@ function UserNotificationsComponent_Conditional_9_Conditional_17_For_5_Template(
91
105
  i0.ɵɵadvance();
92
106
  i0.ɵɵtextInterpolate(type_r8.Name);
93
107
  } }
94
- function UserNotificationsComponent_Conditional_9_Conditional_17_Template(rf, ctx) { if (rf & 1) {
108
+ function UserNotificationsComponent_Conditional_12_Conditional_11_Template(rf, ctx) { if (rf & 1) {
95
109
  const _r7 = i0.ɵɵgetCurrentView();
96
- i0.ɵɵelementStart(0, "div", 33)(1, "select", 41);
97
- i0.ɵɵtwoWayListener("ngModelChange", function UserNotificationsComponent_Conditional_9_Conditional_17_Template_select_ngModelChange_1_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); i0.ɵɵtwoWayBindingSet(ctx_r1.selectedTypeFilter, $event) || (ctx_r1.selectedTypeFilter = $event); return i0.ɵɵresetView($event); });
98
- i0.ɵɵlistener("change", function UserNotificationsComponent_Conditional_9_Conditional_17_Template_select_change_1_listener() { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.onTypeFilterChange(ctx_r1.selectedTypeFilter)); });
99
- i0.ɵɵelementStart(2, "option", 42);
110
+ i0.ɵɵelementStart(0, "div", 29)(1, "select", 37);
111
+ i0.ɵɵtwoWayListener("ngModelChange", function UserNotificationsComponent_Conditional_12_Conditional_11_Template_select_ngModelChange_1_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r0 = i0.ɵɵnextContext(2); i0.ɵɵtwoWayBindingSet(ctx_r0.selectedTypeFilter, $event) || (ctx_r0.selectedTypeFilter = $event); return i0.ɵɵresetView($event); });
112
+ i0.ɵɵlistener("change", function UserNotificationsComponent_Conditional_12_Conditional_11_Template_select_change_1_listener() { i0.ɵɵrestoreView(_r7); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.onTypeFilterChange(ctx_r0.selectedTypeFilter)); });
113
+ i0.ɵɵelementStart(2, "option", 38);
100
114
  i0.ɵɵtext(3, "All Types");
101
115
  i0.ɵɵelementEnd();
102
- i0.ɵɵrepeaterCreate(4, UserNotificationsComponent_Conditional_9_Conditional_17_For_5_Template, 2, 2, "option", 42, _forTrack0);
116
+ i0.ɵɵrepeaterCreate(4, UserNotificationsComponent_Conditional_12_Conditional_11_For_5_Template, 2, 2, "option", 38, _forTrack0);
103
117
  i0.ɵɵelementEnd()();
104
118
  } if (rf & 2) {
105
- const ctx_r1 = i0.ɵɵnextContext(2);
119
+ const ctx_r0 = i0.ɵɵnextContext(2);
106
120
  i0.ɵɵadvance();
107
- i0.ɵɵtwoWayProperty("ngModel", ctx_r1.selectedTypeFilter);
121
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r0.selectedTypeFilter);
108
122
  i0.ɵɵadvance();
109
123
  i0.ɵɵproperty("value", null);
110
124
  i0.ɵɵadvance(2);
111
- i0.ɵɵrepeater(ctx_r1.notificationTypes);
125
+ i0.ɵɵrepeater(ctx_r0.notificationTypes);
112
126
  } }
113
- function UserNotificationsComponent_Conditional_9_Conditional_22_Template(rf, ctx) { if (rf & 1) {
114
- i0.ɵɵelementStart(0, "div", 38)(1, "span", 43);
115
- i0.ɵɵelement(2, "i", 44);
127
+ function UserNotificationsComponent_Conditional_12_Conditional_16_Template(rf, ctx) { if (rf & 1) {
128
+ i0.ɵɵelementStart(0, "div", 34)(1, "span", 39);
129
+ i0.ɵɵelement(2, "i", 40);
116
130
  i0.ɵɵtext(3);
117
131
  i0.ɵɵelementEnd()();
118
132
  } if (rf & 2) {
119
- const ctx_r1 = i0.ɵɵnextContext(2);
133
+ const ctx_r0 = i0.ɵɵnextContext(2);
120
134
  i0.ɵɵadvance(3);
121
- i0.ɵɵtextInterpolate2(" Showing ", ctx_r1.NotificationsToShow.length, " of ", ctx_r1.AllNotifications.length, " ");
135
+ i0.ɵɵtextInterpolate2(" Showing ", ctx_r0.NotificationsToShow.length, " of ", ctx_r0.AllNotifications.length, " ");
122
136
  } }
123
- function UserNotificationsComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
137
+ function UserNotificationsComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
124
138
  const _r4 = i0.ɵɵgetCurrentView();
125
- i0.ɵɵelementStart(0, "div", 10)(1, "div", 22)(2, "div", 23)(3, "div", 24)(4, "input", 25, 0);
126
- i0.ɵɵlistener("change", function UserNotificationsComponent_Conditional_9_Template_input_change_4_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onReadRadioChanged($event)); });
127
- i0.ɵɵelementEnd();
128
- i0.ɵɵelementStart(6, "button", 26);
129
- i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_9_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.selectReadOption("All")); });
130
- i0.ɵɵelementStart(7, "span", 27);
131
- i0.ɵɵtext(8, "All");
139
+ i0.ɵɵelementStart(0, "div", 9)(1, "div", 21)(2, "div", 22)(3, "div", 23)(4, "button", 24);
140
+ i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_12_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.selectReadOption("All")); });
141
+ i0.ɵɵelementStart(5, "span", 25);
142
+ i0.ɵɵtext(6, "All");
132
143
  i0.ɵɵelementEnd();
133
- i0.ɵɵelementStart(9, "span", 28);
134
- i0.ɵɵtext(10);
144
+ i0.ɵɵelementStart(7, "span", 26);
145
+ i0.ɵɵtext(8);
135
146
  i0.ɵɵelementEnd()();
136
- i0.ɵɵelementStart(11, "input", 29, 1);
137
- i0.ɵɵlistener("change", function UserNotificationsComponent_Conditional_9_Template_input_change_11_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onReadRadioChanged($event)); });
147
+ i0.ɵɵconditionalCreate(9, UserNotificationsComponent_Conditional_12_Conditional_9_Template, 5, 8, "button", 27);
148
+ i0.ɵɵconditionalCreate(10, UserNotificationsComponent_Conditional_12_Conditional_10_Template, 5, 4, "button", 28);
138
149
  i0.ɵɵelementEnd();
139
- i0.ɵɵconditionalCreate(13, UserNotificationsComponent_Conditional_9_Conditional_13_Template, 5, 8, "button", 30);
140
- i0.ɵɵelementStart(14, "input", 31, 2);
141
- i0.ɵɵlistener("change", function UserNotificationsComponent_Conditional_9_Template_input_change_14_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onReadRadioChanged($event)); });
142
- i0.ɵɵelementEnd();
143
- i0.ɵɵconditionalCreate(16, UserNotificationsComponent_Conditional_9_Conditional_16_Template, 5, 4, "button", 32);
144
- i0.ɵɵelementEnd();
145
- i0.ɵɵconditionalCreate(17, UserNotificationsComponent_Conditional_9_Conditional_17_Template, 6, 2, "div", 33);
150
+ i0.ɵɵconditionalCreate(11, UserNotificationsComponent_Conditional_12_Conditional_11_Template, 6, 2, "div", 29);
146
151
  i0.ɵɵelementEnd()();
147
- i0.ɵɵelementStart(18, "div", 34)(19, "div", 35);
148
- i0.ɵɵelement(20, "i", 36);
149
- i0.ɵɵelementStart(21, "input", 37);
150
- i0.ɵɵlistener("input", function UserNotificationsComponent_Conditional_9_Template_input_input_21_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onFilterChanged($event.target.value)); });
152
+ i0.ɵɵelementStart(12, "div", 30)(13, "div", 31);
153
+ i0.ɵɵelement(14, "i", 32);
154
+ i0.ɵɵelementStart(15, "input", 33);
155
+ i0.ɵɵlistener("input", function UserNotificationsComponent_Conditional_12_Template_input_input_15_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onFilterChanged($event.target.value)); });
151
156
  i0.ɵɵelementEnd()()();
152
- i0.ɵɵconditionalCreate(22, UserNotificationsComponent_Conditional_9_Conditional_22_Template, 4, 2, "div", 38);
157
+ i0.ɵɵconditionalCreate(16, UserNotificationsComponent_Conditional_12_Conditional_16_Template, 4, 2, "div", 34);
153
158
  i0.ɵɵelementEnd();
154
159
  } if (rf & 2) {
155
- const ctx_r1 = i0.ɵɵnextContext();
156
- i0.ɵɵadvance(6);
157
- i0.ɵɵclassProp("active", ctx_r1.radioSelected === "All");
158
- i0.ɵɵattribute("aria-pressed", ctx_r1.radioSelected === "All");
160
+ const ctx_r0 = i0.ɵɵnextContext();
159
161
  i0.ɵɵadvance(4);
160
- i0.ɵɵtextInterpolate(ctx_r1.AllNotifications.length);
161
- i0.ɵɵadvance(3);
162
- i0.ɵɵconditional(ctx_r1.UnreadNotifications.length > 0 || ctx_r1.ReadNotifications.length === 0 ? 13 : -1);
163
- i0.ɵɵadvance(3);
164
- i0.ɵɵconditional(ctx_r1.ReadNotifications.length > 0 || ctx_r1.UnreadNotifications.length === 0 ? 16 : -1);
162
+ i0.ɵɵclassProp("active", ctx_r0.radioSelected === "All");
163
+ i0.ɵɵattribute("aria-pressed", ctx_r0.radioSelected === "All");
164
+ i0.ɵɵadvance(4);
165
+ i0.ɵɵtextInterpolate(ctx_r0.AllNotifications.length);
166
+ i0.ɵɵadvance();
167
+ i0.ɵɵconditional(ctx_r0.UnreadNotifications.length > 0 || ctx_r0.ReadNotifications.length === 0 ? 9 : -1);
165
168
  i0.ɵɵadvance();
166
- i0.ɵɵconditional(ctx_r1.notificationTypes.length > 0 ? 17 : -1);
169
+ i0.ɵɵconditional(ctx_r0.ReadNotifications.length > 0 || ctx_r0.UnreadNotifications.length === 0 ? 10 : -1);
170
+ i0.ɵɵadvance();
171
+ i0.ɵɵconditional(ctx_r0.notificationTypes.length > 0 ? 11 : -1);
167
172
  i0.ɵɵadvance(4);
168
- i0.ɵɵproperty("value", ctx_r1.currentFilter);
173
+ i0.ɵɵproperty("value", ctx_r0.currentFilter);
169
174
  i0.ɵɵadvance();
170
- i0.ɵɵconditional(ctx_r1.NotificationsToShow.length !== ctx_r1.AllNotifications.length ? 22 : -1);
175
+ i0.ɵɵconditional(ctx_r0.NotificationsToShow.length !== ctx_r0.AllNotifications.length ? 16 : -1);
171
176
  } }
172
- function UserNotificationsComponent_Conditional_11_Template(rf, ctx) { if (rf & 1) {
177
+ function UserNotificationsComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
178
+ i0.ɵɵelement(0, "mj-empty-state", 11);
179
+ } }
180
+ function UserNotificationsComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
173
181
  i0.ɵɵelement(0, "mj-empty-state", 12);
174
182
  } }
175
- function UserNotificationsComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
176
- i0.ɵɵelement(0, "mj-empty-state", 13);
183
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_10_Template(rf, ctx) { if (rf & 1) {
184
+ i0.ɵɵtext(0);
185
+ } if (rf & 2) {
186
+ const vm_r10 = i0.ɵɵnextContext().$implicit;
187
+ i0.ɵɵtextInterpolate1(" ", vm_r10.Relative, " ");
188
+ } }
189
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_11_Template(rf, ctx) { if (rf & 1) {
190
+ i0.ɵɵelementStart(0, "time");
191
+ i0.ɵɵtext(1);
192
+ i0.ɵɵpipe(2, "date");
193
+ i0.ɵɵelementEnd();
194
+ } if (rf & 2) {
195
+ const vm_r10 = i0.ɵɵnextContext().$implicit;
196
+ i0.ɵɵattribute("datetime", vm_r10.N.__mj_CreatedAt);
197
+ i0.ɵɵadvance();
198
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(2, 2, vm_r10.N.__mj_CreatedAt, "MMM d, h:mm a"));
177
199
  } }
178
- function UserNotificationsComponent_Conditional_13_For_2_Conditional_19_Template(rf, ctx) { if (rf & 1) {
200
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_15_Template(rf, ctx) { if (rf & 1) {
201
+ i0.ɵɵelementStart(0, "span", 52);
202
+ i0.ɵɵelement(1, "i", 59);
203
+ i0.ɵɵtext(2, " Open ");
204
+ i0.ɵɵelementEnd();
205
+ } }
206
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_16_Template(rf, ctx) { if (rf & 1) {
207
+ i0.ɵɵelementStart(0, "p", 53);
208
+ i0.ɵɵtext(1);
209
+ i0.ɵɵelementEnd();
210
+ } if (rf & 2) {
211
+ const vm_r10 = i0.ɵɵnextContext().$implicit;
212
+ i0.ɵɵadvance();
213
+ i0.ɵɵtextInterpolate(vm_r10.Preview);
214
+ } }
215
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_17_Case_1_Template(rf, ctx) { if (rf & 1) {
216
+ i0.ɵɵelement(0, "div", 61);
217
+ } if (rf & 2) {
218
+ const vm_r10 = i0.ɵɵnextContext(2).$implicit;
219
+ i0.ɵɵproperty("innerHTML", vm_r10.Body, i0.ɵɵsanitizeHtml);
220
+ } }
221
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_17_Case_2_Template(rf, ctx) { if (rf & 1) {
222
+ i0.ɵɵelement(0, "mj-markdown", 62);
223
+ } if (rf & 2) {
224
+ const vm_r10 = i0.ɵɵnextContext(2).$implicit;
225
+ i0.ɵɵproperty("data", vm_r10.Body)("enableMermaid", false);
226
+ } }
227
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_17_Case_3_Template(rf, ctx) { if (rf & 1) {
228
+ i0.ɵɵelementStart(0, "p", 63);
229
+ i0.ɵɵtext(1);
230
+ i0.ɵɵelementEnd();
231
+ } if (rf & 2) {
232
+ const vm_r10 = i0.ɵɵnextContext(2).$implicit;
233
+ i0.ɵɵadvance();
234
+ i0.ɵɵtextInterpolate(vm_r10.N.Message);
235
+ } }
236
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_17_Template(rf, ctx) { if (rf & 1) {
179
237
  const _r11 = i0.ɵɵgetCurrentView();
180
- i0.ɵɵelementStart(0, "button", 60);
181
- i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_13_For_2_Conditional_19_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r11); const notification_r10 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.markAsRead(notification_r10, true, null)); });
182
- i0.ɵɵelement(1, "i", 61);
183
- i0.ɵɵelementStart(2, "span", 62);
238
+ i0.ɵɵelementStart(0, "div", 60);
239
+ i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_17_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r11); return i0.ɵɵresetView($event.stopPropagation()); });
240
+ i0.ɵɵconditionalCreate(1, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_17_Case_1_Template, 1, 1, "div", 61)(2, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_17_Case_2_Template, 1, 2, "mj-markdown", 62)(3, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_17_Case_3_Template, 2, 1, "p", 63);
241
+ i0.ɵɵelementEnd();
242
+ } if (rf & 2) {
243
+ let tmp_22_0;
244
+ const vm_r10 = i0.ɵɵnextContext().$implicit;
245
+ i0.ɵɵadvance();
246
+ i0.ɵɵconditional((tmp_22_0 = vm_r10.Kind) === "html" ? 1 : tmp_22_0 === "markdown" ? 2 : 3);
247
+ } }
248
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_18_Template(rf, ctx) { if (rf & 1) {
249
+ const _r12 = i0.ɵɵgetCurrentView();
250
+ i0.ɵɵelementStart(0, "button", 64);
251
+ i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_18_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r12); const vm_r10 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.ToggleExpanded(vm_r10, $event)); });
252
+ i0.ɵɵelement(1, "i", 65);
253
+ i0.ɵɵtext(2);
254
+ i0.ɵɵelementEnd();
255
+ } if (rf & 2) {
256
+ const vm_r10 = i0.ɵɵnextContext().$implicit;
257
+ const ctx_r0 = i0.ɵɵnextContext(3);
258
+ i0.ɵɵattribute("aria-expanded", ctx_r0.IsExpanded(vm_r10));
259
+ i0.ɵɵadvance();
260
+ i0.ɵɵclassProp("fa-chevron-down", !ctx_r0.IsExpanded(vm_r10))("fa-chevron-up", ctx_r0.IsExpanded(vm_r10));
261
+ i0.ɵɵadvance();
262
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.IsExpanded(vm_r10) ? "Show less" : "Show more", " ");
263
+ } }
264
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_20_Template(rf, ctx) { if (rf & 1) {
265
+ const _r13 = i0.ɵɵgetCurrentView();
266
+ i0.ɵɵelementStart(0, "button", 66);
267
+ i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_20_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r13); const vm_r10 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.markAsRead(vm_r10.N, true, null)); });
268
+ i0.ɵɵelement(1, "i", 67);
269
+ i0.ɵɵelementStart(2, "span", 68);
184
270
  i0.ɵɵtext(3, "Read");
185
271
  i0.ɵɵelementEnd()();
186
272
  } if (rf & 2) {
187
- const notification_r10 = i0.ɵɵnextContext().$implicit;
188
- i0.ɵɵattribute("aria-label", "Mark " + notification_r10.Title + " as read");
273
+ const vm_r10 = i0.ɵɵnextContext().$implicit;
274
+ i0.ɵɵattribute("aria-label", "Mark " + vm_r10.N.Title + " as read");
189
275
  } }
190
- function UserNotificationsComponent_Conditional_13_For_2_Conditional_20_Template(rf, ctx) { if (rf & 1) {
191
- const _r12 = i0.ɵɵgetCurrentView();
192
- i0.ɵɵelementStart(0, "button", 63);
193
- i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_13_For_2_Conditional_20_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r12); const notification_r10 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.markAsRead(notification_r10, false, null)); });
194
- i0.ɵɵelement(1, "i", 21);
195
- i0.ɵɵelementStart(2, "span", 62);
276
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_21_Template(rf, ctx) { if (rf & 1) {
277
+ const _r14 = i0.ɵɵgetCurrentView();
278
+ i0.ɵɵelementStart(0, "button", 69);
279
+ i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_21_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r14); const vm_r10 = i0.ɵɵnextContext().$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.markAsRead(vm_r10.N, false, null)); });
280
+ i0.ɵɵelement(1, "i", 20);
281
+ i0.ɵɵelementStart(2, "span", 68);
196
282
  i0.ɵɵtext(3, "Unread");
197
283
  i0.ɵɵelementEnd()();
198
284
  } if (rf & 2) {
199
- const notification_r10 = i0.ɵɵnextContext().$implicit;
200
- i0.ɵɵattribute("aria-label", "Mark " + notification_r10.Title + " as unread");
285
+ const vm_r10 = i0.ɵɵnextContext().$implicit;
286
+ i0.ɵɵattribute("aria-label", "Mark " + vm_r10.N.Title + " as unread");
201
287
  } }
202
- function UserNotificationsComponent_Conditional_13_For_2_Template(rf, ctx) { if (rf & 1) {
288
+ function UserNotificationsComponent_Conditional_16_For_2_For_3_Template(rf, ctx) { if (rf & 1) {
203
289
  const _r9 = i0.ɵɵgetCurrentView();
204
- i0.ɵɵelementStart(0, "article", 46);
205
- i0.ɵɵelement(1, "div", 47);
206
- i0.ɵɵelementStart(2, "div", 48);
290
+ i0.ɵɵelementStart(0, "article", 43);
291
+ i0.ɵɵelement(1, "div", 44);
292
+ i0.ɵɵelementStart(2, "div", 45);
207
293
  i0.ɵɵelement(3, "i");
208
294
  i0.ɵɵelementEnd();
209
- i0.ɵɵelementStart(4, "div", 49);
210
- i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_13_For_2_Template_div_click_4_listener() { const notification_r10 = i0.ɵɵrestoreView(_r9).$implicit; const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.notificationClicked(notification_r10)); });
211
- i0.ɵɵelementStart(5, "div", 50)(6, "h3", 51);
295
+ i0.ɵɵelementStart(4, "div", 46);
296
+ i0.ɵɵlistener("click", function UserNotificationsComponent_Conditional_16_For_2_For_3_Template_div_click_4_listener() { const vm_r10 = i0.ɵɵrestoreView(_r9).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.notificationClicked(vm_r10.N)); });
297
+ i0.ɵɵelementStart(5, "div", 47)(6, "h3", 48);
212
298
  i0.ɵɵtext(7);
213
299
  i0.ɵɵelementEnd();
214
- i0.ɵɵelementStart(8, "span", 52);
215
- i0.ɵɵelement(9, "i", 53);
216
- i0.ɵɵelementStart(10, "time");
217
- i0.ɵɵtext(11);
218
- i0.ɵɵpipe(12, "date");
219
- i0.ɵɵelementEnd()()();
220
- i0.ɵɵelementStart(13, "div", 54)(14, "span", 55);
221
- i0.ɵɵtext(15);
300
+ i0.ɵɵelementStart(8, "span", 49);
301
+ i0.ɵɵpipe(9, "date");
302
+ i0.ɵɵconditionalCreate(10, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_10_Template, 1, 1)(11, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_11_Template, 3, 5, "time");
222
303
  i0.ɵɵelementEnd()();
223
- i0.ɵɵelementStart(16, "p", 56);
224
- i0.ɵɵtext(17);
225
- i0.ɵɵelementEnd()();
226
- i0.ɵɵelementStart(18, "div", 57);
227
- i0.ɵɵconditionalCreate(19, UserNotificationsComponent_Conditional_13_For_2_Conditional_19_Template, 4, 1, "button", 58)(20, UserNotificationsComponent_Conditional_13_For_2_Conditional_20_Template, 4, 1, "button", 59);
304
+ i0.ɵɵelementStart(12, "div", 50)(13, "span", 51);
305
+ i0.ɵɵtext(14);
306
+ i0.ɵɵelementEnd();
307
+ i0.ɵɵconditionalCreate(15, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_15_Template, 3, 0, "span", 52);
308
+ i0.ɵɵelementEnd();
309
+ i0.ɵɵconditionalCreate(16, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_16_Template, 2, 1, "p", 53)(17, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_17_Template, 4, 1, "div", 54);
310
+ i0.ɵɵconditionalCreate(18, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_18_Template, 3, 6, "button", 55);
311
+ i0.ɵɵelementEnd();
312
+ i0.ɵɵelementStart(19, "div", 56);
313
+ i0.ɵɵconditionalCreate(20, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_20_Template, 4, 1, "button", 57)(21, UserNotificationsComponent_Conditional_16_For_2_For_3_Conditional_21_Template, 4, 1, "button", 58);
228
314
  i0.ɵɵelementEnd()();
229
315
  } if (rf & 2) {
230
- const notification_r10 = ctx.$implicit;
231
- const ctx_r1 = i0.ɵɵnextContext(2);
232
- i0.ɵɵclassProp("unread", notification_r10.Unread)("clickable", ctx_r1.isNotificationClickable(notification_r10));
233
- i0.ɵɵattribute("aria-label", notification_r10.Title);
234
- i0.ɵɵadvance(2);
235
- i0.ɵɵstyleProp("background-color", ctx_r1.getTypeColor(notification_r10) + "15");
236
- i0.ɵɵadvance();
237
- i0.ɵɵclassMap("fa-solid " + ctx_r1.getTypeIcon(notification_r10));
238
- i0.ɵɵstyleProp("color", ctx_r1.getTypeColor(notification_r10));
316
+ const vm_r10 = ctx.$implicit;
317
+ const ctx_r0 = i0.ɵɵnextContext(3);
318
+ i0.ɵɵclassProp("unread", vm_r10.N.Unread)("expanded", ctx_r0.IsExpanded(vm_r10))("clickable", vm_r10.Clickable);
319
+ i0.ɵɵattribute("aria-label", vm_r10.N.Title);
239
320
  i0.ɵɵadvance(3);
240
- i0.ɵɵclassProp("card-title-unread", notification_r10.Unread);
241
- i0.ɵɵadvance();
242
- i0.ɵɵtextInterpolate1(" ", notification_r10.Title, " ");
321
+ i0.ɵɵclassMap("fa-solid " + ctx_r0.getTypeIcon(vm_r10.N));
322
+ i0.ɵɵstyleProp("color", ctx_r0.getTypeColor(vm_r10.N));
243
323
  i0.ɵɵadvance(3);
244
- i0.ɵɵattribute("datetime", notification_r10.__mj_CreatedAt);
324
+ i0.ɵɵclassProp("card-title-unread", vm_r10.N.Unread);
245
325
  i0.ɵɵadvance();
246
- i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind2(12, 23, notification_r10.__mj_CreatedAt, "MMM d, h:mm a"), " ");
247
- i0.ɵɵadvance(3);
248
- i0.ɵɵstyleProp("background-color", ctx_r1.getTypeColor(notification_r10) + "15")("color", ctx_r1.getTypeColor(notification_r10));
326
+ i0.ɵɵtextInterpolate1(" ", vm_r10.N.Title, " ");
327
+ i0.ɵɵadvance();
328
+ i0.ɵɵproperty("title", i0.ɵɵpipeBind2(9, 21, vm_r10.N.__mj_CreatedAt, "medium") ?? "");
329
+ i0.ɵɵadvance(2);
330
+ i0.ɵɵconditional(vm_r10.Relative ? 10 : 11);
331
+ i0.ɵɵadvance(4);
332
+ i0.ɵɵtextInterpolate(ctx_r0.getTypeName(vm_r10.N));
249
333
  i0.ɵɵadvance();
250
- i0.ɵɵtextInterpolate1(" ", ctx_r1.getTypeName(notification_r10), " ");
334
+ i0.ɵɵconditional(vm_r10.Clickable ? 15 : -1);
335
+ i0.ɵɵadvance();
336
+ i0.ɵɵconditional(!ctx_r0.IsExpanded(vm_r10) ? 16 : 17);
251
337
  i0.ɵɵadvance(2);
252
- i0.ɵɵtextInterpolate(notification_r10.Message);
338
+ i0.ɵɵconditional(vm_r10.Expandable ? 18 : -1);
253
339
  i0.ɵɵadvance(2);
254
- i0.ɵɵconditional(notification_r10.Unread ? 19 : 20);
340
+ i0.ɵɵconditional(vm_r10.N.Unread ? 20 : 21);
341
+ } }
342
+ function UserNotificationsComponent_Conditional_16_For_2_Template(rf, ctx) { if (rf & 1) {
343
+ i0.ɵɵelementStart(0, "div", 41);
344
+ i0.ɵɵtext(1);
345
+ i0.ɵɵelementEnd();
346
+ i0.ɵɵrepeaterCreate(2, UserNotificationsComponent_Conditional_16_For_2_For_3_Template, 22, 24, "article", 42, _forTrack2);
347
+ } if (rf & 2) {
348
+ const group_r15 = ctx.$implicit;
349
+ i0.ɵɵadvance();
350
+ i0.ɵɵtextInterpolate(group_r15.Label);
351
+ i0.ɵɵadvance();
352
+ i0.ɵɵrepeater(group_r15.Items);
255
353
  } }
256
- function UserNotificationsComponent_Conditional_13_Template(rf, ctx) { if (rf & 1) {
257
- i0.ɵɵelementStart(0, "div", 14);
258
- i0.ɵɵrepeaterCreate(1, UserNotificationsComponent_Conditional_13_For_2_Template, 21, 26, "article", 45, _forTrack0);
354
+ function UserNotificationsComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
355
+ i0.ɵɵelementStart(0, "div", 13);
356
+ i0.ɵɵrepeaterCreate(1, UserNotificationsComponent_Conditional_16_For_2_Template, 4, 1, null, null, _forTrack1);
259
357
  i0.ɵɵelementEnd();
260
358
  } if (rf & 2) {
261
- const ctx_r1 = i0.ɵɵnextContext();
359
+ const ctx_r0 = i0.ɵɵnextContext();
262
360
  i0.ɵɵadvance();
263
- i0.ɵɵrepeater(ctx_r1.NotificationsToShow);
361
+ i0.ɵɵrepeater(ctx_r0.Groups);
264
362
  } }
363
+ /** Collapsed-preview cap (characters of plain text). */
364
+ const PREVIEW_MAX_CHARS = 240;
365
+ /** How often the snapshot relative-time labels refresh. */
366
+ const RELATIVE_REFRESH_MS = 60_000;
265
367
  export class UserNotificationsComponent extends BaseAngularComponent {
266
368
  sharedService;
267
369
  navigationService;
268
370
  appManager;
269
- allRadio;
270
- unreadRadio;
271
- readRadio;
371
+ cdr = inject(ChangeDetectorRef);
272
372
  radioSelected = 'All';
273
373
  currentFilter = '';
274
374
  notificationTypes = [];
275
375
  selectedTypeFilter = null;
276
376
  loadingTypes = true;
377
+ /** IDs of cards currently expanded to their full rendered content. */
378
+ Expanded = new Set();
379
+ groups = [];
380
+ groupsKey = '';
381
+ vmCache = new Map();
382
+ relativeTimer = null;
277
383
  constructor(sharedService, navigationService, appManager) {
278
384
  super();
279
385
  this.sharedService = sharedService;
@@ -282,10 +388,23 @@ export class UserNotificationsComponent extends BaseAngularComponent {
282
388
  }
283
389
  async ngOnInit() {
284
390
  this.loadNotificationTypes();
391
+ // Refresh the snapshot relative-time labels between change-detection passes.
392
+ this.relativeTimer = setInterval(() => {
393
+ for (const vm of this.vmCache.values()) {
394
+ vm.Relative = this.relativeTime(vm.N.__mj_CreatedAt);
395
+ }
396
+ this.groupsKey = ''; // day buckets can shift at midnight
397
+ this.cdr.markForCheck();
398
+ }, RELATIVE_REFRESH_MS);
285
399
  }
286
400
  ngAfterViewInit() {
287
401
  this.sharedService.InvokeManualResize(); // make sure the notifications component is sized correctly
288
402
  }
403
+ ngOnDestroy() {
404
+ if (this.relativeTimer != null) {
405
+ clearInterval(this.relativeTimer);
406
+ }
407
+ }
289
408
  loadNotificationTypes() {
290
409
  // Get notification types from UserInfoEngine cache, sorted client-side
291
410
  // UserInfoEngine is auto-configured via @RegisterForStartup()
@@ -299,6 +418,9 @@ export class UserNotificationsComponent extends BaseAngularComponent {
299
418
  });
300
419
  this.loadingTypes = false;
301
420
  }
421
+ // ========================================================================
422
+ // Data shaping (filter → view models → day groups)
423
+ // ========================================================================
302
424
  get NotificationsToShow() {
303
425
  let temp = [];
304
426
  switch (this.radioSelected) {
@@ -324,6 +446,153 @@ export class UserNotificationsComponent extends BaseAngularComponent {
324
446
  }
325
447
  return temp;
326
448
  }
449
+ /**
450
+ * The filtered notifications as render-ready day groups. Memoized on a composite
451
+ * key of the inputs so the getter stays cheap across change-detection passes and
452
+ * the (time-sensitive) VM snapshots never rebuild mid-pass.
453
+ */
454
+ get Groups() {
455
+ const list = this.NotificationsToShow;
456
+ const unread = list.filter(n => n.Unread).length;
457
+ const key = `${list.length}|${unread}|${this.radioSelected}|${this.selectedTypeFilter}|${this.currentFilter}|${list[0]?.ID ?? ''}|${list[list.length - 1]?.ID ?? ''}`;
458
+ if (key !== this.groupsKey) {
459
+ this.groups = this.buildGroups(list);
460
+ this.groupsKey = key;
461
+ }
462
+ return this.groups;
463
+ }
464
+ buildGroups(list) {
465
+ const groups = [];
466
+ let current = null;
467
+ for (const n of list) {
468
+ const label = this.dayLabel(n.__mj_CreatedAt);
469
+ if (!current || current.Label !== label) {
470
+ current = { Key: `${label}-${n.ID}`, Label: label, Items: [] };
471
+ groups.push(current);
472
+ }
473
+ current.Items.push(this.vmFor(n));
474
+ }
475
+ return groups;
476
+ }
477
+ vmFor(n) {
478
+ const cached = this.vmCache.get(n.ID);
479
+ if (cached) {
480
+ cached.N = n;
481
+ cached.Clickable = this.isNotificationClickable(n);
482
+ return cached;
483
+ }
484
+ const { kind, body, preview, expandable } = this.classifyMessage(n.Message ?? '');
485
+ const vm = {
486
+ N: n,
487
+ Kind: kind,
488
+ Body: body,
489
+ Preview: preview,
490
+ Expandable: expandable,
491
+ Relative: this.relativeTime(n.__mj_CreatedAt),
492
+ Clickable: this.isNotificationClickable(n),
493
+ };
494
+ this.vmCache.set(n.ID, vm);
495
+ return vm;
496
+ }
497
+ /**
498
+ * Classifies a notification Message for rendering:
499
+ * - Full HTML documents (e.g. templated email bodies) and HTML fragments →
500
+ * parsed with DOMParser, chrome elements (style/script/link/meta/title) removed,
501
+ * and the body markup kept for a SANITIZED [innerHTML] binding (Angular's
502
+ * default sanitizer strips scripts/event handlers on bind).
503
+ * - Markdown-looking text → rendered through mj-markdown when expanded.
504
+ * - Everything else → plain text.
505
+ */
506
+ classifyMessage(message) {
507
+ const trimmed = (message ?? '').trim();
508
+ const looksHtml = /^<!doctype\s|^<html[\s>]/i.test(trimmed) || /<\/?[a-z][^>]*>/i.test(trimmed);
509
+ if (looksHtml) {
510
+ const doc = new DOMParser().parseFromString(trimmed, 'text/html');
511
+ for (const el of Array.from(doc.querySelectorAll('style, script, link, meta, title'))) {
512
+ el.remove();
513
+ }
514
+ const body = doc.body?.innerHTML?.trim() ?? '';
515
+ const text = this.collapseWhitespace(doc.body?.textContent ?? '');
516
+ return { kind: 'html', body, preview: this.excerpt(text), expandable: true };
517
+ }
518
+ const looksMarkdown = /(^|\n)#{1,6}\s|\*\*[^*]+\*\*|(^|\n)\s*[-*]\s+|\[[^\]]+\]\([^)]+\)|```/m.test(trimmed);
519
+ const text = this.collapseWhitespace(trimmed);
520
+ if (looksMarkdown) {
521
+ return { kind: 'markdown', body: trimmed, preview: this.excerpt(this.stripMarkdown(text)), expandable: true };
522
+ }
523
+ return { kind: 'text', body: '', preview: text, expandable: text.length > PREVIEW_MAX_CHARS };
524
+ }
525
+ collapseWhitespace(value) {
526
+ return value.replace(/\s+/g, ' ').trim();
527
+ }
528
+ excerpt(text) {
529
+ return text.length > PREVIEW_MAX_CHARS ? `${text.substring(0, PREVIEW_MAX_CHARS - 1)}…` : text;
530
+ }
531
+ /** Light de-noising of markdown syntax for the plain-text preview. */
532
+ stripMarkdown(text) {
533
+ return text
534
+ .replace(/```[\s\S]*?```/g, ' ')
535
+ .replace(/[#*_`>]+/g, '')
536
+ .replace(/\[([^\]]+)\]\([^)]*\)/g, '$1')
537
+ .replace(/\s+/g, ' ')
538
+ .trim();
539
+ }
540
+ /** Compact "just now / 5m ago / 3h ago / 2d ago" label. NEVER call from templates (NG0100) — snapshot only. */
541
+ relativeTime(value) {
542
+ if (!value) {
543
+ return '';
544
+ }
545
+ const ms = Date.now() - new Date(value).getTime();
546
+ const minutes = Math.floor(ms / 60_000);
547
+ if (minutes < 1)
548
+ return 'just now';
549
+ if (minutes < 60)
550
+ return `${minutes}m ago`;
551
+ const hours = Math.floor(minutes / 60);
552
+ if (hours < 24)
553
+ return `${hours}h ago`;
554
+ const days = Math.floor(hours / 24);
555
+ return days < 7 ? `${days}d ago` : '';
556
+ }
557
+ dayLabel(value) {
558
+ if (!value) {
559
+ return 'Earlier';
560
+ }
561
+ const d = new Date(value);
562
+ const now = new Date();
563
+ const startOfDay = (x) => new Date(x.getFullYear(), x.getMonth(), x.getDate()).getTime();
564
+ const diffDays = Math.round((startOfDay(now) - startOfDay(d)) / 86_400_000);
565
+ if (diffDays <= 0)
566
+ return 'Today';
567
+ if (diffDays === 1)
568
+ return 'Yesterday';
569
+ if (diffDays < 7)
570
+ return 'This Week';
571
+ return 'Earlier';
572
+ }
573
+ // ========================================================================
574
+ // Expand / collapse
575
+ // ========================================================================
576
+ IsExpanded(vm) {
577
+ return this.Expanded.has(vm.N.ID);
578
+ }
579
+ ToggleExpanded(vm, event) {
580
+ event.stopPropagation();
581
+ if (this.Expanded.has(vm.N.ID)) {
582
+ this.Expanded.delete(vm.N.ID);
583
+ }
584
+ else {
585
+ this.Expanded.add(vm.N.ID);
586
+ // Reading the full content is reading — mark it as such.
587
+ if (vm.N.Unread) {
588
+ void this.markAsRead(vm.N, true, null);
589
+ }
590
+ }
591
+ this.cdr.markForCheck();
592
+ }
593
+ // ========================================================================
594
+ // Clickability + navigation (unchanged behavior)
595
+ // ========================================================================
327
596
  isNotificationClickable(notification) {
328
597
  // Check for special types navigated via NavigationService (not a URL)
329
598
  if (notification.ResourceConfiguration && notification.ResourceConfiguration.trim().length > 0) {
@@ -401,41 +670,10 @@ export class UserNotificationsComponent extends BaseAngularComponent {
401
670
  }
402
671
  selectReadOption(option) {
403
672
  this.radioSelected = option;
404
- // now update the radio button group in the UI
405
- switch (option) {
406
- case 'All':
407
- this.allRadio.nativeElement.checked = true;
408
- break;
409
- case 'Unread':
410
- this.unreadRadio.nativeElement.checked = true;
411
- break;
412
- case 'Read':
413
- this.readRadio.nativeElement.checked = true;
414
- break;
415
- }
416
- }
417
- onReadRadioChanged(event) {
418
- if (event.target instanceof HTMLInputElement) {
419
- this.radioSelected = event.target.value;
420
- }
421
673
  }
422
674
  onFilterChanged(value) {
423
675
  this.currentFilter = value;
424
676
  }
425
- getItemTitleClass(notification) {
426
- if (notification.Unread) {
427
- return 'notification-title notification-title-unread';
428
- }
429
- return 'notification-title';
430
- }
431
- getItemWrapperClass(notification) {
432
- let classInfo = 'notification-wrap';
433
- if (this.isNotificationClickable(notification))
434
- classInfo += ' notification-wrap-clickable';
435
- if (notification.Unread)
436
- classInfo += ' notification-wrap-unread';
437
- return classInfo;
438
- }
439
677
  async markAsRead(notification, bRead, transGroup) {
440
678
  if (notification) {
441
679
  const notificationId = notification.ID;
@@ -472,37 +710,6 @@ export class UserNotificationsComponent extends BaseAngularComponent {
472
710
  }
473
711
  async markAllAsRead() {
474
712
  await this.markAll(true);
475
- // test harness for creating Conversations and Conversation Details record in a single transaction using variables
476
- await this.TestTransactionGroupVariables();
477
- }
478
- async TestTransactionGroupVariables() {
479
- const md = this.ProviderToUse;
480
- const transGroup = await md.CreateTransactionGroup();
481
- const conversation = await md.GetEntityObject('MJ: Conversations');
482
- conversation.UserID = md.CurrentUser.ID;
483
- conversation.Description = 'Test Conversation';
484
- conversation.TransactionGroup = transGroup;
485
- if (!await conversation.Save()) {
486
- this.sharedService.CreateSimpleNotification('Unable to create conversation', 'error', 5000);
487
- }
488
- const tvDefine = new TransactionVariable('NewConvoID', conversation, 'ID', 'Define');
489
- transGroup.AddVariable(tvDefine);
490
- const conversationDetail = await md.GetEntityObject('MJ: Conversation Details');
491
- conversationDetail.Message = 'Test Message';
492
- conversationDetail.Role = 'User';
493
- conversationDetail.ConversationID = 'x'; // fake UUID must be non-null to pass validation, this will be replaced by the variable, since we're part of a TG, not a real save, so doesn't validate it as a true fkey
494
- conversationDetail.TransactionGroup = transGroup;
495
- if (!await conversationDetail.Save()) {
496
- this.sharedService.CreateSimpleNotification('Unable to create conversation detail', 'error', 500);
497
- }
498
- const tvUse = new TransactionVariable('NewConvoID', conversationDetail, 'ConversationID', 'Use');
499
- transGroup.AddVariable(tvUse);
500
- if (await transGroup.Submit()) {
501
- this.sharedService.CreateSimpleNotification('Transaction Group with Variables worked', 'success', 5000);
502
- }
503
- else {
504
- this.sharedService.CreateSimpleNotification('Transaction Group with Variables failed', 'error', 5000);
505
- }
506
713
  }
507
714
  async markAllAsUnread() {
508
715
  await this.markAll(false);
@@ -669,7 +876,7 @@ export class UserNotificationsComponent extends BaseAngularComponent {
669
876
  }
670
877
  getTypeColor(notification) {
671
878
  const type = this.getNotificationType(notification.NotificationTypeID);
672
- return type?.Color || '#999';
879
+ return type?.Color || 'var(--mj-text-muted)';
673
880
  }
674
881
  getTypeName(notification) {
675
882
  const type = this.getNotificationType(notification.NotificationTypeID);
@@ -679,48 +886,36 @@ export class UserNotificationsComponent extends BaseAngularComponent {
679
886
  this.selectedTypeFilter = typeId;
680
887
  }
681
888
  static ɵfac = function UserNotificationsComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || UserNotificationsComponent)(i0.ɵɵdirectiveInject(i1.SharedService), i0.ɵɵdirectiveInject(i1.NavigationService), i0.ɵɵdirectiveInject(i2.ApplicationManager)); };
682
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UserNotificationsComponent, selectors: [["app-user-notifications"]], viewQuery: function UserNotificationsComponent_Query(rf, ctx) { if (rf & 1) {
683
- i0.ɵɵviewQuery(_c0, 5)(_c1, 5)(_c2, 5);
684
- } if (rf & 2) {
685
- let _t;
686
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.allRadio = _t.first);
687
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.unreadRadio = _t.first);
688
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.readRadio = _t.first);
689
- } }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 14, vars: 3, consts: [["allRadio", ""], ["unreadRadio", ""], ["readRadio", ""], [1, "notifications-container"], [1, "notifications-header"], [1, "header-top"], [1, "header-title"], [1, "title-icon"], [1, "fa-solid", "fa-bell"], [1, "header-actions"], [1, "filters-section"], [1, "notifications-content"], ["Variant", "success", "Icon", "fa-solid fa-bell-slash", "Title", "No Notifications", "Message", "You're all caught up! New notifications will appear here."], ["Variant", "no-results", "Icon", "fa-solid fa-filter-circle-xmark", "Title", "No Matching Notifications", "Message", "Try adjusting your filters to see more notifications."], [1, "notifications-list"], ["type", "button", "aria-label", "Mark all notifications as read", 1, "action-btn", "action-btn-ghost"], ["type", "button", "aria-label", "Mark all notifications as unread", 1, "action-btn", "action-btn-secondary"], ["type", "button", "aria-label", "Mark all notifications as read", 1, "action-btn", "action-btn-ghost", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-check-double"], [1, "btn-text"], ["type", "button", "aria-label", "Mark all notifications as unread", 1, "action-btn", "action-btn-secondary", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-envelope"], [1, "filters-slider"], [1, "slider-content"], ["role", "group", "aria-label", "Filter notifications by read status", 1, "header-stats"], ["type", "radio", "name", "UnreadStatus", "value", "All", "checked", "", "id", "filter-all", 1, "hidden-radio", 3, "change"], ["type", "button", "aria-label", "Show all notifications", 1, "stat-pill", 3, "click"], [1, "stat-label"], [1, "stat-count"], ["type", "radio", "name", "UnreadStatus", "value", "Unread", "id", "filter-unread", 1, "hidden-radio", 3, "change"], ["type", "button", "aria-label", "Show unread notifications", 1, "stat-pill", 3, "active", "has-unread"], ["type", "radio", "name", "UnreadStatus", "value", "Read", "id", "filter-read", 1, "hidden-radio", 3, "change"], ["type", "button", "aria-label", "Show read notifications", 1, "stat-pill", 3, "active"], [1, "filter-item", "type-filter-slider"], [1, "search-section"], [1, "search-container"], ["aria-hidden", "true", 1, "fa-solid", "fa-magnifying-glass", "search-icon"], ["type", "text", "placeholder", "Search notifications...", "aria-label", "Search notifications", 1, "search-input", 3, "input", "value"], [1, "filter-info"], ["type", "button", "aria-label", "Show unread notifications", 1, "stat-pill", 3, "click"], ["type", "button", "aria-label", "Show read notifications", 1, "stat-pill", 3, "click"], ["aria-label", "Filter by notification type", 1, "type-select", 3, "ngModelChange", "change", "ngModel"], [3, "value"], [1, "filter-badge"], ["aria-hidden", "true", 1, "fa-solid", "fa-filter"], ["role", "article", 1, "notification-card", 3, "unread", "clickable"], ["role", "article", 1, "notification-card"], ["aria-hidden", "true", 1, "card-indicator"], ["aria-hidden", "true", 1, "card-icon"], [1, "card-content", 3, "click"], [1, "card-header"], [1, "card-title"], [1, "card-time"], ["aria-hidden", "true", 1, "fa-regular", "fa-clock"], [1, "card-meta"], [1, "card-type"], [1, "card-message"], [1, "card-actions"], ["type", "button", "title", "Mark as Read", 1, "card-action-btn"], ["type", "button", "title", "Mark as Unread", 1, "card-action-btn"], ["type", "button", "title", "Mark as Read", 1, "card-action-btn", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-check"], [1, "action-text"], ["type", "button", "title", "Mark as Unread", 1, "card-action-btn", 3, "click"]], template: function UserNotificationsComponent_Template(rf, ctx) { if (rf & 1) {
690
- i0.ɵɵelementStart(0, "div", 3)(1, "div", 4)(2, "div", 5)(3, "div", 6)(4, "div", 7);
691
- i0.ɵɵelement(5, "i", 8);
889
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UserNotificationsComponent, selectors: [["app-user-notifications"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 17, vars: 4, consts: [[1, "notifications-container"], [1, "notifications-header"], [1, "header-top"], [1, "header-title"], [1, "title-icon"], [1, "fa-solid", "fa-bell"], [1, "title-text"], [1, "title-subtitle"], [1, "header-actions"], [1, "filters-section"], [1, "notifications-content"], ["Variant", "success", "Icon", "fa-solid fa-bell-slash", "Title", "No Notifications", "Message", "You're all caught up! New notifications will appear here."], ["Variant", "no-results", "Icon", "fa-solid fa-filter-circle-xmark", "Title", "No Matching Notifications", "Message", "Try adjusting your filters to see more notifications."], [1, "notifications-list"], ["type", "button", "aria-label", "Mark all notifications as read", 1, "action-btn", "action-btn-ghost"], ["type", "button", "aria-label", "Mark all notifications as unread", 1, "action-btn", "action-btn-secondary"], ["type", "button", "aria-label", "Mark all notifications as read", 1, "action-btn", "action-btn-ghost", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-check-double"], [1, "btn-text"], ["type", "button", "aria-label", "Mark all notifications as unread", 1, "action-btn", "action-btn-secondary", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-envelope"], [1, "filters-slider"], [1, "slider-content"], ["role", "group", "aria-label", "Filter notifications by read status", 1, "header-stats"], ["type", "button", "aria-label", "Show all notifications", 1, "stat-pill", 3, "click"], [1, "stat-label"], [1, "stat-count"], ["type", "button", "aria-label", "Show unread notifications", 1, "stat-pill", 3, "active", "has-unread"], ["type", "button", "aria-label", "Show read notifications", 1, "stat-pill", 3, "active"], [1, "filter-item", "type-filter-slider"], [1, "search-section"], [1, "search-container"], ["aria-hidden", "true", 1, "fa-solid", "fa-magnifying-glass", "search-icon"], ["type", "text", "placeholder", "Search notifications...", "aria-label", "Search notifications", 1, "search-input", 3, "input", "value"], [1, "filter-info"], ["type", "button", "aria-label", "Show unread notifications", 1, "stat-pill", 3, "click"], ["type", "button", "aria-label", "Show read notifications", 1, "stat-pill", 3, "click"], ["aria-label", "Filter by notification type", 1, "type-select", 3, "ngModelChange", "change", "ngModel"], [3, "value"], [1, "filter-badge"], ["aria-hidden", "true", 1, "fa-solid", "fa-filter"], [1, "group-label"], ["role", "article", 1, "notification-card", 3, "unread", "expanded", "clickable"], ["role", "article", 1, "notification-card"], ["aria-hidden", "true", 1, "card-indicator"], ["aria-hidden", "true", 1, "card-icon"], [1, "card-content", 3, "click"], [1, "card-header"], [1, "card-title"], [1, "card-time", 3, "title"], [1, "card-meta"], [1, "card-type"], [1, "card-open-hint"], [1, "card-message"], [1, "card-rich"], ["type", "button", 1, "card-expand-btn"], [1, "card-actions"], ["type", "button", "title", "Mark as Read", 1, "card-action-btn"], ["type", "button", "title", "Mark as Unread", 1, "card-action-btn"], ["aria-hidden", "true", 1, "fa-solid", "fa-arrow-up-right-from-square"], [1, "card-rich", 3, "click"], [1, "card-rich-html", 3, "innerHTML"], [1, "card-rich-md", 3, "data", "enableMermaid"], [1, "card-message", "card-message-full"], ["type", "button", 1, "card-expand-btn", 3, "click"], ["aria-hidden", "true", 1, "fa-solid"], ["type", "button", "title", "Mark as Read", 1, "card-action-btn", 3, "click"], ["aria-hidden", "true", 1, "fa-solid", "fa-check"], [1, "action-text"], ["type", "button", "title", "Mark as Unread", 1, "card-action-btn", 3, "click"]], template: function UserNotificationsComponent_Template(rf, ctx) { if (rf & 1) {
890
+ i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "div", 3)(4, "div", 4);
891
+ i0.ɵɵelement(5, "i", 5);
892
+ i0.ɵɵelementEnd();
893
+ i0.ɵɵelementStart(6, "div", 6)(7, "h1");
894
+ i0.ɵɵtext(8, "Notifications");
692
895
  i0.ɵɵelementEnd();
693
- i0.ɵɵelementStart(6, "h1");
694
- i0.ɵɵtext(7, "Notifications");
896
+ i0.ɵɵconditionalCreate(9, UserNotificationsComponent_Conditional_9_Template, 2, 1, "span", 7)(10, UserNotificationsComponent_Conditional_10_Template, 2, 0, "span", 7);
695
897
  i0.ɵɵelementEnd()();
696
- i0.ɵɵconditionalCreate(8, UserNotificationsComponent_Conditional_8_Template, 3, 2, "div", 9);
898
+ i0.ɵɵconditionalCreate(11, UserNotificationsComponent_Conditional_11_Template, 3, 2, "div", 8);
697
899
  i0.ɵɵelementEnd();
698
- i0.ɵɵconditionalCreate(9, UserNotificationsComponent_Conditional_9_Template, 23, 9, "div", 10);
900
+ i0.ɵɵconditionalCreate(12, UserNotificationsComponent_Conditional_12_Template, 17, 9, "div", 9);
699
901
  i0.ɵɵelementEnd();
700
- i0.ɵɵelementStart(10, "div", 11);
701
- i0.ɵɵconditionalCreate(11, UserNotificationsComponent_Conditional_11_Template, 1, 0, "mj-empty-state", 12)(12, UserNotificationsComponent_Conditional_12_Template, 1, 0, "mj-empty-state", 13)(13, UserNotificationsComponent_Conditional_13_Template, 3, 0, "div", 14);
902
+ i0.ɵɵelementStart(13, "div", 10);
903
+ i0.ɵɵconditionalCreate(14, UserNotificationsComponent_Conditional_14_Template, 1, 0, "mj-empty-state", 11)(15, UserNotificationsComponent_Conditional_15_Template, 1, 0, "mj-empty-state", 12)(16, UserNotificationsComponent_Conditional_16_Template, 3, 0, "div", 13);
702
904
  i0.ɵɵelementEnd()();
703
905
  } if (rf & 2) {
704
- i0.ɵɵadvance(8);
705
- i0.ɵɵconditional(ctx.AllNotifications.length > 0 ? 8 : -1);
906
+ i0.ɵɵadvance(9);
907
+ i0.ɵɵconditional(ctx.UnreadNotifications.length > 0 ? 9 : ctx.AllNotifications.length > 0 ? 10 : -1);
908
+ i0.ɵɵadvance(2);
909
+ i0.ɵɵconditional(ctx.AllNotifications.length > 0 ? 11 : -1);
706
910
  i0.ɵɵadvance();
707
- i0.ɵɵconditional(ctx.AllNotifications.length > 0 ? 9 : -1);
911
+ i0.ɵɵconditional(ctx.AllNotifications.length > 0 ? 12 : -1);
708
912
  i0.ɵɵadvance(2);
709
- i0.ɵɵconditional(ctx.NotificationsToShow.length === 0 && ctx.AllNotifications.length === 0 ? 11 : ctx.NotificationsToShow.length === 0 ? 12 : 13);
710
- } }, dependencies: [i3.NgSelectOption, i3.ɵNgSelectMultipleOption, i3.SelectControlValueAccessor, i3.NgControlStatus, i3.NgModel, i4.MJEmptyStateComponent, i5.DatePipe], styles: ["\n\n\n\n\n\n[_nghost-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n\n\n\n\n\n.notifications-container[_ngcontent-%COMP%] {\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface-card);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n}\n\n\n\n\n\n\n.notifications-header[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 0;\n flex-shrink: 0;\n}\n\n\n\n.header-top[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 24px 24px 16px;\n gap: 16px;\n}\n\n.header-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.title-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n background: var(--mj-brand-primary);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.title-icon[_ngcontent-%COMP%]:hover {\n transform: scale(1.05);\n}\n\n.title-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 22px;\n color: white;\n}\n\n.header-title[_ngcontent-%COMP%] h1[_ngcontent-%COMP%] {\n font-size: 1.75rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n letter-spacing: -0.02em;\n}\n\n\n\n\n\n\n.header-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.action-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n border-radius: 9999px; \n\n font-size: 14px;\n font-weight: 600;\n border: 2px solid var(--mj-brand-primary);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n font-family: inherit;\n}\n\n.action-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n transition: transform 0.2s ease;\n}\n\n.action-btn[_ngcontent-%COMP%]:hover i[_ngcontent-%COMP%] {\n transform: scale(1.1);\n}\n\n\n\n.action-btn-ghost[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.action-btn-ghost[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n color: white;\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-md);\n}\n\n.action-btn-ghost[_ngcontent-%COMP%]:active {\n background: var(--mj-brand-primary);\n transform: scale(0.98);\n}\n\n\n\n.action-btn-secondary[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.action-btn-secondary[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n color: white;\n border-color: var(--mj-brand-primary);\n}\n\n.action-btn-secondary[_ngcontent-%COMP%]:active {\n transform: scale(0.98);\n}\n\n\n\n\n\n\n.filters-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.filters-slider[_ngcontent-%COMP%] {\n overflow-x: auto;\n overflow-y: hidden;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n\n.filters-slider[_ngcontent-%COMP%]::-webkit-scrollbar {\n display: none;\n}\n\n.slider-content[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 24px;\n min-width: min-content;\n}\n\n\n\n\n\n\n.header-stats[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-shrink: 0;\n}\n\n.hidden-radio[_ngcontent-%COMP%] {\n position: absolute;\n opacity: 0;\n pointer-events: none;\n}\n\n.stat-pill[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n border-radius: 9999px; \n\n border: 2px solid var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-family: inherit;\n min-height: 44px;\n}\n\n.stat-pill[_ngcontent-%COMP%]:hover:not(.active) {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-brand-primary);\n transform: translateY(-1px);\n}\n\n\n\n.stat-pill.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: white;\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n transform: translateY(-1px);\n}\n\n.stat-label[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n}\n\n.stat-count[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 700;\n padding: 4px 10px;\n border-radius: 9999px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n min-width: 28px;\n text-align: center;\n transition: all 0.2s ease;\n}\n\n.stat-pill.active[_ngcontent-%COMP%] .stat-count[_ngcontent-%COMP%] {\n background: rgba(255, 255, 255, 0.25);\n color: white;\n}\n\n.stat-count.unread-count[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 700;\n}\n\n.stat-pill.has-unread[_ngcontent-%COMP%]:not(.active) {\n border-color: var(--mj-brand-primary);\n}\n\n\n\n\n\n\n.type-filter-slider[_ngcontent-%COMP%] {\n flex-shrink: 0;\n min-width: 180px;\n}\n\n\n\n\n\n\n.search-section[_ngcontent-%COMP%] {\n padding: 0 24px 16px;\n}\n\n\n\n\n\n\n.filter-info[_ngcontent-%COMP%] {\n padding: 0 24px 16px;\n display: flex;\n justify-content: center;\n}\n\n\n\n.type-select[_ngcontent-%COMP%] {\n width: 100%;\n padding: 10px 16px;\n border: 2px solid var(--mj-brand-primary);\n border-radius: 9999px; \n\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-family: inherit;\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%230076B6' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 16px center;\n padding-right: 42px;\n}\n\n.type-select[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background-color: var(--mj-bg-surface-card);\n}\n\n.type-select[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.search-container[_ngcontent-%COMP%] {\n position: relative;\n width: 100%;\n max-width: 100%;\n}\n\n.search-icon[_ngcontent-%COMP%] {\n position: absolute;\n left: 16px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--mj-text-secondary);\n font-size: 14px;\n pointer-events: none;\n transition: color 0.2s ease;\n}\n\n.search-input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 10px 16px 10px 44px;\n border: 2px solid var(--mj-brand-primary);\n border-radius: 9999px; \n\n font-size: 14px;\n font-weight: 500;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-family: inherit;\n}\n\n.search-input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-secondary);\n}\n\n.search-input[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface-card);\n}\n\n.search-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n background: var(--mj-bg-surface);\n}\n\n.search-container[_ngcontent-%COMP%]:focus-within .search-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.filter-badge[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-radius: 9999px; \n\n font-size: 13px;\n font-weight: 600;\n border: 1px solid var(--mj-brand-primary);\n}\n\n.filter-badge[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n\n\n\n\n\n.notifications-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch; \n\n}\n\n\n\n.notifications-content[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 8px;\n}\n\n.notifications-content[_ngcontent-%COMP%]::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-card);\n}\n\n.notifications-content[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background: var(--mj-border-default);\n border-radius: 4px;\n}\n\n.notifications-content[_ngcontent-%COMP%]::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-muted);\n}\n\n\n\n\n\n\n.notifications-list[_ngcontent-%COMP%] {\n background: transparent;\n}\n\n\n\n\n\n\n.notification-card[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 4px 56px 1fr auto;\n gap: 14px;\n align-items: start;\n padding: 16px 20px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n position: relative;\n}\n\n.notification-card[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.notification-card[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.notification-card.clickable[_ngcontent-%COMP%] {\n cursor: pointer;\n}\n\n.notification-card.clickable[_ngcontent-%COMP%]:active {\n background: var(--mj-bg-surface-sunken);\n transform: scale(0.995);\n}\n\n\n\n.notification-card.unread[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.notification-card.unread[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-card));\n}\n\n\n\n.card-indicator[_ngcontent-%COMP%] {\n width: 4px;\n height: 100%;\n min-height: 64px;\n border-radius: 2px;\n background: transparent;\n transition: all 0.2s ease;\n align-self: stretch;\n}\n\n.notification-card.unread[_ngcontent-%COMP%] .card-indicator[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n box-shadow: 0 0 8px color-mix(in srgb, var(--mj-brand-primary) 50%, transparent);\n}\n\n\n\n.card-icon[_ngcontent-%COMP%] {\n width: 56px;\n height: 56px;\n border-radius: 14px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition: transform 0.2s ease;\n}\n\n.notification-card[_ngcontent-%COMP%]:hover .card-icon[_ngcontent-%COMP%] {\n transform: scale(1.05);\n}\n\n.card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n}\n\n\n\n.card-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.card-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 6px;\n}\n\n.card-title[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n margin: 0;\n line-height: 1.4;\n word-wrap: break-word;\n}\n\n.card-title-unread[_ngcontent-%COMP%] {\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.notification-card.clickable[_ngcontent-%COMP%] .card-title[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.notification-card.clickable[_ngcontent-%COMP%]:hover .card-title[_ngcontent-%COMP%] {\n text-decoration: underline;\n}\n\n.card-time[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.card-time[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n\n\n.card-meta[_ngcontent-%COMP%] {\n margin-bottom: 8px;\n}\n\n.card-type[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 4px 12px;\n border-radius: 9999px; \n\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n\n\n.card-message[_ngcontent-%COMP%] {\n font-size: 15px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n margin: 0;\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n\n\n.card-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding-top: 6px;\n}\n\n.card-action-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border-radius: 9999px; \n\n font-size: 13px;\n font-weight: 600;\n border: 2px solid var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n white-space: nowrap;\n font-family: inherit;\n}\n\n.card-action-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: white;\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-md);\n}\n\n.card-action-btn[_ngcontent-%COMP%]:active {\n transform: translateY(0) scale(0.98);\n}\n\n.card-action-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 13px;\n}\n\n\n\n\n\n\n@keyframes _ngcontent-%COMP%_slideInUp {\n from {\n opacity: 0;\n transform: translateY(12px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.notification-card[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_slideInUp 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n\n\n\n\n\nbutton[_ngcontent-%COMP%]:focus-visible, \ninput[_ngcontent-%COMP%]:focus-visible, \nselect[_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n\n\n@media (prefers-reduced-motion: reduce) {\n *[_ngcontent-%COMP%], \n *[_ngcontent-%COMP%]::before, \n *[_ngcontent-%COMP%]::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n\n\n\n\n\n\n\n\n@media (max-width: 768px) {\n .notifications-container[_ngcontent-%COMP%] {\n border-radius: 0;\n box-shadow: none;\n }\n\n .header-top[_ngcontent-%COMP%] {\n padding: 20px 20px 14px;\n }\n\n .title-icon[_ngcontent-%COMP%] {\n width: 44px;\n height: 44px;\n }\n\n .title-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n }\n\n .header-title[_ngcontent-%COMP%] h1[_ngcontent-%COMP%] {\n font-size: 1.5rem;\n }\n\n \n\n .slider-content[_ngcontent-%COMP%] {\n padding: 14px 20px;\n gap: 10px;\n }\n\n .stat-pill[_ngcontent-%COMP%] {\n padding: 8px 16px;\n flex-shrink: 0;\n }\n\n .type-filter-slider[_ngcontent-%COMP%] {\n min-width: 160px;\n }\n\n .search-section[_ngcontent-%COMP%] {\n padding: 0 20px 14px;\n }\n\n .filter-info[_ngcontent-%COMP%] {\n padding: 0 20px 14px;\n }\n\n .notification-card[_ngcontent-%COMP%] {\n grid-template-columns: 4px 52px 1fr;\n gap: 12px;\n padding: 14px 16px;\n }\n\n .card-actions[_ngcontent-%COMP%] {\n grid-column: 2 / -1;\n padding-top: 10px;\n }\n\n .card-icon[_ngcontent-%COMP%] {\n width: 52px;\n height: 52px;\n }\n\n .card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 22px;\n }\n}\n\n\n\n@media (max-width: 480px) {\n .header-top[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n gap: 12px;\n padding: 16px 16px 12px;\n }\n\n .header-title[_ngcontent-%COMP%] {\n justify-content: flex-start;\n }\n\n .title-icon[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n }\n\n .title-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 18px;\n }\n\n .header-title[_ngcontent-%COMP%] h1[_ngcontent-%COMP%] {\n font-size: 1.375rem;\n }\n\n .header-actions[_ngcontent-%COMP%] {\n width: 100%;\n justify-content: stretch;\n gap: 8px;\n }\n\n .action-btn[_ngcontent-%COMP%] {\n flex: 1;\n justify-content: center;\n padding: 10px 16px;\n font-size: 13px;\n }\n\n .btn-text[_ngcontent-%COMP%] {\n display: none;\n }\n\n \n\n .slider-content[_ngcontent-%COMP%] {\n padding: 12px 16px;\n gap: 8px;\n }\n\n .header-stats[_ngcontent-%COMP%] {\n gap: 8px;\n }\n\n .stat-pill[_ngcontent-%COMP%] {\n padding: 8px 14px;\n min-height: 40px;\n flex-shrink: 0;\n }\n\n .stat-label[_ngcontent-%COMP%] {\n font-size: 13px;\n }\n\n .stat-count[_ngcontent-%COMP%] {\n font-size: 12px;\n padding: 3px 8px;\n min-width: 24px;\n }\n\n .type-filter-slider[_ngcontent-%COMP%] {\n min-width: 140px;\n }\n\n .type-select[_ngcontent-%COMP%], \n .search-input[_ngcontent-%COMP%] {\n font-size: 16px; \n\n }\n\n .search-section[_ngcontent-%COMP%] {\n padding: 0 16px 12px;\n }\n\n .filter-info[_ngcontent-%COMP%] {\n padding: 0 16px 12px;\n }\n\n .notification-card[_ngcontent-%COMP%] {\n grid-template-columns: 3px 48px 1fr;\n gap: 10px;\n padding: 12px 12px;\n }\n\n .card-indicator[_ngcontent-%COMP%] {\n width: 3px;\n min-height: 56px;\n }\n\n .card-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n }\n\n .card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 20px;\n }\n\n .card-title[_ngcontent-%COMP%] {\n font-size: 15px;\n }\n\n .card-time[_ngcontent-%COMP%] {\n font-size: 12px;\n }\n\n .card-message[_ngcontent-%COMP%] {\n font-size: 14px;\n -webkit-line-clamp: 2;\n }\n\n .card-type[_ngcontent-%COMP%] {\n font-size: 11px;\n padding: 3px 10px;\n }\n\n .card-actions[_ngcontent-%COMP%] {\n padding-top: 8px;\n gap: 6px;\n }\n\n .action-text[_ngcontent-%COMP%] {\n display: none;\n }\n\n .card-action-btn[_ngcontent-%COMP%] {\n padding: 8px 12px;\n min-width: 40px;\n justify-content: center;\n }\n\n}\n\n\n\n@media (min-width: 390px) and (max-width: 480px) {\n .btn-text[_ngcontent-%COMP%] {\n display: inline;\n }\n\n .action-btn[_ngcontent-%COMP%] {\n font-size: 14px;\n }\n}\n\n\n\n@media (prefers-color-scheme: dark) {\n \n\n}"] });
913
+ i0.ɵɵconditional(ctx.NotificationsToShow.length === 0 && ctx.AllNotifications.length === 0 ? 14 : ctx.NotificationsToShow.length === 0 ? 15 : 16);
914
+ } }, dependencies: [i3.MarkdownComponent, i4.NgSelectOption, i4.ɵNgSelectMultipleOption, i4.SelectControlValueAccessor, i4.NgControlStatus, i4.NgModel, i5.MJEmptyStateComponent, i6.DatePipe], styles: ["\n\n\n\n\n\n[_nghost-%COMP%] {\n display: block;\n height: 100%;\n}\n\n.notifications-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-page);\n}\n\n\n\n\n.notifications-header[_ngcontent-%COMP%] {\n flex-shrink: 0;\n padding: 20px 24px 12px 24px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.header-top[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n margin-bottom: 14px;\n}\n\n.header-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n}\n\n.title-icon[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: var(--mj-radius-lg, 12px);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.title-text[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.title-text[_ngcontent-%COMP%] h1[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 20px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.title-subtitle[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.header-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.action-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 12px;\n border-radius: var(--mj-radius-md, 8px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.15s, color 0.15s, border-color 0.15s;\n}\n\n.action-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.action-btn-ghost[_ngcontent-%COMP%] {\n border-color: color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.action-btn-ghost[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n\n\n\n.filters-section[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 10px;\n}\n\n.filters-slider[_ngcontent-%COMP%] {\n min-width: 0;\n}\n\n.slider-content[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.header-stats[_ngcontent-%COMP%] {\n display: inline-flex;\n gap: 4px;\n padding: 3px;\n border-radius: var(--mj-radius-full, 999px);\n background: var(--mj-bg-surface-sunken);\n}\n\n.stat-pill[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n border: none;\n border-radius: var(--mj-radius-full, 999px);\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.15s, color 0.15s;\n}\n\n.stat-pill[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-primary);\n}\n\n.stat-pill.active[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.stat-count[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: var(--mj-radius-full, 999px);\n background: color-mix(in srgb, var(--mj-text-muted) 14%, transparent);\n color: var(--mj-text-secondary);\n font-size: 10px;\n font-weight: 700;\n}\n\n.stat-count.unread-count[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.type-select[_ngcontent-%COMP%] {\n padding: 7px 10px;\n border-radius: var(--mj-radius-md, 8px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.search-section[_ngcontent-%COMP%] {\n flex: 1 1 220px;\n min-width: 180px;\n}\n\n.search-container[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.search-icon[_ngcontent-%COMP%] {\n position: absolute;\n left: 12px;\n color: var(--mj-text-muted);\n font-size: 12px;\n pointer-events: none;\n}\n\n.search-input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 8px 12px 8px 32px;\n border-radius: var(--mj-radius-md, 8px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 13px;\n}\n\n.search-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n}\n\n.filter-info[_ngcontent-%COMP%] {\n flex-basis: 100%;\n}\n\n.filter-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: var(--mj-radius-full, 999px);\n background: color-mix(in srgb, var(--mj-status-info) 10%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n font-size: 11px;\n font-weight: 600;\n}\n\n\n\n\n.notifications-content[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n padding: 16px 24px 32px 24px;\n}\n\n.notifications-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 10px;\n max-width: 960px;\n margin: 0 auto;\n}\n\n.group-label[_ngcontent-%COMP%] {\n position: sticky;\n top: -16px;\n z-index: 1;\n padding: 10px 2px 4px 2px;\n background: var(--mj-bg-page);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--mj-text-muted);\n}\n\n.group-label[_ngcontent-%COMP%]:not(:first-child) {\n margin-top: 8px;\n}\n\n\n\n\n.notification-card[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n gap: 12px;\n padding: 14px 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n background: var(--mj-bg-surface);\n transition: border-color 0.15s, box-shadow 0.15s, background-color 0.15s;\n overflow: hidden;\n}\n\n.notification-card[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-border-strong);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.notification-card.unread[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 3%, var(--mj-bg-surface));\n}\n\n.card-indicator[_ngcontent-%COMP%] {\n display: none;\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 3px;\n background: var(--mj-brand-primary);\n border-radius: 0 2px 2px 0;\n}\n\n.notification-card.unread[_ngcontent-%COMP%] .card-indicator[_ngcontent-%COMP%] {\n display: block;\n}\n\n.card-icon[_ngcontent-%COMP%] {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border-radius: var(--mj-radius-md, 10px);\n background: var(--mj-bg-surface-sunken);\n font-size: 14px;\n}\n\n.card-content[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.notification-card.clickable[_ngcontent-%COMP%] .card-content[_ngcontent-%COMP%] {\n cursor: pointer;\n}\n\n.card-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n}\n\n.card-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow-wrap: anywhere;\n}\n\n.card-title-unread[_ngcontent-%COMP%] {\n font-weight: 700;\n}\n\n.card-time[_ngcontent-%COMP%] {\n flex-shrink: 0;\n font-size: 11px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n.card-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 3px 0 6px 0;\n}\n\n.card-type[_ngcontent-%COMP%] {\n display: inline-flex;\n padding: 2px 8px;\n border-radius: var(--mj-radius-full, 999px);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n\n.card-open-hint[_ngcontent-%COMP%] {\n display: none;\n align-items: center;\n gap: 4px;\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.notification-card.clickable[_ngcontent-%COMP%]:hover .card-open-hint[_ngcontent-%COMP%] {\n display: inline-flex;\n}\n\n.card-message[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--mj-text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n overflow-wrap: anywhere;\n}\n\n.card-message-full[_ngcontent-%COMP%] {\n display: block;\n -webkit-line-clamp: unset;\n white-space: pre-wrap;\n}\n\n\n\n\n.card-rich[_ngcontent-%COMP%] {\n margin-top: 4px;\n padding: 12px 14px;\n border: 1px solid var(--mj-border-subtle);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-card);\n cursor: default;\n}\n\n\n\n.card-rich-html[_ngcontent-%COMP%] {\n font-size: 13px;\n line-height: 1.55;\n color: var(--mj-text-primary);\n overflow-x: auto;\n}\n\n.card-rich-html[_ngcontent-%COMP%] img[_ngcontent-%COMP%] {\n max-width: 100%;\n height: auto;\n}\n\n.card-rich-html[_ngcontent-%COMP%] table[_ngcontent-%COMP%] {\n max-width: 100%;\n border-collapse: collapse;\n}\n\n.card-rich-html[_ngcontent-%COMP%] td[_ngcontent-%COMP%], \n.card-rich-html[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n padding: 4px 8px;\n border: 1px solid var(--mj-border-subtle);\n}\n\n.card-rich-html[_ngcontent-%COMP%] a[_ngcontent-%COMP%] {\n color: var(--mj-text-link);\n}\n\n.card-rich-html[_ngcontent-%COMP%] h1[_ngcontent-%COMP%], \n.card-rich-html[_ngcontent-%COMP%] h2[_ngcontent-%COMP%], \n.card-rich-html[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0.4em 0 0.3em 0;\n font-size: 1.05em;\n}\n\n.card-rich-md[_ngcontent-%COMP%] {\n display: block;\n font-size: 13px;\n}\n\n.card-expand-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n padding: 4px 10px;\n border: none;\n border-radius: var(--mj-radius-full, 999px);\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 11px;\n font-weight: 700;\n cursor: pointer;\n}\n\n.card-expand-btn[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n\n\n\n.card-actions[_ngcontent-%COMP%] {\n flex-shrink: 0;\n display: flex;\n align-items: flex-start;\n}\n\n.card-action-btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border: 1px solid transparent;\n border-radius: var(--mj-radius-md, 8px);\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 11px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.15s, color 0.15s;\n}\n\n.card-action-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-default);\n}\n\n\n\n\n@media (max-width: 720px) {\n .notifications-header[_ngcontent-%COMP%] {\n padding: 14px 14px 10px 14px;\n }\n\n .notifications-content[_ngcontent-%COMP%] {\n padding: 12px 12px 24px 12px;\n }\n\n .btn-text[_ngcontent-%COMP%], \n .action-text[_ngcontent-%COMP%] {\n display: none;\n }\n\n .card-header[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 2px;\n }\n\n .card-time[_ngcontent-%COMP%] {\n order: -1;\n }\n}"] });
711
915
  }
712
916
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UserNotificationsComponent, [{
713
917
  type: Component,
714
- args: [{ standalone: false, selector: 'app-user-notifications', template: "<div class=\"notifications-container\">\n <!-- Header Section -->\n <div class=\"notifications-header\">\n <div class=\"header-top\">\n <div class=\"header-title\">\n <div class=\"title-icon\">\n <i class=\"fa-solid fa-bell\"></i>\n </div>\n <h1>Notifications</h1>\n </div>\n\n @if (AllNotifications.length > 0) {\n <div class=\"header-actions\">\n @if (UnreadNotifications.length > 0) {\n <button class=\"action-btn action-btn-ghost\"\n type=\"button\"\n (click)=\"markAllAsRead()\"\n aria-label=\"Mark all notifications as read\">\n <i class=\"fa-solid fa-check-double\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Mark All Read</span>\n </button>\n }\n @if (ReadNotifications.length > 0) {\n <button class=\"action-btn action-btn-secondary\"\n type=\"button\"\n (click)=\"markAllAsUnread()\"\n aria-label=\"Mark all notifications as unread\">\n <i class=\"fa-solid fa-envelope\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Mark All Unread</span>\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Filters Section -->\n @if (AllNotifications.length > 0) {\n <div class=\"filters-section\">\n <!-- Horizontal Slider for Filters (Mobile) -->\n <div class=\"filters-slider\">\n <div class=\"slider-content\">\n <!-- Stats Pills (Filter Tabs) -->\n <div class=\"header-stats\" role=\"group\" aria-label=\"Filter notifications by read status\">\n <input #allRadio type=\"radio\" name=\"UnreadStatus\" value=\"All\"\n (change)=\"onReadRadioChanged($event)\" checked class=\"hidden-radio\"\n id=\"filter-all\" />\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'All'\"\n (click)=\"selectReadOption('All')\"\n [attr.aria-pressed]=\"radioSelected === 'All'\"\n aria-label=\"Show all notifications\">\n <span class=\"stat-label\">All</span>\n <span class=\"stat-count\">{{AllNotifications.length}}</span>\n </button>\n\n <input #unreadRadio type=\"radio\" name=\"UnreadStatus\" value=\"Unread\"\n (change)=\"onReadRadioChanged($event)\" class=\"hidden-radio\"\n id=\"filter-unread\" />\n @if (UnreadNotifications.length > 0 || ReadNotifications.length === 0) {\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'Unread'\"\n [class.has-unread]=\"UnreadNotifications.length > 0\"\n (click)=\"selectReadOption('Unread')\"\n [attr.aria-pressed]=\"radioSelected === 'Unread'\"\n aria-label=\"Show unread notifications\">\n <span class=\"stat-label\">Unread</span>\n <span class=\"stat-count\" [class.unread-count]=\"UnreadNotifications.length > 0\">\n {{UnreadNotifications.length}}\n </span>\n </button>\n }\n\n <input #readRadio type=\"radio\" name=\"UnreadStatus\" value=\"Read\"\n (change)=\"onReadRadioChanged($event)\" class=\"hidden-radio\"\n id=\"filter-read\" />\n @if (ReadNotifications.length > 0 || UnreadNotifications.length === 0) {\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'Read'\"\n (click)=\"selectReadOption('Read')\"\n [attr.aria-pressed]=\"radioSelected === 'Read'\"\n aria-label=\"Show read notifications\">\n <span class=\"stat-label\">Read</span>\n <span class=\"stat-count\">{{ReadNotifications.length}}</span>\n </button>\n }\n </div>\n\n <!-- Native Type Filter Dropdown (in slider on mobile) -->\n @if (notificationTypes.length > 0) {\n <div class=\"filter-item type-filter-slider\">\n <select class=\"type-select\"\n [(ngModel)]=\"selectedTypeFilter\"\n (change)=\"onTypeFilterChange(selectedTypeFilter)\"\n aria-label=\"Filter by notification type\">\n <option [value]=\"null\">All Types</option>\n @for (type of notificationTypes; track type.ID) {\n <option [value]=\"type.ID\">{{type.Name}}</option>\n }\n </select>\n </div>\n }\n </div>\n </div>\n\n <!-- Search Filter (separate, full-width on mobile) -->\n <div class=\"search-section\">\n <div class=\"search-container\">\n <i class=\"fa-solid fa-magnifying-glass search-icon\" aria-hidden=\"true\"></i>\n <input type=\"text\"\n class=\"search-input\"\n placeholder=\"Search notifications...\"\n [value]=\"currentFilter\"\n (input)=\"onFilterChanged($any($event.target).value)\"\n aria-label=\"Search notifications\" />\n </div>\n </div>\n\n <!-- Filter Info Badge -->\n @if (NotificationsToShow.length !== AllNotifications.length) {\n <div class=\"filter-info\">\n <span class=\"filter-badge\">\n <i class=\"fa-solid fa-filter\" aria-hidden=\"true\"></i>\n Showing {{NotificationsToShow.length}} of {{AllNotifications.length}}\n </span>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Notifications Content -->\n <div class=\"notifications-content\">\n @if (NotificationsToShow.length === 0 && AllNotifications.length === 0) {\n <mj-empty-state\n Variant=\"success\"\n Icon=\"fa-solid fa-bell-slash\"\n Title=\"No Notifications\"\n Message=\"You're all caught up! New notifications will appear here.\" />\n } @else if (NotificationsToShow.length === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Icon=\"fa-solid fa-filter-circle-xmark\"\n Title=\"No Matching Notifications\"\n Message=\"Try adjusting your filters to see more notifications.\" />\n } @else {\n <!-- Notifications List -->\n <div class=\"notifications-list\">\n @for (notification of NotificationsToShow; track notification.ID) {\n <article class=\"notification-card\"\n [class.unread]=\"notification.Unread\"\n [class.clickable]=\"isNotificationClickable(notification)\"\n role=\"article\"\n [attr.aria-label]=\"notification.Title\">\n\n <!-- Unread Indicator Bar -->\n <div class=\"card-indicator\" aria-hidden=\"true\"></div>\n\n <!-- Type Icon -->\n <div class=\"card-icon\"\n [style.background-color]=\"getTypeColor(notification) + '15'\"\n aria-hidden=\"true\">\n <i [class]=\"'fa-solid ' + getTypeIcon(notification)\"\n [style.color]=\"getTypeColor(notification)\"></i>\n </div>\n\n <!-- Content -->\n <div class=\"card-content\" (click)=\"notificationClicked(notification)\">\n <div class=\"card-header\">\n <h3 class=\"card-title\" [class.card-title-unread]=\"notification.Unread\">\n {{notification.Title}}\n </h3>\n <span class=\"card-time\">\n <i class=\"fa-regular fa-clock\" aria-hidden=\"true\"></i>\n <time [attr.datetime]=\"notification.__mj_CreatedAt\">\n {{notification.__mj_CreatedAt | date:'MMM d, h:mm a'}}\n </time>\n </span>\n </div>\n\n <div class=\"card-meta\">\n <span class=\"card-type\"\n [style.background-color]=\"getTypeColor(notification) + '15'\"\n [style.color]=\"getTypeColor(notification)\">\n {{getTypeName(notification)}}\n </span>\n </div>\n\n <p class=\"card-message\">{{notification.Message}}</p>\n </div>\n\n <!-- Actions -->\n <div class=\"card-actions\">\n @if (notification.Unread) {\n <button class=\"card-action-btn\"\n type=\"button\"\n (click)=\"markAsRead(notification, true, null)\"\n [attr.aria-label]=\"'Mark ' + notification.Title + ' as read'\"\n title=\"Mark as Read\">\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n <span class=\"action-text\">Read</span>\n </button>\n } @else {\n <button class=\"card-action-btn\"\n type=\"button\"\n (click)=\"markAsRead(notification, false, null)\"\n [attr.aria-label]=\"'Mark ' + notification.Title + ' as unread'\"\n title=\"Mark as Unread\">\n <i class=\"fa-solid fa-envelope\" aria-hidden=\"true\"></i>\n <span class=\"action-text\">Unread</span>\n </button>\n }\n </div>\n </article>\n }\n </div>\n }\n </div>\n</div>\n", styles: ["/* ===================================\n USER NOTIFICATIONS - MD3 DESIGN\n iPhone-inspired UX with Circular Buttons\n =================================== */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n}\n\n/* ===================================\n CONTAINER\n =================================== */\n\n.notifications-container {\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface-card);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n}\n\n/* ===================================\n HEADER SECTION\n =================================== */\n\n.notifications-header {\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n padding: 0;\n flex-shrink: 0;\n}\n\n/* Header Top Row */\n.header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 24px 24px 16px;\n gap: 16px;\n}\n\n.header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.title-icon {\n width: 48px;\n height: 48px;\n background: var(--mj-brand-primary);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.title-icon:hover {\n transform: scale(1.05);\n}\n\n.title-icon i {\n font-size: 22px;\n color: white;\n}\n\n.header-title h1 {\n font-size: 1.75rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0;\n letter-spacing: -0.02em;\n}\n\n/* ===================================\n HEADER ACTIONS - Circular Buttons\n =================================== */\n\n.header-actions {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.action-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n border-radius: 9999px; /* Circular design */\n font-size: 14px;\n font-weight: 600;\n border: 2px solid var(--mj-brand-primary);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n min-height: 44px;\n font-family: inherit;\n}\n\n.action-btn i {\n font-size: 16px;\n transition: transform 0.2s ease;\n}\n\n.action-btn:hover i {\n transform: scale(1.1);\n}\n\n/* Ghost Button - White Background (Mark All Read) */\n.action-btn-ghost {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.action-btn-ghost:hover {\n background: var(--mj-brand-primary);\n color: white;\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-md);\n}\n\n.action-btn-ghost:active {\n background: var(--mj-brand-primary);\n transform: scale(0.98);\n}\n\n/* Secondary Button */\n.action-btn-secondary {\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.action-btn-secondary:hover {\n background: var(--mj-brand-primary);\n color: white;\n border-color: var(--mj-brand-primary);\n}\n\n.action-btn-secondary:active {\n transform: scale(0.98);\n}\n\n/* ===================================\n FILTERS SECTION\n =================================== */\n\n.filters-section {\n display: flex;\n flex-direction: column;\n gap: 12px;\n background: var(--mj-bg-surface-card);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.filters-slider {\n overflow-x: auto;\n overflow-y: hidden;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n\n.filters-slider::-webkit-scrollbar {\n display: none;\n}\n\n.slider-content {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 24px;\n min-width: min-content;\n}\n\n/* ===================================\n STATS PILLS (Filter Tabs) - Circular with mj-blue Active\n =================================== */\n\n.header-stats {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-shrink: 0;\n}\n\n.hidden-radio {\n position: absolute;\n opacity: 0;\n pointer-events: none;\n}\n\n.stat-pill {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n border-radius: 9999px; /* Circular design */\n border: 2px solid var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-family: inherit;\n min-height: 44px;\n}\n\n.stat-pill:hover:not(.active) {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-brand-primary);\n transform: translateY(-1px);\n}\n\n/* Active State - MJ Blue */\n.stat-pill.active {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: white;\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n transform: translateY(-1px);\n}\n\n.stat-label {\n font-size: 14px;\n font-weight: 600;\n}\n\n.stat-count {\n font-size: 13px;\n font-weight: 700;\n padding: 4px 10px;\n border-radius: 9999px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n min-width: 28px;\n text-align: center;\n transition: all 0.2s ease;\n}\n\n.stat-pill.active .stat-count {\n background: rgba(255, 255, 255, 0.25);\n color: white;\n}\n\n.stat-count.unread-count {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 700;\n}\n\n.stat-pill.has-unread:not(.active) {\n border-color: var(--mj-brand-primary);\n}\n\n/* ===================================\n TYPE FILTER IN SLIDER\n =================================== */\n\n.type-filter-slider {\n flex-shrink: 0;\n min-width: 180px;\n}\n\n/* ===================================\n SEARCH SECTION\n =================================== */\n\n.search-section {\n padding: 0 24px 16px;\n}\n\n/* ===================================\n FILTER INFO\n =================================== */\n\n.filter-info {\n padding: 0 24px 16px;\n display: flex;\n justify-content: center;\n}\n\n/* Native Select Dropdown Styling */\n.type-select {\n width: 100%;\n padding: 10px 16px;\n border: 2px solid var(--mj-brand-primary);\n border-radius: 9999px; /* Circular design */\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-family: inherit;\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%230076B6' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 16px center;\n padding-right: 42px;\n}\n\n.type-select:hover {\n border-color: var(--mj-brand-primary);\n background-color: var(--mj-bg-surface-card);\n}\n\n.type-select:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.search-container {\n position: relative;\n width: 100%;\n max-width: 100%;\n}\n\n.search-icon {\n position: absolute;\n left: 16px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--mj-text-secondary);\n font-size: 14px;\n pointer-events: none;\n transition: color 0.2s ease;\n}\n\n.search-input {\n width: 100%;\n padding: 10px 16px 10px 44px;\n border: 2px solid var(--mj-brand-primary);\n border-radius: 9999px; /* Circular design */\n font-size: 14px;\n font-weight: 500;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n font-family: inherit;\n}\n\n.search-input::placeholder {\n color: var(--mj-text-secondary);\n}\n\n.search-input:hover {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface-card);\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n background: var(--mj-bg-surface);\n}\n\n.search-container:focus-within .search-icon {\n color: var(--mj-brand-primary);\n}\n\n.filter-badge {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-radius: 9999px; /* Circular design */\n font-size: 13px;\n font-weight: 600;\n border: 1px solid var(--mj-brand-primary);\n}\n\n.filter-badge i {\n font-size: 12px;\n}\n\n/* ===================================\n NOTIFICATIONS CONTENT\n =================================== */\n\n.notifications-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n -webkit-overflow-scrolling: touch; /* iPhone smooth scrolling */\n}\n\n/* Custom Scrollbar */\n.notifications-content::-webkit-scrollbar {\n width: 8px;\n}\n\n.notifications-content::-webkit-scrollbar-track {\n background: var(--mj-bg-surface-card);\n}\n\n.notifications-content::-webkit-scrollbar-thumb {\n background: var(--mj-border-default);\n border-radius: 4px;\n}\n\n.notifications-content::-webkit-scrollbar-thumb:hover {\n background: var(--mj-text-muted);\n}\n\n/* ===================================\n NOTIFICATIONS LIST\n =================================== */\n\n.notifications-list {\n background: transparent;\n}\n\n/* ===================================\n NOTIFICATION CARD - iPhone UX Style\n =================================== */\n\n.notification-card {\n display: grid;\n grid-template-columns: 4px 56px 1fr auto;\n gap: 14px;\n align-items: start;\n padding: 16px 20px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n position: relative;\n}\n\n.notification-card:last-child {\n border-bottom: none;\n}\n\n.notification-card:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.notification-card.clickable {\n cursor: pointer;\n}\n\n.notification-card.clickable:active {\n background: var(--mj-bg-surface-sunken);\n transform: scale(0.995);\n}\n\n/* Unread State with Blue Highlight */\n.notification-card.unread {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, var(--mj-bg-surface));\n}\n\n.notification-card.unread:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface-card));\n}\n\n/* Card Indicator - Blue Bar for Unread */\n.card-indicator {\n width: 4px;\n height: 100%;\n min-height: 64px;\n border-radius: 2px;\n background: transparent;\n transition: all 0.2s ease;\n align-self: stretch;\n}\n\n.notification-card.unread .card-indicator {\n background: var(--mj-brand-primary);\n box-shadow: 0 0 8px color-mix(in srgb, var(--mj-brand-primary) 50%, transparent);\n}\n\n/* Card Icon - Larger and More Prominent */\n.card-icon {\n width: 56px;\n height: 56px;\n border-radius: 14px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n transition: transform 0.2s ease;\n}\n\n.notification-card:hover .card-icon {\n transform: scale(1.05);\n}\n\n.card-icon i {\n font-size: 24px;\n}\n\n/* Card Content */\n.card-content {\n flex: 1;\n min-width: 0;\n}\n\n.card-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 6px;\n}\n\n.card-title {\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n margin: 0;\n line-height: 1.4;\n word-wrap: break-word;\n}\n\n.card-title-unread {\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.notification-card.clickable .card-title {\n color: var(--mj-brand-primary);\n}\n\n.notification-card.clickable:hover .card-title {\n text-decoration: underline;\n}\n\n.card-time {\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 13px;\n color: var(--mj-text-secondary);\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.card-time i {\n font-size: 12px;\n}\n\n/* Card Meta */\n.card-meta {\n margin-bottom: 8px;\n}\n\n.card-type {\n display: inline-flex;\n align-items: center;\n padding: 4px 12px;\n border-radius: 9999px; /* Circular design */\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n}\n\n/* Card Message */\n.card-message {\n font-size: 15px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n margin: 0;\n display: -webkit-box;\n -webkit-line-clamp: 3;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Card Actions - Circular Buttons */\n.card-actions {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n padding-top: 6px;\n}\n\n.card-action-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border-radius: 9999px; /* Circular design */\n font-size: 13px;\n font-weight: 600;\n border: 2px solid var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n cursor: pointer;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n white-space: nowrap;\n font-family: inherit;\n}\n\n.card-action-btn:hover {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: white;\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-md);\n}\n\n.card-action-btn:active {\n transform: translateY(0) scale(0.98);\n}\n\n.card-action-btn i {\n font-size: 13px;\n}\n\n/* ===================================\n ANIMATIONS\n =================================== */\n\n@keyframes slideInUp {\n from {\n opacity: 0;\n transform: translateY(12px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n.notification-card {\n animation: slideInUp 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n/* ===================================\n ACCESSIBILITY\n =================================== */\n\nbutton:focus-visible,\ninput:focus-visible,\nselect:focus-visible {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n/* Reduced Motion */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n}\n\n/* ===================================\n RESPONSIVE DESIGN - iPhone UX\n =================================== */\n\n/* iPad and smaller */\n@media (max-width: 768px) {\n .notifications-container {\n border-radius: 0;\n box-shadow: none;\n }\n\n .header-top {\n padding: 20px 20px 14px;\n }\n\n .title-icon {\n width: 44px;\n height: 44px;\n }\n\n .title-icon i {\n font-size: 20px;\n }\n\n .header-title h1 {\n font-size: 1.5rem;\n }\n\n /* Horizontal Slider on Mobile */\n .slider-content {\n padding: 14px 20px;\n gap: 10px;\n }\n\n .stat-pill {\n padding: 8px 16px;\n flex-shrink: 0;\n }\n\n .type-filter-slider {\n min-width: 160px;\n }\n\n .search-section {\n padding: 0 20px 14px;\n }\n\n .filter-info {\n padding: 0 20px 14px;\n }\n\n .notification-card {\n grid-template-columns: 4px 52px 1fr;\n gap: 12px;\n padding: 14px 16px;\n }\n\n .card-actions {\n grid-column: 2 / -1;\n padding-top: 10px;\n }\n\n .card-icon {\n width: 52px;\n height: 52px;\n }\n\n .card-icon i {\n font-size: 22px;\n }\n}\n\n/* iPhone SE and smaller */\n@media (max-width: 480px) {\n .header-top {\n flex-direction: column;\n align-items: stretch;\n gap: 12px;\n padding: 16px 16px 12px;\n }\n\n .header-title {\n justify-content: flex-start;\n }\n\n .title-icon {\n width: 40px;\n height: 40px;\n }\n\n .title-icon i {\n font-size: 18px;\n }\n\n .header-title h1 {\n font-size: 1.375rem;\n }\n\n .header-actions {\n width: 100%;\n justify-content: stretch;\n gap: 8px;\n }\n\n .action-btn {\n flex: 1;\n justify-content: center;\n padding: 10px 16px;\n font-size: 13px;\n }\n\n .btn-text {\n display: none;\n }\n\n /* Horizontal Slider - iPhone */\n .slider-content {\n padding: 12px 16px;\n gap: 8px;\n }\n\n .header-stats {\n gap: 8px;\n }\n\n .stat-pill {\n padding: 8px 14px;\n min-height: 40px;\n flex-shrink: 0;\n }\n\n .stat-label {\n font-size: 13px;\n }\n\n .stat-count {\n font-size: 12px;\n padding: 3px 8px;\n min-width: 24px;\n }\n\n .type-filter-slider {\n min-width: 140px;\n }\n\n .type-select,\n .search-input {\n font-size: 16px; /* Prevent iOS zoom on focus */\n }\n\n .search-section {\n padding: 0 16px 12px;\n }\n\n .filter-info {\n padding: 0 16px 12px;\n }\n\n .notification-card {\n grid-template-columns: 3px 48px 1fr;\n gap: 10px;\n padding: 12px 12px;\n }\n\n .card-indicator {\n width: 3px;\n min-height: 56px;\n }\n\n .card-icon {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n }\n\n .card-icon i {\n font-size: 20px;\n }\n\n .card-title {\n font-size: 15px;\n }\n\n .card-time {\n font-size: 12px;\n }\n\n .card-message {\n font-size: 14px;\n -webkit-line-clamp: 2;\n }\n\n .card-type {\n font-size: 11px;\n padding: 3px 10px;\n }\n\n .card-actions {\n padding-top: 8px;\n gap: 6px;\n }\n\n .action-text {\n display: none;\n }\n\n .card-action-btn {\n padding: 8px 12px;\n min-width: 40px;\n justify-content: center;\n }\n\n}\n\n/* iPhone 12/13/14 Pro Max and similar */\n@media (min-width: 390px) and (max-width: 480px) {\n .btn-text {\n display: inline;\n }\n\n .action-btn {\n font-size: 14px;\n }\n}\n\n/* Dark mode support (if needed in future) */\n@media (prefers-color-scheme: dark) {\n /* Dark mode styles would go here */\n}\n"] }]
715
- }], () => [{ type: i1.SharedService }, { type: i1.NavigationService }, { type: i2.ApplicationManager }], { allRadio: [{
716
- type: ViewChild,
717
- args: ['allRadio']
718
- }], unreadRadio: [{
719
- type: ViewChild,
720
- args: ['unreadRadio']
721
- }], readRadio: [{
722
- type: ViewChild,
723
- args: ['readRadio']
724
- }] }); })();
725
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UserNotificationsComponent, { className: "UserNotificationsComponent", filePath: "src/lib/user-notifications/user-notifications.component.ts", lineNumber: 56 }); })();
918
+ args: [{ standalone: false, selector: 'app-user-notifications', template: "<div class=\"notifications-container\">\n <!-- Header Section -->\n <div class=\"notifications-header\">\n <div class=\"header-top\">\n <div class=\"header-title\">\n <div class=\"title-icon\">\n <i class=\"fa-solid fa-bell\"></i>\n </div>\n <div class=\"title-text\">\n <h1>Notifications</h1>\n @if (UnreadNotifications.length > 0) {\n <span class=\"title-subtitle\">{{UnreadNotifications.length}} unread</span>\n } @else if (AllNotifications.length > 0) {\n <span class=\"title-subtitle\">All caught up</span>\n }\n </div>\n </div>\n\n @if (AllNotifications.length > 0) {\n <div class=\"header-actions\">\n @if (UnreadNotifications.length > 0) {\n <button class=\"action-btn action-btn-ghost\"\n type=\"button\"\n (click)=\"markAllAsRead()\"\n aria-label=\"Mark all notifications as read\">\n <i class=\"fa-solid fa-check-double\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Mark All Read</span>\n </button>\n }\n @if (ReadNotifications.length > 0) {\n <button class=\"action-btn action-btn-secondary\"\n type=\"button\"\n (click)=\"markAllAsUnread()\"\n aria-label=\"Mark all notifications as unread\">\n <i class=\"fa-solid fa-envelope\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Mark All Unread</span>\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Filters Section -->\n @if (AllNotifications.length > 0) {\n <div class=\"filters-section\">\n <div class=\"filters-slider\">\n <div class=\"slider-content\">\n <!-- Read-status pills -->\n <div class=\"header-stats\" role=\"group\" aria-label=\"Filter notifications by read status\">\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'All'\"\n (click)=\"selectReadOption('All')\"\n [attr.aria-pressed]=\"radioSelected === 'All'\"\n aria-label=\"Show all notifications\">\n <span class=\"stat-label\">All</span>\n <span class=\"stat-count\">{{AllNotifications.length}}</span>\n </button>\n\n @if (UnreadNotifications.length > 0 || ReadNotifications.length === 0) {\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'Unread'\"\n [class.has-unread]=\"UnreadNotifications.length > 0\"\n (click)=\"selectReadOption('Unread')\"\n [attr.aria-pressed]=\"radioSelected === 'Unread'\"\n aria-label=\"Show unread notifications\">\n <span class=\"stat-label\">Unread</span>\n <span class=\"stat-count\" [class.unread-count]=\"UnreadNotifications.length > 0\">\n {{UnreadNotifications.length}}\n </span>\n </button>\n }\n\n @if (ReadNotifications.length > 0 || UnreadNotifications.length === 0) {\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'Read'\"\n (click)=\"selectReadOption('Read')\"\n [attr.aria-pressed]=\"radioSelected === 'Read'\"\n aria-label=\"Show read notifications\">\n <span class=\"stat-label\">Read</span>\n <span class=\"stat-count\">{{ReadNotifications.length}}</span>\n </button>\n }\n </div>\n\n <!-- Type filter -->\n @if (notificationTypes.length > 0) {\n <div class=\"filter-item type-filter-slider\">\n <select class=\"type-select\"\n [(ngModel)]=\"selectedTypeFilter\"\n (change)=\"onTypeFilterChange(selectedTypeFilter)\"\n aria-label=\"Filter by notification type\">\n <option [value]=\"null\">All Types</option>\n @for (type of notificationTypes; track type.ID) {\n <option [value]=\"type.ID\">{{type.Name}}</option>\n }\n </select>\n </div>\n }\n </div>\n </div>\n\n <!-- Search -->\n <div class=\"search-section\">\n <div class=\"search-container\">\n <i class=\"fa-solid fa-magnifying-glass search-icon\" aria-hidden=\"true\"></i>\n <input type=\"text\"\n class=\"search-input\"\n placeholder=\"Search notifications...\"\n [value]=\"currentFilter\"\n (input)=\"onFilterChanged($any($event.target).value)\"\n aria-label=\"Search notifications\" />\n </div>\n </div>\n\n @if (NotificationsToShow.length !== AllNotifications.length) {\n <div class=\"filter-info\">\n <span class=\"filter-badge\">\n <i class=\"fa-solid fa-filter\" aria-hidden=\"true\"></i>\n Showing {{NotificationsToShow.length}} of {{AllNotifications.length}}\n </span>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Notifications Content -->\n <div class=\"notifications-content\">\n @if (NotificationsToShow.length === 0 && AllNotifications.length === 0) {\n <mj-empty-state\n Variant=\"success\"\n Icon=\"fa-solid fa-bell-slash\"\n Title=\"No Notifications\"\n Message=\"You're all caught up! New notifications will appear here.\" />\n } @else if (NotificationsToShow.length === 0) {\n <mj-empty-state\n Variant=\"no-results\"\n Icon=\"fa-solid fa-filter-circle-xmark\"\n Title=\"No Matching Notifications\"\n Message=\"Try adjusting your filters to see more notifications.\" />\n } @else {\n <!-- Day-grouped notification list -->\n <div class=\"notifications-list\">\n @for (group of Groups; track group.Key) {\n <div class=\"group-label\">{{group.Label}}</div>\n\n @for (vm of group.Items; track vm.N.ID) {\n <article class=\"notification-card\"\n [class.unread]=\"vm.N.Unread\"\n [class.expanded]=\"IsExpanded(vm)\"\n [class.clickable]=\"vm.Clickable\"\n role=\"article\"\n [attr.aria-label]=\"vm.N.Title\">\n\n <!-- Unread Indicator Bar -->\n <div class=\"card-indicator\" aria-hidden=\"true\"></div>\n\n <!-- Type Icon -->\n <div class=\"card-icon\" aria-hidden=\"true\">\n <i [class]=\"'fa-solid ' + getTypeIcon(vm.N)\"\n [style.color]=\"getTypeColor(vm.N)\"></i>\n </div>\n\n <!-- Content -->\n <div class=\"card-content\" (click)=\"notificationClicked(vm.N)\">\n <div class=\"card-header\">\n <h3 class=\"card-title\" [class.card-title-unread]=\"vm.N.Unread\">\n {{vm.N.Title}}\n </h3>\n <span class=\"card-time\" [title]=\"(vm.N.__mj_CreatedAt | date:'medium') ?? ''\">\n @if (vm.Relative) {\n {{vm.Relative}}\n } @else {\n <time [attr.datetime]=\"vm.N.__mj_CreatedAt\">{{vm.N.__mj_CreatedAt | date:'MMM d, h:mm a'}}</time>\n }\n </span>\n </div>\n\n <div class=\"card-meta\">\n <span class=\"card-type\">{{getTypeName(vm.N)}}</span>\n @if (vm.Clickable) {\n <span class=\"card-open-hint\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\" aria-hidden=\"true\"></i> Open\n </span>\n }\n </div>\n\n @if (!IsExpanded(vm)) {\n <p class=\"card-message\">{{vm.Preview}}</p>\n } @else {\n <div class=\"card-rich\" (click)=\"$event.stopPropagation()\">\n @switch (vm.Kind) {\n @case ('html') {\n <!-- Angular sanitizes on [innerHTML] bind; chrome tags were stripped at classification -->\n <div class=\"card-rich-html\" [innerHTML]=\"vm.Body\"></div>\n }\n @case ('markdown') {\n <mj-markdown class=\"card-rich-md\" [data]=\"vm.Body\" [enableMermaid]=\"false\"></mj-markdown>\n }\n @default {\n <p class=\"card-message card-message-full\">{{vm.N.Message}}</p>\n }\n }\n </div>\n }\n\n @if (vm.Expandable) {\n <button class=\"card-expand-btn\" type=\"button\"\n (click)=\"ToggleExpanded(vm, $event)\"\n [attr.aria-expanded]=\"IsExpanded(vm)\">\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!IsExpanded(vm)\" [class.fa-chevron-up]=\"IsExpanded(vm)\" aria-hidden=\"true\"></i>\n {{ IsExpanded(vm) ? 'Show less' : 'Show more' }}\n </button>\n }\n </div>\n\n <!-- Actions -->\n <div class=\"card-actions\">\n @if (vm.N.Unread) {\n <button class=\"card-action-btn\"\n type=\"button\"\n (click)=\"markAsRead(vm.N, true, null)\"\n [attr.aria-label]=\"'Mark ' + vm.N.Title + ' as read'\"\n title=\"Mark as Read\">\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n <span class=\"action-text\">Read</span>\n </button>\n } @else {\n <button class=\"card-action-btn\"\n type=\"button\"\n (click)=\"markAsRead(vm.N, false, null)\"\n [attr.aria-label]=\"'Mark ' + vm.N.Title + ' as unread'\"\n title=\"Mark as Unread\">\n <i class=\"fa-solid fa-envelope\" aria-hidden=\"true\"></i>\n <span class=\"action-text\">Unread</span>\n </button>\n }\n </div>\n </article>\n }\n }\n </div>\n }\n </div>\n</div>\n", styles: ["/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n User Notifications \u2014 day-grouped cards with rich (HTML/Markdown) content,\n expand/collapse, and token-driven theming (light + dark for free).\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n:host {\n display: block;\n height: 100%;\n}\n\n.notifications-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-page);\n}\n\n/* \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.notifications-header {\n flex-shrink: 0;\n padding: 20px 24px 12px 24px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n margin-bottom: 14px;\n}\n\n.header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n}\n\n.title-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: var(--mj-radius-lg, 12px);\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.title-text {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n.title-text h1 {\n margin: 0;\n font-size: 20px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.2;\n}\n\n.title-subtitle {\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.header-actions {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.action-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 12px;\n border-radius: var(--mj-radius-md, 8px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.15s, color 0.15s, border-color 0.15s;\n}\n\n.action-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.action-btn-ghost {\n border-color: color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.action-btn-ghost:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n/* \u2500\u2500 Filters \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.filters-section {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 10px;\n}\n\n.filters-slider {\n min-width: 0;\n}\n\n.slider-content {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.header-stats {\n display: inline-flex;\n gap: 4px;\n padding: 3px;\n border-radius: var(--mj-radius-full, 999px);\n background: var(--mj-bg-surface-sunken);\n}\n\n.stat-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n border: none;\n border-radius: var(--mj-radius-full, 999px);\n background: transparent;\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.15s, color 0.15s;\n}\n\n.stat-pill:hover {\n color: var(--mj-text-primary);\n}\n\n.stat-pill.active {\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.stat-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n border-radius: var(--mj-radius-full, 999px);\n background: color-mix(in srgb, var(--mj-text-muted) 14%, transparent);\n color: var(--mj-text-secondary);\n font-size: 10px;\n font-weight: 700;\n}\n\n.stat-count.unread-count {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--mj-brand-primary);\n}\n\n.type-select {\n padding: 7px 10px;\n border-radius: var(--mj-radius-md, 8px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.search-section {\n flex: 1 1 220px;\n min-width: 180px;\n}\n\n.search-container {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.search-icon {\n position: absolute;\n left: 12px;\n color: var(--mj-text-muted);\n font-size: 12px;\n pointer-events: none;\n}\n\n.search-input {\n width: 100%;\n padding: 8px 12px 8px 32px;\n border-radius: var(--mj-radius-md, 8px);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 13px;\n}\n\n.search-input:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n}\n\n.filter-info {\n flex-basis: 100%;\n}\n\n.filter-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: var(--mj-radius-full, 999px);\n background: color-mix(in srgb, var(--mj-status-info) 10%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n font-size: 11px;\n font-weight: 600;\n}\n\n/* \u2500\u2500 Content / list \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.notifications-content {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n padding: 16px 24px 32px 24px;\n}\n\n.notifications-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n max-width: 960px;\n margin: 0 auto;\n}\n\n.group-label {\n position: sticky;\n top: -16px;\n z-index: 1;\n padding: 10px 2px 4px 2px;\n background: var(--mj-bg-page);\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--mj-text-muted);\n}\n\n.group-label:not(:first-child) {\n margin-top: 8px;\n}\n\n/* \u2500\u2500 Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.notification-card {\n position: relative;\n display: flex;\n gap: 12px;\n padding: 14px 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n background: var(--mj-bg-surface);\n transition: border-color 0.15s, box-shadow 0.15s, background-color 0.15s;\n overflow: hidden;\n}\n\n.notification-card:hover {\n border-color: var(--mj-border-strong);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.notification-card.unread {\n background: color-mix(in srgb, var(--mj-brand-primary) 3%, var(--mj-bg-surface));\n}\n\n.card-indicator {\n display: none;\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 3px;\n background: var(--mj-brand-primary);\n border-radius: 0 2px 2px 0;\n}\n\n.notification-card.unread .card-indicator {\n display: block;\n}\n\n.card-icon {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border-radius: var(--mj-radius-md, 10px);\n background: var(--mj-bg-surface-sunken);\n font-size: 14px;\n}\n\n.card-content {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.notification-card.clickable .card-content {\n cursor: pointer;\n}\n\n.card-header {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 12px;\n}\n\n.card-title {\n margin: 0;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow-wrap: anywhere;\n}\n\n.card-title-unread {\n font-weight: 700;\n}\n\n.card-time {\n flex-shrink: 0;\n font-size: 11px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n.card-meta {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 3px 0 6px 0;\n}\n\n.card-type {\n display: inline-flex;\n padding: 2px 8px;\n border-radius: var(--mj-radius-full, 999px);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n\n.card-open-hint {\n display: none;\n align-items: center;\n gap: 4px;\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.notification-card.clickable:hover .card-open-hint {\n display: inline-flex;\n}\n\n.card-message {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--mj-text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n overflow-wrap: anywhere;\n}\n\n.card-message-full {\n display: block;\n -webkit-line-clamp: unset;\n white-space: pre-wrap;\n}\n\n/* \u2500\u2500 Rich (expanded) content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.card-rich {\n margin-top: 4px;\n padding: 12px 14px;\n border: 1px solid var(--mj-border-subtle);\n border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-card);\n cursor: default;\n}\n\n/* Sanitized foreign markup: keep it inside the card no matter what it brought along */\n.card-rich-html {\n font-size: 13px;\n line-height: 1.55;\n color: var(--mj-text-primary);\n overflow-x: auto;\n}\n\n.card-rich-html img {\n max-width: 100%;\n height: auto;\n}\n\n.card-rich-html table {\n max-width: 100%;\n border-collapse: collapse;\n}\n\n.card-rich-html td,\n.card-rich-html th {\n padding: 4px 8px;\n border: 1px solid var(--mj-border-subtle);\n}\n\n.card-rich-html a {\n color: var(--mj-text-link);\n}\n\n.card-rich-html h1,\n.card-rich-html h2,\n.card-rich-html h3 {\n margin: 0.4em 0 0.3em 0;\n font-size: 1.05em;\n}\n\n.card-rich-md {\n display: block;\n font-size: 13px;\n}\n\n.card-expand-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n padding: 4px 10px;\n border: none;\n border-radius: var(--mj-radius-full, 999px);\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 11px;\n font-weight: 700;\n cursor: pointer;\n}\n\n.card-expand-btn:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n/* \u2500\u2500 Card actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.card-actions {\n flex-shrink: 0;\n display: flex;\n align-items: flex-start;\n}\n\n.card-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n border: 1px solid transparent;\n border-radius: var(--mj-radius-md, 8px);\n background: transparent;\n color: var(--mj-text-muted);\n font-size: 11px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.15s, color 0.15s;\n}\n\n.card-action-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-default);\n}\n\n/* \u2500\u2500 Responsive \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n@media (max-width: 720px) {\n .notifications-header {\n padding: 14px 14px 10px 14px;\n }\n\n .notifications-content {\n padding: 12px 12px 24px 12px;\n }\n\n .btn-text,\n .action-text {\n display: none;\n }\n\n .card-header {\n flex-direction: column;\n gap: 2px;\n }\n\n .card-time {\n order: -1;\n }\n}\n"] }]
919
+ }], () => [{ type: i1.SharedService }, { type: i1.NavigationService }, { type: i2.ApplicationManager }], null); })();
920
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UserNotificationsComponent, { className: "UserNotificationsComponent", filePath: "src/lib/user-notifications/user-notifications.component.ts", lineNumber: 93 }); })();
726
921
  //# sourceMappingURL=user-notifications.component.js.map