mesauth-angular 1.3.4 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/mesauth-angular.mjs +48 -45
- package/fesm2022/mesauth-angular.mjs.map +1 -1
- package/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -556,7 +556,7 @@ class UserProfileComponent {
|
|
|
556
556
|
<ng-container *ngIf="currentUser()">
|
|
557
557
|
<div class="user-header">
|
|
558
558
|
<!-- Notification Bell -->
|
|
559
|
-
<button class="notification-btn" [class.has-unread]="unreadCount > 0" (click)="onNotificationClick()" title="Notifications">
|
|
559
|
+
<button class="notification-btn" [class.has-unread]="unreadCount > 0" (click)="onNotificationClick()" title="Notifications" aria-label="Notifications">
|
|
560
560
|
<svg class="bell-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
561
561
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
|
|
562
562
|
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
|
@@ -566,7 +566,7 @@ class UserProfileComponent {
|
|
|
566
566
|
|
|
567
567
|
<!-- User Avatar + Dropdown -->
|
|
568
568
|
<div class="user-menu-wrapper">
|
|
569
|
-
<button class="user-menu-btn" (click)="toggleDropdown()">
|
|
569
|
+
<button class="user-menu-btn" (click)="toggleDropdown()" [attr.aria-label]="'User menu for ' + (currentUser().fullName || currentUser().userName)" aria-haspopup="true" [attr.aria-expanded]="dropdownOpen">
|
|
570
570
|
<div class="avatar-ring" [class.active]="dropdownOpen">
|
|
571
571
|
<img
|
|
572
572
|
*ngIf="currentUser().fullName || currentUser().userName"
|
|
@@ -639,7 +639,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
639
639
|
<ng-container *ngIf="currentUser()">
|
|
640
640
|
<div class="user-header">
|
|
641
641
|
<!-- Notification Bell -->
|
|
642
|
-
<button class="notification-btn" [class.has-unread]="unreadCount > 0" (click)="onNotificationClick()" title="Notifications">
|
|
642
|
+
<button class="notification-btn" [class.has-unread]="unreadCount > 0" (click)="onNotificationClick()" title="Notifications" aria-label="Notifications">
|
|
643
643
|
<svg class="bell-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
644
644
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
|
|
645
645
|
<path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
|
@@ -649,7 +649,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
649
649
|
|
|
650
650
|
<!-- User Avatar + Dropdown -->
|
|
651
651
|
<div class="user-menu-wrapper">
|
|
652
|
-
<button class="user-menu-btn" (click)="toggleDropdown()">
|
|
652
|
+
<button class="user-menu-btn" (click)="toggleDropdown()" [attr.aria-label]="'User menu for ' + (currentUser().fullName || currentUser().userName)" aria-haspopup="true" [attr.aria-expanded]="dropdownOpen">
|
|
653
653
|
<div class="avatar-ring" [class.active]="dropdownOpen">
|
|
654
654
|
<img
|
|
655
655
|
*ngIf="currentUser().fullName || currentUser().userName"
|
|
@@ -820,8 +820,8 @@ class ToastContainerComponent {
|
|
|
820
820
|
</svg>
|
|
821
821
|
</button>
|
|
822
822
|
|
|
823
|
-
<!-- Auto-dismiss progress bar -->
|
|
824
|
-
<div class="toast-progress" [style.animation-duration]="(toast.duration
|
|
823
|
+
<!-- Auto-dismiss progress bar — hidden when duration <= 0 (persistent toast) -->
|
|
824
|
+
<div class="toast-progress" *ngIf="toast.duration == null || toast.duration > 0" [style.animation-duration]="(toast.duration ?? 5000) + 'ms'"></div>
|
|
825
825
|
</div>
|
|
826
826
|
</div>
|
|
827
827
|
`, isInline: true, styles: [":host{--info-color: #2196f3;--info-bg: rgba(33, 150, 243, .1);--success-color: #43a047;--success-bg: rgba(67, 160, 71, .1);--warning-color: #f57c00;--warning-bg: rgba(245, 124, 0, .1);--error-color: #e53935;--error-bg: rgba(229, 57, 53, .1);--text-primary: #212121;--text-secondary: #757575;--bg-primary: #ffffff;--border-color: rgba(0, 0, 0, .08);--shadow: rgba(0, 0, 0, .1);--shadow-lg: rgba(0, 0, 0, .18)}:host(.theme-dark){--info-color: #64b5f6;--info-bg: rgba(100, 181, 246, .12);--success-color: #66bb6a;--success-bg: rgba(102, 187, 106, .12);--warning-color: #ffb74d;--warning-bg: rgba(255, 183, 77, .12);--error-color: #ef5350;--error-bg: rgba(239, 83, 80, .12);--text-primary: #e0e0e0;--text-secondary: #9e9e9e;--bg-primary: #1e1e2e;--border-color: rgba(255, 255, 255, .08);--shadow: rgba(0, 0, 0, .35);--shadow-lg: rgba(0, 0, 0, .5)}.toast-container{position:fixed;top:20px;right:20px;z-index:9999;pointer-events:none;display:flex;flex-direction:column;gap:10px}.toast{position:relative;display:flex;align-items:flex-start;gap:11px;padding:13px 13px 16px 16px;border-radius:12px;background:var(--bg-primary);border:1px solid var(--border-color);box-shadow:0 8px 28px var(--shadow-lg),0 2px 8px var(--shadow);pointer-events:auto;min-width:300px;max-width:420px;overflow:hidden;animation:toast-in .35s cubic-bezier(.16,1,.3,1)}@keyframes toast-in{0%{opacity:0;transform:translate(36px) scale(.96)}to{opacity:1;transform:translate(0) scale(1)}}.toast:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:12px 0 0 12px}.toast-info:before{background:var(--info-color)}.toast-success:before{background:var(--success-color)}.toast-warning:before{background:var(--warning-color)}.toast-error:before{background:var(--error-color)}.toast-icon{flex-shrink:0;width:34px;height:34px;border-radius:9px;display:flex;align-items:center;justify-content:center;margin-left:2px}.toast-info .toast-icon{color:var(--info-color);background:var(--info-bg)}.toast-success .toast-icon{color:var(--success-color);background:var(--success-bg)}.toast-warning .toast-icon{color:var(--warning-color);background:var(--warning-bg)}.toast-error .toast-icon{color:var(--error-color);background:var(--error-bg)}.toast-content{flex:1;min-width:0;padding-top:1px}.toast-title{font-weight:700;font-size:13.5px;margin-bottom:3px;line-height:1.3}.toast-info .toast-title{color:var(--info-color)}.toast-success .toast-title{color:var(--success-color)}.toast-warning .toast-title{color:var(--warning-color)}.toast-error .toast-title{color:var(--error-color)}.toast-message{font-size:12.5px;line-height:1.45;color:var(--text-secondary)}.toast-close{background:none;border:none;cursor:pointer;color:var(--text-secondary);width:26px;height:26px;border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0;padding:0;transition:color .15s,background-color .15s}.toast-close:hover{color:var(--text-primary);background:var(--border-color)}.toast-progress{position:absolute;bottom:0;left:4px;right:0;height:3px;border-radius:0 0 12px;animation:toast-progress linear forwards;opacity:.7}.toast-info .toast-progress{background:var(--info-color)}.toast-success .toast-progress{background:var(--success-color)}.toast-warning .toast-progress{background:var(--warning-color)}.toast-error .toast-progress{background:var(--error-color)}@keyframes toast-progress{0%{width:calc(100% - 4px)}to{width:0}}@media(max-width:600px){.toast-container{top:10px;right:10px;left:10px}.toast{min-width:auto;max-width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
@@ -861,8 +861,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
861
861
|
</svg>
|
|
862
862
|
</button>
|
|
863
863
|
|
|
864
|
-
<!-- Auto-dismiss progress bar -->
|
|
865
|
-
<div class="toast-progress" [style.animation-duration]="(toast.duration
|
|
864
|
+
<!-- Auto-dismiss progress bar — hidden when duration <= 0 (persistent toast) -->
|
|
865
|
+
<div class="toast-progress" *ngIf="toast.duration == null || toast.duration > 0" [style.animation-duration]="(toast.duration ?? 5000) + 'ms'"></div>
|
|
866
866
|
</div>
|
|
867
867
|
</div>
|
|
868
868
|
`, styles: [":host{--info-color: #2196f3;--info-bg: rgba(33, 150, 243, .1);--success-color: #43a047;--success-bg: rgba(67, 160, 71, .1);--warning-color: #f57c00;--warning-bg: rgba(245, 124, 0, .1);--error-color: #e53935;--error-bg: rgba(229, 57, 53, .1);--text-primary: #212121;--text-secondary: #757575;--bg-primary: #ffffff;--border-color: rgba(0, 0, 0, .08);--shadow: rgba(0, 0, 0, .1);--shadow-lg: rgba(0, 0, 0, .18)}:host(.theme-dark){--info-color: #64b5f6;--info-bg: rgba(100, 181, 246, .12);--success-color: #66bb6a;--success-bg: rgba(102, 187, 106, .12);--warning-color: #ffb74d;--warning-bg: rgba(255, 183, 77, .12);--error-color: #ef5350;--error-bg: rgba(239, 83, 80, .12);--text-primary: #e0e0e0;--text-secondary: #9e9e9e;--bg-primary: #1e1e2e;--border-color: rgba(255, 255, 255, .08);--shadow: rgba(0, 0, 0, .35);--shadow-lg: rgba(0, 0, 0, .5)}.toast-container{position:fixed;top:20px;right:20px;z-index:9999;pointer-events:none;display:flex;flex-direction:column;gap:10px}.toast{position:relative;display:flex;align-items:flex-start;gap:11px;padding:13px 13px 16px 16px;border-radius:12px;background:var(--bg-primary);border:1px solid var(--border-color);box-shadow:0 8px 28px var(--shadow-lg),0 2px 8px var(--shadow);pointer-events:auto;min-width:300px;max-width:420px;overflow:hidden;animation:toast-in .35s cubic-bezier(.16,1,.3,1)}@keyframes toast-in{0%{opacity:0;transform:translate(36px) scale(.96)}to{opacity:1;transform:translate(0) scale(1)}}.toast:before{content:\"\";position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:12px 0 0 12px}.toast-info:before{background:var(--info-color)}.toast-success:before{background:var(--success-color)}.toast-warning:before{background:var(--warning-color)}.toast-error:before{background:var(--error-color)}.toast-icon{flex-shrink:0;width:34px;height:34px;border-radius:9px;display:flex;align-items:center;justify-content:center;margin-left:2px}.toast-info .toast-icon{color:var(--info-color);background:var(--info-bg)}.toast-success .toast-icon{color:var(--success-color);background:var(--success-bg)}.toast-warning .toast-icon{color:var(--warning-color);background:var(--warning-bg)}.toast-error .toast-icon{color:var(--error-color);background:var(--error-bg)}.toast-content{flex:1;min-width:0;padding-top:1px}.toast-title{font-weight:700;font-size:13.5px;margin-bottom:3px;line-height:1.3}.toast-info .toast-title{color:var(--info-color)}.toast-success .toast-title{color:var(--success-color)}.toast-warning .toast-title{color:var(--warning-color)}.toast-error .toast-title{color:var(--error-color)}.toast-message{font-size:12.5px;line-height:1.45;color:var(--text-secondary)}.toast-close{background:none;border:none;cursor:pointer;color:var(--text-secondary);width:26px;height:26px;border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0;padding:0;transition:color .15s,background-color .15s}.toast-close:hover{color:var(--text-primary);background:var(--border-color)}.toast-progress{position:absolute;bottom:0;left:4px;right:0;height:3px;border-radius:0 0 12px;animation:toast-progress linear forwards;opacity:.7}.toast-info .toast-progress{background:var(--info-color)}.toast-success .toast-progress{background:var(--success-color)}.toast-warning .toast-progress{background:var(--warning-color)}.toast-error .toast-progress{background:var(--error-color)}@keyframes toast-progress{0%{width:calc(100% - 4px)}to{width:0}}@media(max-width:600px){.toast-container{top:10px;right:10px;left:10px}.toast{min-width:auto;max-width:100%}}\n"] }]
|
|
@@ -906,17 +906,18 @@ class NotificationPanelComponent {
|
|
|
906
906
|
getNotificationMessage(notification) {
|
|
907
907
|
return notification.message || '';
|
|
908
908
|
}
|
|
909
|
-
// Normalize type to string — API may return integer
|
|
909
|
+
// Normalize type to string — API may return integer or string
|
|
910
|
+
// Backend enum: Info=0, Success=1, Warning=2, Error=3
|
|
910
911
|
typeOf(notification) {
|
|
911
912
|
const t = notification.type;
|
|
912
913
|
if (t === 0 || t === 'Info')
|
|
913
914
|
return 'Info';
|
|
914
|
-
if (t === 1 || t === '
|
|
915
|
+
if (t === 1 || t === 'Success')
|
|
916
|
+
return 'Success';
|
|
917
|
+
if (t === 2 || t === 'Warning')
|
|
915
918
|
return 'Warning';
|
|
916
|
-
if (t ===
|
|
919
|
+
if (t === 3 || t === 'Error')
|
|
917
920
|
return 'Error';
|
|
918
|
-
if (t === 3 || t === 'Success')
|
|
919
|
-
return 'Success';
|
|
920
921
|
return 'Info';
|
|
921
922
|
}
|
|
922
923
|
toastType(type) {
|
|
@@ -1133,7 +1134,7 @@ class NotificationPanelComponent {
|
|
|
1133
1134
|
</svg>
|
|
1134
1135
|
<h3>Notifications</h3>
|
|
1135
1136
|
</div>
|
|
1136
|
-
<button class="close-btn" (click)="close()" title="Close">
|
|
1137
|
+
<button class="close-btn" (click)="close()" title="Close" aria-label="Close notifications">
|
|
1137
1138
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1138
1139
|
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
|
1139
1140
|
</svg>
|
|
@@ -1161,26 +1162,27 @@ class NotificationPanelComponent {
|
|
|
1161
1162
|
[class.unread]="!notification.isRead"
|
|
1162
1163
|
(click)="openDetails(notification)"
|
|
1163
1164
|
>
|
|
1165
|
+
@let t = typeOf(notification);
|
|
1164
1166
|
<div class="notif-accent"
|
|
1165
|
-
[class.type-info]="
|
|
1166
|
-
[class.type-success]="
|
|
1167
|
-
[class.type-warning]="
|
|
1168
|
-
[class.type-error]="
|
|
1167
|
+
[class.type-info]="t === 'Info'"
|
|
1168
|
+
[class.type-success]="t === 'Success'"
|
|
1169
|
+
[class.type-warning]="t === 'Warning'"
|
|
1170
|
+
[class.type-error]="t === 'Error'"></div>
|
|
1169
1171
|
<div class="notif-type-icon"
|
|
1170
|
-
[class.type-info]="
|
|
1171
|
-
[class.type-success]="
|
|
1172
|
-
[class.type-warning]="
|
|
1173
|
-
[class.type-error]="
|
|
1174
|
-
<svg *ngIf="
|
|
1172
|
+
[class.type-info]="t === 'Info'"
|
|
1173
|
+
[class.type-success]="t === 'Success'"
|
|
1174
|
+
[class.type-warning]="t === 'Warning'"
|
|
1175
|
+
[class.type-error]="t === 'Error'">
|
|
1176
|
+
<svg *ngIf="t === 'Info'" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1175
1177
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
|
1176
1178
|
</svg>
|
|
1177
|
-
<svg *ngIf="
|
|
1179
|
+
<svg *ngIf="t === 'Success'" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1178
1180
|
<polyline points="20 6 9 17 4 12"/>
|
|
1179
1181
|
</svg>
|
|
1180
|
-
<svg *ngIf="
|
|
1182
|
+
<svg *ngIf="t === 'Warning'" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1181
1183
|
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>
|
|
1182
1184
|
</svg>
|
|
1183
|
-
<svg *ngIf="
|
|
1185
|
+
<svg *ngIf="t === 'Error'" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1184
1186
|
<circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>
|
|
1185
1187
|
</svg>
|
|
1186
1188
|
</div>
|
|
@@ -1192,12 +1194,12 @@ class NotificationPanelComponent {
|
|
|
1192
1194
|
<span class="time">{{ dateLabels.get(notification.id) }}</span>
|
|
1193
1195
|
</div>
|
|
1194
1196
|
</div>
|
|
1195
|
-
<button class="icon-btn read-btn" (click)="markAsRead(notification.id, $event)" title="Mark as read" *ngIf="!notification.isRead">
|
|
1197
|
+
<button class="icon-btn read-btn" (click)="markAsRead(notification.id, $event)" title="Mark as read" aria-label="Mark as read" *ngIf="!notification.isRead">
|
|
1196
1198
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1197
1199
|
<polyline points="20 6 9 17 4 12"/>
|
|
1198
1200
|
</svg>
|
|
1199
1201
|
</button>
|
|
1200
|
-
<button class="icon-btn delete-btn" (click)="delete(notification.id, $event)" title="Delete" *ngIf="notification.isRead">
|
|
1202
|
+
<button class="icon-btn delete-btn" (click)="delete(notification.id, $event)" title="Delete" aria-label="Delete notification" *ngIf="notification.isRead">
|
|
1201
1203
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1202
1204
|
<polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
|
|
1203
1205
|
</svg>
|
|
@@ -1260,7 +1262,7 @@ class NotificationPanelComponent {
|
|
|
1260
1262
|
</div>
|
|
1261
1263
|
<h3>{{ selectedNotification.title }}</h3>
|
|
1262
1264
|
</div>
|
|
1263
|
-
<button class="close-btn" (click)="closeDetails()" title="Close">
|
|
1265
|
+
<button class="close-btn" (click)="closeDetails()" title="Close" aria-label="Close notification detail">
|
|
1264
1266
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1265
1267
|
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
|
1266
1268
|
</svg>
|
|
@@ -1291,7 +1293,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
1291
1293
|
</svg>
|
|
1292
1294
|
<h3>Notifications</h3>
|
|
1293
1295
|
</div>
|
|
1294
|
-
<button class="close-btn" (click)="close()" title="Close">
|
|
1296
|
+
<button class="close-btn" (click)="close()" title="Close" aria-label="Close notifications">
|
|
1295
1297
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1296
1298
|
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
|
1297
1299
|
</svg>
|
|
@@ -1319,26 +1321,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
1319
1321
|
[class.unread]="!notification.isRead"
|
|
1320
1322
|
(click)="openDetails(notification)"
|
|
1321
1323
|
>
|
|
1324
|
+
@let t = typeOf(notification);
|
|
1322
1325
|
<div class="notif-accent"
|
|
1323
|
-
[class.type-info]="
|
|
1324
|
-
[class.type-success]="
|
|
1325
|
-
[class.type-warning]="
|
|
1326
|
-
[class.type-error]="
|
|
1326
|
+
[class.type-info]="t === 'Info'"
|
|
1327
|
+
[class.type-success]="t === 'Success'"
|
|
1328
|
+
[class.type-warning]="t === 'Warning'"
|
|
1329
|
+
[class.type-error]="t === 'Error'"></div>
|
|
1327
1330
|
<div class="notif-type-icon"
|
|
1328
|
-
[class.type-info]="
|
|
1329
|
-
[class.type-success]="
|
|
1330
|
-
[class.type-warning]="
|
|
1331
|
-
[class.type-error]="
|
|
1332
|
-
<svg *ngIf="
|
|
1331
|
+
[class.type-info]="t === 'Info'"
|
|
1332
|
+
[class.type-success]="t === 'Success'"
|
|
1333
|
+
[class.type-warning]="t === 'Warning'"
|
|
1334
|
+
[class.type-error]="t === 'Error'">
|
|
1335
|
+
<svg *ngIf="t === 'Info'" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1333
1336
|
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/>
|
|
1334
1337
|
</svg>
|
|
1335
|
-
<svg *ngIf="
|
|
1338
|
+
<svg *ngIf="t === 'Success'" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1336
1339
|
<polyline points="20 6 9 17 4 12"/>
|
|
1337
1340
|
</svg>
|
|
1338
|
-
<svg *ngIf="
|
|
1341
|
+
<svg *ngIf="t === 'Warning'" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1339
1342
|
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/>
|
|
1340
1343
|
</svg>
|
|
1341
|
-
<svg *ngIf="
|
|
1344
|
+
<svg *ngIf="t === 'Error'" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1342
1345
|
<circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>
|
|
1343
1346
|
</svg>
|
|
1344
1347
|
</div>
|
|
@@ -1350,12 +1353,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
1350
1353
|
<span class="time">{{ dateLabels.get(notification.id) }}</span>
|
|
1351
1354
|
</div>
|
|
1352
1355
|
</div>
|
|
1353
|
-
<button class="icon-btn read-btn" (click)="markAsRead(notification.id, $event)" title="Mark as read" *ngIf="!notification.isRead">
|
|
1356
|
+
<button class="icon-btn read-btn" (click)="markAsRead(notification.id, $event)" title="Mark as read" aria-label="Mark as read" *ngIf="!notification.isRead">
|
|
1354
1357
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1355
1358
|
<polyline points="20 6 9 17 4 12"/>
|
|
1356
1359
|
</svg>
|
|
1357
1360
|
</button>
|
|
1358
|
-
<button class="icon-btn delete-btn" (click)="delete(notification.id, $event)" title="Delete" *ngIf="notification.isRead">
|
|
1361
|
+
<button class="icon-btn delete-btn" (click)="delete(notification.id, $event)" title="Delete" aria-label="Delete notification" *ngIf="notification.isRead">
|
|
1359
1362
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1360
1363
|
<polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
|
|
1361
1364
|
</svg>
|
|
@@ -1418,7 +1421,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
1418
1421
|
</div>
|
|
1419
1422
|
<h3>{{ selectedNotification.title }}</h3>
|
|
1420
1423
|
</div>
|
|
1421
|
-
<button class="close-btn" (click)="closeDetails()" title="Close">
|
|
1424
|
+
<button class="close-btn" (click)="closeDetails()" title="Close" aria-label="Close notification detail">
|
|
1422
1425
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
1423
1426
|
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
|
1424
1427
|
</svg>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mesauth-angular.mjs","sources":["../../src/mes-auth.service.ts","../../src/mes-auth.interceptor.ts","../../src/mes-auth.module.ts","../../src/theme.service.ts","../../src/user-profile.component.ts","../../src/toast.service.ts","../../src/toast-container.component.ts","../../src/notification-panel.component.ts","../../src/ma-user.component.ts","../../src/notification-badge.component.ts","../../src/mesauth-angular.ts"],"sourcesContent":["import { inject, Injectable, InjectionToken, EnvironmentProviders, makeEnvironmentProviders, provideAppInitializer, NgZone } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { HubConnection, HubConnectionBuilder, LogLevel } from '@microsoft/signalr';\nimport { BehaviorSubject, Subject, Observable, of, EMPTY } from 'rxjs';\nimport { tap, catchError } from 'rxjs/operators';\nimport { Router } from '@angular/router';\n\nexport interface MesAuthConfig {\n apiBaseUrl: string;\n withCredentials?: boolean;\n userBaseUrl?: string;\n}\n\n/** Injection token for MesAuth configuration */\nexport const MES_AUTH_CONFIG = new InjectionToken<MesAuthConfig>('MES_AUTH_CONFIG');\n\n/**\n * Provides MesAuth with configuration.\n * This is the recommended way to set up mesauth-angular in standalone apps.\n *\n * @example\n * ```typescript\n * // app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHttpClient(withInterceptors([mesAuthInterceptor])),\n * provideMesAuth({\n * apiBaseUrl: 'https://auth.example.com',\n * userBaseUrl: 'https://app.example.com'\n * })\n * ]\n * };\n * ```\n */\nexport function provideMesAuth(config: MesAuthConfig): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: MES_AUTH_CONFIG, useValue: config },\n MesAuthService,\n provideAppInitializer(() => {\n const mesAuthService = inject(MesAuthService);\n const httpClient = inject(HttpClient);\n const router = inject(Router);\n const ngZone = inject(NgZone);\n mesAuthService.init(config, httpClient, router, ngZone);\n })\n ]);\n}\n\nexport interface IUser {\n userId?: string;\n userName?: string;\n fullName?: string;\n gender?: string;\n email?: string;\n phoneNumber?: string;\n department?: string;\n position?: string;\n tokenVersion?: string;\n permEndpoint?: string;\n perms?: Set<string>;\n employeeCode?: string;\n avatarPath?: string;\n loginMethod?: number;\n hrFullNameVn?: string;\n hrFullNameEn?: string;\n hrPosition?: string;\n hrJobTitle?: string;\n hrGender?: string;\n hrMobile?: string;\n hrEmail?: string;\n hrJoinDate?: string;\n hrBirthDate?: string;\n hrWorkStatus?: string;\n hrDoiTuong?: string;\n hrTeamCode?: string;\n hrLineCode?: string;\n}\n\nexport enum NotificationType {\n Info = 'Info',\n Warning = 'Warning',\n Error = 'Error',\n Success = 'Success'\n}\n\nexport interface NotificationDto {\n id: string;\n title: string;\n message: string;\n messageHtml?: string;\n url?: string;\n type: NotificationType;\n isRead: boolean;\n createdAt: string;\n sourceAppName: string;\n sourceAppIconUrl?: string;\n}\n\nexport interface FrontEndRoute {\n id: number;\n roleId: string;\n roleName: string;\n routePath: string;\n routeName: string;\n description?: string;\n icon?: string;\n cssClass?: string;\n parentId?: number | null;\n sortOrder: number;\n isLabel: boolean;\n isActive: boolean;\n createdAt: string;\n updatedAt?: string;\n children: FrontEndRoute[];\n}\n\nexport interface UserFrontEndRoutesGrouped {\n appId: string;\n appName: string;\n feUrl?: string;\n routes: FrontEndRoute[];\n}\n\nexport interface FrontEndRouteMaster {\n id: number;\n appId: string;\n routePath: string;\n routeName: string;\n description?: string;\n icon?: string;\n cssClass?: string;\n parentId?: number | null;\n sortOrder: number;\n isLabel: boolean;\n isActive: boolean;\n createdAt: string;\n updatedAt?: string;\n}\n\nexport interface CreateFrontEndRouteDto {\n routePath: string;\n routeName: string;\n description?: string;\n icon?: string;\n cssClass?: string;\n parentId?: number | null;\n sortOrder?: number;\n isLabel?: boolean;\n}\n\nexport interface PagedList<T> {\n items: T[];\n totalCount: number;\n page: number;\n pageSize: number;\n totalPages: number;\n hasNext: boolean;\n hasPrevious: boolean;\n}\n\nexport interface RealTimeNotificationDto {\n id: string;\n title: string;\n message: string;\n messageHtml?: string;\n url?: string;\n type: NotificationType;\n createdAt: string;\n sourceAppName: string;\n sourceAppIconUrl?: string;\n}\n\n@Injectable()\nexport class MesAuthService {\n private hubConnection: HubConnection | null = null;\n private _currentUser = new BehaviorSubject<IUser | null>(null);\n public currentUser$: Observable<IUser | null> = this._currentUser.asObservable();\n private _notifications = new Subject<any>();\n public notifications$: Observable<any> = this._notifications.asObservable();\n\n private apiBase = '';\n private config: MesAuthConfig | null = null;\n private http!: HttpClient;\n private router?: Router;\n private ngZone: NgZone | null = null;\n\n constructor() {\n // Empty constructor - all dependencies passed to init()\n }\n\n init(config: MesAuthConfig, httpClient: HttpClient, router?: Router, ngZone?: NgZone) {\n this.config = config;\n this.http = httpClient;\n this.router = router;\n this.ngZone = ngZone ?? null;\n this.apiBase = config.apiBaseUrl.replace(/\\/$/, '');\n\n // Fetch user once on init. Route changes do NOT re-fetch the user.\n // Auth state is maintained via cookies; 401 errors are handled by HTTP interceptors.\n // SignalR handles real-time notification delivery without polling.\n this.fetchCurrentUser().subscribe();\n }\n\n getConfig(): MesAuthConfig | null {\n return this.config;\n }\n\n private fetchCurrentUser(): Observable<any> {\n if (!this.apiBase) return EMPTY;\n const url = `${this.apiBase}/auth/me`;\n return this.http.get(url, { withCredentials: this.config?.withCredentials ?? true }).pipe(\n tap((u) => {\n this._currentUser.next(u);\n if (u && this.config) {\n this.startConnection(this.config);\n }\n }),\n catchError((err) => {\n // Silently handle auth errors (401/403) - user is not logged in\n if (err.status === 401 || err.status === 403) {\n this._currentUser.next(null);\n }\n return of(null);\n })\n );\n }\n\n public getUnreadCount(): Observable<any> {\n return this.http.get(`${this.apiBase}/notif/me/unread-count`, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n public getNotifications(page: number = 1, pageSize: number = 20, includeRead: boolean = false, type?: string): Observable<any> {\n let url = `${this.apiBase}/notif/me?page=${page}&pageSize=${pageSize}&includeRead=${includeRead}`;\n if (type) {\n url += `&type=${type}`;\n }\n return this.http.get(url, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n public markAsRead(notificationId: string): Observable<any> {\n return this.http.patch(`${this.apiBase}/notif/${notificationId}/read`, {}, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n public markAllAsRead(): Observable<any> {\n return this.http.patch(`${this.apiBase}/notif/me/read-all`, {}, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n public deleteNotification(notificationId: string): Observable<any> {\n return this.http.delete(`${this.apiBase}/notif/${notificationId}`, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n /**\n * Get frontend routes assigned to the current user\n * Returns routes grouped by application\n */\n public getFrontEndRoutes(): Observable<UserFrontEndRoutesGrouped[]> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.get<UserFrontEndRoutesGrouped[]>(`${this.apiBase}/fe-routes/me`, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n /**\n * Get master routes for a specific application\n * @param appId - The application ID\n */\n public getRouteMasters(appId: string): Observable<FrontEndRouteMaster[]> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.get<FrontEndRouteMaster[]>(`${this.apiBase}/fe-routes/masters/${appId}`, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n /**\n * Register/sync frontend routes for an application\n * This is typically called on app startup to sync routes from the frontend app\n * @param appId - The application ID (passed via X-App-Id header)\n * @param routes - Array of route definitions\n */\n public registerFrontEndRoutes(appId: string, routes: CreateFrontEndRouteDto[]): Observable<any> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n const headers = { 'X-App-Id': appId };\n return this.http.post(`${this.apiBase}/fe-routes/register`, routes, {\n headers,\n withCredentials: this.config?.withCredentials ?? true\n });\n }\n\n /**\n * Create a new route master\n * @param appId - The application ID\n * @param route - Route details\n */\n public createRouteMaster(appId: string, route: CreateFrontEndRouteDto): Observable<FrontEndRouteMaster> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.post<FrontEndRouteMaster>(`${this.apiBase}/fe-routes/masters`, {\n appId,\n ...route\n }, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n /**\n * Update an existing route master\n * @param routeId - The route master ID\n * @param route - Updated route details\n */\n public updateRouteMaster(routeId: number, route: Partial<CreateFrontEndRouteDto> & { isActive?: boolean }): Observable<FrontEndRouteMaster> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.put<FrontEndRouteMaster>(`${this.apiBase}/fe-routes/masters/${routeId}`, route, {\n withCredentials: this.config?.withCredentials ?? true\n });\n }\n\n /**\n * Delete a route master\n * @param routeId - The route master ID\n */\n public deleteRouteMaster(routeId: number): Observable<any> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.delete(`${this.apiBase}/fe-routes/masters/${routeId}`, {\n withCredentials: this.config?.withCredentials ?? true\n });\n }\n\n /**\n * Assign a route to a role\n * @param routeMasterId - The route master ID\n * @param roleId - The role ID (GUID)\n */\n public assignRouteToRole(routeMasterId: number, roleId: string): Observable<any> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.post(`${this.apiBase}/fe-routes/mappings`, {\n routeMasterId,\n roleId\n }, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n /**\n * Remove a route assignment from a role\n * @param mappingId - The mapping ID\n */\n public removeRouteFromRole(mappingId: number): Observable<any> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.delete(`${this.apiBase}/fe-routes/mappings/${mappingId}`, {\n withCredentials: this.config?.withCredentials ?? true\n });\n }\n\n /**\n * Get route-to-role mappings for a specific role\n * @param roleId - The role ID (GUID)\n */\n public getRouteMappingsByRole(roleId: string): Observable<any[]> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.get<any[]>(`${this.apiBase}/fe-routes/mappings?roleId=${roleId}`, {\n withCredentials: this.config?.withCredentials ?? true\n });\n }\n\n private startConnection(config: MesAuthConfig) {\n if (this.hubConnection) return;\n const signalrUrl = config.apiBaseUrl.replace(/\\/$/, '') + '/hub/notification';\n const builder = new HubConnectionBuilder()\n .withUrl(signalrUrl, { withCredentials: config.withCredentials ?? true })\n .withAutomaticReconnect()\n .configureLogging(LogLevel.Warning);\n\n this.hubConnection = builder.build();\n\n this.hubConnection.on('ReceiveNotification', (n: any) => {\n if (this.ngZone) {\n this.ngZone.run(() => this._notifications.next(n));\n } else {\n this._notifications.next(n);\n }\n });\n\n this.hubConnection.start().then(() => {}).catch((err) => {});\n\n this.hubConnection.onclose(() => {});\n this.hubConnection.onreconnecting(() => {});\n this.hubConnection.onreconnected(() => {});\n }\n\n public stop() {\n if (!this.hubConnection) return;\n this.hubConnection.stop().catch(() => {});\n this.hubConnection = null;\n }\n\n public logout(): Observable<any> {\n const url = `${this.apiBase}/auth/logout`;\n return this.http.post(url, {}, { withCredentials: this.config?.withCredentials ?? true }).pipe(\n tap(() => {\n this._currentUser.next(null);\n this.stop();\n })\n );\n }\n\n public get currentUser(): IUser | null {\n return this._currentUser.value;\n }\n\n public get isAuthenticated(): boolean {\n return this._currentUser.value !== null;\n }\n\n /**\n * Refreshes the current user from the server.\n * Returns an Observable that completes when the user data is loaded.\n * Callers can subscribe to wait for completion before proceeding (e.g., navigating after login).\n */\n public refreshUser(): Observable<any> {\n return this.fetchCurrentUser();\n }\n}\n","import { inject } from '@angular/core';\nimport { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';\nimport { throwError, timer } from 'rxjs';\nimport { catchError, switchMap } from 'rxjs/operators';\nimport { Router } from '@angular/router';\nimport { MesAuthService } from './mes-auth.service';\n\n// Track if we're currently redirecting to prevent loopback\nlet isRedirecting = false;\n\n/**\n * Functional HTTP interceptor for handling 401/403 auth errors.\n * Redirects to login page on 401, and to 403 page on 403.\n * Includes loopback prevention to avoid infinite redirects.\n */\nexport const mesAuthInterceptor: HttpInterceptorFn = (req, next) => {\n const authService = inject(MesAuthService);\n const router = inject(Router);\n\n return next(req).pipe(\n catchError((error: HttpErrorResponse) => {\n const status = error.status;\n\n // Check if we should handle this error and prevent loopback\n if ((status === 401 || status === 403) && !isRedirecting) {\n const config = authService.getConfig();\n const baseUrl = config?.userBaseUrl || '';\n\n const currentUrl = router.url + (window.location.hash || '');\n const returnUrl = encodeURIComponent(currentUrl);\n\n // Avoid loops if already on auth/unauth pages\n const isLoginPage = currentUrl.includes('/login');\n const is403Page = currentUrl.includes('/403');\n const isAuthPage = currentUrl.includes('/auth');\n // Public pages that should never trigger a 401 redirect (e.g., register, password reset)\n const isPublicPage = currentUrl.includes('/register')\n || currentUrl.includes('/forgot-password')\n || currentUrl.includes('/reset-password');\n // Skip redirect for the initial /auth/me check (app startup when not logged in)\n const isMeAuthPage = req.url.includes('/auth/me');\n\n if (status === 401 && !isLoginPage && !isAuthPage && !isMeAuthPage && !isPublicPage) {\n // Wait 1.5s for the concurrent refresh's Set-Cookie to be processed, then retry once.\n // If retry also gets 401, redirect to login.\n return timer(1500).pipe(\n switchMap(() => next(req)),\n catchError((retryError: HttpErrorResponse) => {\n if (retryError.status === 401) {\n isRedirecting = true;\n setTimeout(() => { isRedirecting = false; }, 5000);\n window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;\n }\n return throwError(() => retryError);\n })\n );\n } else if (status === 403 && !is403Page) {\n isRedirecting = true;\n setTimeout(() => { isRedirecting = false; }, 5000);\n let redirectUrl = `${baseUrl}/403?returnUrl=${returnUrl}`;\n if (error.error && error.error.required) {\n redirectUrl += `&required=${encodeURIComponent(error.error.required)}`;\n }\n window.location.href = redirectUrl;\n }\n }\n return throwError(() => error);\n })\n );\n};\n","import { NgModule } from '@angular/core';\nimport { MesAuthService } from './mes-auth.service';\n\n@NgModule({\n providers: [\n MesAuthService\n ]\n})\nexport class MesAuthModule {}\n","import { Injectable, OnDestroy } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\nexport type Theme = 'light' | 'dark';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ThemeService implements OnDestroy {\n private _currentTheme = new BehaviorSubject<Theme>('light');\n public currentTheme$: Observable<Theme> = this._currentTheme.asObservable();\n private observer: MutationObserver | null = null;\n\n constructor() {\n this.detectTheme();\n this.startWatching();\n }\n\n ngOnDestroy(): void {\n this.stopWatching();\n }\n\n private detectTheme(): void {\n const html = document.documentElement;\n const isDark = html.classList.contains('dark') ||\n html.getAttribute('data-theme') === 'dark' ||\n html.getAttribute('theme') === 'dark' ||\n html.getAttribute('data-coreui-theme') === 'dark';\n\n this._currentTheme.next(isDark ? 'dark' : 'light');\n }\n\n private startWatching(): void {\n if (typeof MutationObserver === 'undefined') {\n // Fallback for older browsers - check periodically\n setInterval(() => this.detectTheme(), 1000);\n return;\n }\n\n this.observer = new MutationObserver(() => {\n this.detectTheme();\n });\n\n this.observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'data-theme', 'theme', 'data-coreui-theme']\n });\n }\n\n private stopWatching(): void {\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n }\n\n get currentTheme(): Theme {\n return this._currentTheme.value;\n }\n\n // Method to manually set theme if needed\n setTheme(theme: Theme): void {\n this._currentTheme.next(theme);\n }\n\n // Re-detect theme from DOM\n refreshTheme(): void {\n this.detectTheme();\n }\n}","import { Component, OnInit, OnDestroy, Output, EventEmitter, HostBinding, HostListener, signal, ChangeDetectorRef } from '@angular/core';\nimport { NgIf } from '@angular/common';\nimport { Router } from '@angular/router';\nimport { MesAuthService, IUser } from './mes-auth.service';\nimport { ThemeService, Theme } from './theme.service';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n@Component({\n selector: 'ma-user-profile',\n standalone: true,\n imports: [NgIf],\n template: `\n <div class=\"user-profile-container\">\n <!-- Not logged in -->\n <ng-container *ngIf=\"!currentUser()\">\n <button class=\"login-btn\" (click)=\"onLogin()\">Login</button>\n </ng-container>\n\n <!-- Logged in -->\n <ng-container *ngIf=\"currentUser()\">\n <div class=\"user-header\">\n <!-- Notification Bell -->\n <button class=\"notification-btn\" [class.has-unread]=\"unreadCount > 0\" (click)=\"onNotificationClick()\" title=\"Notifications\">\n <svg class=\"bell-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/>\n <path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <span class=\"badge\" *ngIf=\"unreadCount > 0\">{{ unreadCount > 99 ? '99+' : unreadCount }}</span>\n </button>\n\n <!-- User Avatar + Dropdown -->\n <div class=\"user-menu-wrapper\">\n <button class=\"user-menu-btn\" (click)=\"toggleDropdown()\">\n <div class=\"avatar-ring\" [class.active]=\"dropdownOpen\">\n <img\n *ngIf=\"currentUser().fullName || currentUser().userName\"\n [src]=\"getAvatarUrl(currentUser())\"\n [alt]=\"currentUser().fullName || currentUser().userName\"\n class=\"avatar\"\n />\n <span *ngIf=\"!(currentUser().fullName || currentUser().userName)\" class=\"avatar-initial\">\n {{ getLastNameInitial(currentUser()) }}\n </span>\n </div>\n </button>\n\n <div class=\"mes-dropdown-menu\" *ngIf=\"dropdownOpen\">\n <!-- User info header -->\n <div class=\"mes-dropdown-header\">\n <div class=\"dropdown-avatar-wrap\">\n <img\n *ngIf=\"currentUser().fullName || currentUser().userName\"\n [src]=\"getAvatarUrl(currentUser())\"\n [alt]=\"currentUser().fullName || currentUser().userName\"\n class=\"dropdown-avatar\"\n />\n <span *ngIf=\"!(currentUser().fullName || currentUser().userName)\" class=\"dropdown-avatar-initial\">\n {{ getLastNameInitial(currentUser()) }}\n </span>\n </div>\n <div class=\"dropdown-user-info\">\n <span class=\"dropdown-user-name\">{{ currentUser().fullName || currentUser().userName }}</span>\n <span class=\"dropdown-user-sub\" *ngIf=\"currentUser().position || currentUser().department\">\n {{ currentUser().position || currentUser().department }}\n </span>\n </div>\n </div>\n\n <div class=\"mes-dropdown-divider\"></div>\n\n <button class=\"mes-dropdown-item profile-link\" (click)=\"onViewProfile()\">\n <svg class=\"item-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\"/><circle cx=\"12\" cy=\"7\" r=\"4\"/>\n </svg>\n View Profile\n </button>\n\n <div class=\"mes-dropdown-divider\"></div>\n\n <button class=\"mes-dropdown-item logout-item\" (click)=\"onLogout()\">\n <svg class=\"item-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\"/><polyline points=\"16 17 21 12 16 7\"/><line x1=\"21\" y1=\"12\" x2=\"9\" y2=\"12\"/>\n </svg>\n Logout\n </button>\n </div>\n </div>\n </div>\n </ng-container>\n </div>\n `,\n styles: [`\n :host {\n --primary-color: #1976d2;\n --primary-hover: #1565c0;\n --primary-light: rgba(25, 118, 210, 0.12);\n --primary-glow: rgba(25, 118, 210, 0.3);\n --error-color: #f44336;\n --error-light: rgba(244, 67, 54, 0.1);\n --text-primary: #212121;\n --text-secondary: #616161;\n --text-muted: #9e9e9e;\n --bg-primary: #ffffff;\n --bg-secondary: #f8f9fa;\n --bg-hover: #f0f4ff;\n --border-color: #e0e0e0;\n --shadow: rgba(0, 0, 0, 0.12);\n --shadow-lg: rgba(0, 0, 0, 0.18);\n }\n\n :host(.theme-dark) {\n --primary-color: #90caf9;\n --primary-hover: #64b5f6;\n --primary-light: rgba(144, 202, 249, 0.12);\n --primary-glow: rgba(144, 202, 249, 0.25);\n --error-color: #ef5350;\n --error-light: rgba(239, 83, 80, 0.12);\n --text-primary: #e0e0e0;\n --text-secondary: #b0b0b0;\n --text-muted: #757575;\n --bg-primary: #1e1e2e;\n --bg-secondary: #27273a;\n --bg-hover: #2a2d4a;\n --border-color: #383850;\n --shadow: rgba(0, 0, 0, 0.35);\n --shadow-lg: rgba(0, 0, 0, 0.5);\n }\n\n .user-profile-container {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .login-btn {\n padding: 7px 18px;\n background-color: var(--primary-color);\n color: white;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n font-weight: 500;\n font-size: 13px;\n letter-spacing: 0.2px;\n transition: background-color 0.2s, transform 0.15s;\n }\n\n .login-btn:hover {\n background-color: var(--primary-hover);\n transform: translateY(-1px);\n }\n\n .user-header {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n /* ── Bell button ── */\n .notification-btn {\n position: relative;\n background: none;\n border: none;\n cursor: pointer;\n padding: 8px;\n border-radius: 10px;\n color: var(--text-secondary);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.2s, background-color 0.2s;\n }\n\n .notification-btn:hover {\n background-color: var(--primary-light);\n color: var(--primary-color);\n }\n\n .notification-btn.has-unread {\n color: var(--primary-color);\n }\n\n .bell-icon {\n display: block;\n transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n .notification-btn:hover .bell-icon {\n transform: rotate(-20deg) scale(1.15);\n }\n\n .badge {\n position: absolute;\n top: 2px;\n right: 2px;\n background-color: var(--error-color);\n color: white;\n border-radius: 10px;\n min-width: 17px;\n height: 17px;\n padding: 0 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n line-height: 1;\n box-shadow: 0 0 0 2px var(--bg-primary);\n animation: badge-pop 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n @keyframes badge-pop {\n from { transform: scale(0); }\n to { transform: scale(1); }\n }\n\n /* ── Avatar ── */\n .user-menu-wrapper {\n position: relative;\n }\n\n .user-menu-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 2px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.2s;\n }\n\n .user-menu-btn:hover {\n transform: scale(1.06);\n }\n\n .avatar-ring {\n border-radius: 50%;\n padding: 2px;\n border: 2px solid transparent;\n transition: border-color 0.25s, box-shadow 0.25s;\n }\n\n .avatar-ring.active,\n .user-menu-btn:hover .avatar-ring {\n border-color: var(--primary-color);\n box-shadow: 0 0 0 3px var(--primary-glow);\n }\n\n .avatar {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n object-fit: cover;\n display: block;\n }\n\n .avatar-initial {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n font-size: 15px;\n }\n\n /* ── Dropdown ── */\n .mes-dropdown-menu {\n position: absolute;\n top: calc(100% + 10px);\n right: 0;\n background: var(--bg-primary);\n border: 1px solid var(--border-color);\n border-radius: 14px;\n box-shadow: 0 8px 32px var(--shadow-lg), 0 2px 8px var(--shadow);\n min-width: 220px;\n z-index: 1000;\n overflow: hidden;\n animation: dropdown-in 0.16s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n @keyframes dropdown-in {\n from { opacity: 0; transform: translateY(-8px) scale(0.96); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n }\n\n .mes-dropdown-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n background: var(--bg-secondary);\n }\n\n .dropdown-avatar-wrap {\n flex-shrink: 0;\n }\n\n .dropdown-avatar {\n width: 46px;\n height: 46px;\n border-radius: 50%;\n object-fit: cover;\n border: 2px solid var(--primary-color);\n display: block;\n }\n\n .dropdown-avatar-initial {\n width: 46px;\n height: 46px;\n border-radius: 50%;\n background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n font-size: 18px;\n border: 2px solid var(--primary-color);\n }\n\n .dropdown-user-info {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n }\n\n .dropdown-user-name {\n font-weight: 600;\n font-size: 14px;\n color: var(--text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .dropdown-user-sub {\n font-size: 11px;\n color: var(--primary-color);\n font-weight: 500;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .mes-dropdown-divider {\n height: 1px;\n background: var(--border-color);\n }\n\n .mes-dropdown-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 11px 16px;\n border: none;\n background: none;\n text-align: left;\n cursor: pointer;\n font-size: 13.5px;\n font-weight: 500;\n transition: background-color 0.15s;\n }\n\n .item-icon {\n flex-shrink: 0;\n opacity: 0.8;\n }\n\n .profile-link {\n color: var(--primary-color);\n }\n\n .profile-link:hover {\n background-color: var(--primary-light);\n }\n\n .logout-item {\n color: var(--error-color);\n }\n\n .logout-item:hover {\n background-color: var(--error-light);\n }\n\n @media (max-width: 768px) {\n .avatar, .avatar-initial { width: 32px; height: 32px; font-size: 13px; }\n }\n `]\n})\nexport class UserProfileComponent implements OnInit, OnDestroy {\n @Output() notificationClick = new EventEmitter<void>();\n @HostBinding('class') get themeClass(): string {\n return `theme-${this.currentTheme}`;\n }\n\n currentUser = signal<IUser | null>(null);\n currentTheme: Theme = 'light';\n unreadCount = 0;\n dropdownOpen = false;\n private hasUser = false;\n private destroy$ = new Subject<void>();\n\n // Signal to force avatar refresh\n avatarRefresh = signal<number>(Date.now());\n\n constructor(private authService: MesAuthService, private router: Router, private themeService: ThemeService, private cdr: ChangeDetectorRef) {}\n\n ngOnInit() {\n this.authService.currentUser$\n .pipe(takeUntil(this.destroy$))\n .subscribe(user => {\n this.currentUser.set(user);\n this.hasUser = !!user;\n // Force avatar refresh when user changes\n this.avatarRefresh.set(Date.now());\n if (!this.hasUser) {\n this.unreadCount = 0;\n } else {\n this.loadUnreadCount();\n }\n this.cdr.markForCheck();\n });\n\n this.themeService.currentTheme$\n .pipe(takeUntil(this.destroy$))\n .subscribe(theme => {\n this.currentTheme = theme;\n });\n\n // Listen for new real-time notifications (SignalR only)\n this.authService.notifications$\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n if (this.hasUser) {\n this.loadUnreadCount();\n }\n });\n }\n\n ngOnDestroy() {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n loadUnreadCount() {\n if (!this.hasUser) {\n this.unreadCount = 0;\n return;\n }\n\n this.authService.getUnreadCount().subscribe({\n next: (response: any) => {\n this.unreadCount = response.unreadCount || 0;\n },\n error: (err) => {}\n });\n }\n\n getAvatarUrl(user: IUser): string {\n // Use the refresh signal to force update\n const refresh = this.avatarRefresh();\n const config = this.authService.getConfig();\n const baseUrl = config?.apiBaseUrl || '';\n \n // If user has avatarPath, use it directly\n if (user.avatarPath) {\n // If avatarPath is already a full URL, use it as-is\n if (user.avatarPath.startsWith('http://') || user.avatarPath.startsWith('https://')) {\n return user.avatarPath;\n }\n // If it's a relative path, construct full URL with refresh timestamp\n return `${baseUrl.replace(/\\/$/, '')}${user.avatarPath}?t=${refresh}`;\n }\n\n // Fallback: construct URL using userId\n const userId = user.userId;\n if (userId && baseUrl) {\n return `${baseUrl.replace(/\\/$/, '')}/auth/${userId}/avatar?t=${refresh}`;\n }\n \n // Fallback to UI avatars service if no userId or baseUrl\n const displayName = user.userName || user.userId || 'User';\n return `https://ui-avatars.com/api/?name=${encodeURIComponent(displayName)}&background=1976d2&color=fff`;\n }\n\n getLastNameInitial(user: IUser): string {\n const fullName = user.fullName || user.userName || 'U';\n const parts = fullName.split(' ');\n const lastPart = parts[parts.length - 1];\n return lastPart.charAt(0).toUpperCase();\n }\n\n toggleDropdown() {\n this.dropdownOpen = !this.dropdownOpen;\n }\n\n @HostListener('document:click', ['$event'])\n onDocumentClick(event: Event) {\n const target = event.target as HTMLElement;\n const clickedInside = target.closest('.user-menu-wrapper');\n if (!clickedInside) {\n this.dropdownOpen = false;\n }\n }\n\n onLogin() {\n const config = this.authService.getConfig();\n const baseUrl = config?.userBaseUrl || '';\n const returnUrl = encodeURIComponent(this.router.url);\n window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;\n }\n\n onViewProfile() {\n this.router.navigate(['/profile']);\n this.dropdownOpen = false;\n }\n\n onLogout() {\n this.authService.logout().subscribe({\n next: () => {\n // Clear current user after successful logout\n this.dropdownOpen = false;\n \n // Navigate to login with return URL\n const config = this.authService.getConfig();\n const baseUrl = config?.userBaseUrl || '';\n const returnUrl = encodeURIComponent(window.location.href);\n window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;\n },\n error: (err) => {\n // Still navigate to login even if logout fails\n const config = this.authService.getConfig();\n const baseUrl = config?.userBaseUrl || '';\n window.location.href = `${baseUrl}/login`;\n }\n });\n }\n\n onNotificationClick() {\n this.notificationClick.emit();\n }\n}\n\n","import { Injectable } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\nexport interface Toast {\n id: string;\n message: string;\n title?: string;\n type: 'info' | 'success' | 'warning' | 'error';\n duration?: number;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class ToastService {\n private toasts$ = new BehaviorSubject<Toast[]>([]);\n public toasts: Observable<Toast[]> = this.toasts$.asObservable();\n\n show(message: string, title?: string, type: 'info' | 'success' | 'warning' | 'error' = 'info', duration: number = 5000) {\n const id = Math.random().toString(36).substr(2, 9);\n const toast: Toast = {\n id,\n message,\n title,\n type,\n duration\n };\n\n const currentToasts = this.toasts$.value;\n this.toasts$.next([...currentToasts, toast]);\n\n if (duration > 0) {\n setTimeout(() => {\n this.remove(id);\n }, duration);\n }\n\n return id;\n }\n\n remove(id: string) {\n const currentToasts = this.toasts$.value;\n this.toasts$.next(currentToasts.filter(t => t.id !== id));\n }\n\n clear() {\n this.toasts$.next([]);\n }\n}\n","import { Component, OnInit, OnDestroy, HostBinding } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { ToastService, Toast } from './toast.service';\nimport { ThemeService, Theme } from './theme.service';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n@Component({\n selector: 'ma-toast-container',\n standalone: true,\n imports: [CommonModule],\n template: `\n <div class=\"toast-container\">\n <div *ngFor=\"let toast of toasts\" class=\"toast toast-{{ toast.type }}\">\n\n <!-- Type icon -->\n <div class=\"toast-icon\">\n <svg *ngIf=\"toast.type === 'info'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/><path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <svg *ngIf=\"toast.type === 'success'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\"/><polyline points=\"22 4 12 14.01 9 11.01\"/>\n </svg>\n <svg *ngIf=\"toast.type === 'warning'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/>\n </svg>\n <svg *ngIf=\"toast.type === 'error'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/>\n </svg>\n </div>\n\n <!-- Content -->\n <div class=\"toast-content\">\n <div *ngIf=\"toast.title\" class=\"toast-title\">{{ toast.title }}</div>\n <div class=\"toast-message\" [innerHTML]=\"toast.message\"></div>\n </div>\n\n <!-- Close -->\n <button class=\"toast-close\" (click)=\"close(toast.id)\" aria-label=\"Close\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n\n <!-- Auto-dismiss progress bar -->\n <div class=\"toast-progress\" [style.animation-duration]=\"(toast.duration || 5000) + 'ms'\"></div>\n </div>\n </div>\n `,\n styles: [`\n :host {\n --info-color: #2196f3;\n --info-bg: rgba(33, 150, 243, 0.1);\n --success-color: #43a047;\n --success-bg: rgba(67, 160, 71, 0.1);\n --warning-color: #f57c00;\n --warning-bg: rgba(245, 124, 0, 0.1);\n --error-color: #e53935;\n --error-bg: rgba(229, 57, 53, 0.1);\n --text-primary: #212121;\n --text-secondary: #757575;\n --bg-primary: #ffffff;\n --border-color: rgba(0, 0, 0, 0.08);\n --shadow: rgba(0, 0, 0, 0.1);\n --shadow-lg: rgba(0, 0, 0, 0.18);\n }\n\n :host(.theme-dark) {\n --info-color: #64b5f6;\n --info-bg: rgba(100, 181, 246, 0.12);\n --success-color: #66bb6a;\n --success-bg: rgba(102, 187, 106, 0.12);\n --warning-color: #ffb74d;\n --warning-bg: rgba(255, 183, 77, 0.12);\n --error-color: #ef5350;\n --error-bg: rgba(239, 83, 80, 0.12);\n --text-primary: #e0e0e0;\n --text-secondary: #9e9e9e;\n --bg-primary: #1e1e2e;\n --border-color: rgba(255, 255, 255, 0.08);\n --shadow: rgba(0, 0, 0, 0.35);\n --shadow-lg: rgba(0, 0, 0, 0.5);\n }\n\n .toast-container {\n position: fixed;\n top: 20px;\n right: 20px;\n z-index: 9999;\n pointer-events: none;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .toast {\n position: relative;\n display: flex;\n align-items: flex-start;\n gap: 11px;\n padding: 13px 13px 16px 16px;\n border-radius: 12px;\n background: var(--bg-primary);\n border: 1px solid var(--border-color);\n box-shadow: 0 8px 28px var(--shadow-lg), 0 2px 8px var(--shadow);\n pointer-events: auto;\n min-width: 300px;\n max-width: 420px;\n overflow: hidden;\n animation: toast-in 0.35s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n @keyframes toast-in {\n from { opacity: 0; transform: translateX(36px) scale(0.96); }\n to { opacity: 1; transform: translateX(0) scale(1); }\n }\n\n /* Left accent bar via ::before */\n .toast::before {\n content: '';\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 4px;\n border-radius: 12px 0 0 12px;\n }\n\n .toast-info::before { background: var(--info-color); }\n .toast-success::before { background: var(--success-color); }\n .toast-warning::before { background: var(--warning-color); }\n .toast-error::before { background: var(--error-color); }\n\n /* Type icon bubble */\n .toast-icon {\n flex-shrink: 0;\n width: 34px;\n height: 34px;\n border-radius: 9px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 2px;\n }\n\n .toast-info .toast-icon { color: var(--info-color); background: var(--info-bg); }\n .toast-success .toast-icon { color: var(--success-color); background: var(--success-bg); }\n .toast-warning .toast-icon { color: var(--warning-color); background: var(--warning-bg); }\n .toast-error .toast-icon { color: var(--error-color); background: var(--error-bg); }\n\n .toast-content {\n flex: 1;\n min-width: 0;\n padding-top: 1px;\n }\n\n .toast-title {\n font-weight: 700;\n font-size: 13.5px;\n margin-bottom: 3px;\n line-height: 1.3;\n }\n\n .toast-info .toast-title { color: var(--info-color); }\n .toast-success .toast-title { color: var(--success-color); }\n .toast-warning .toast-title { color: var(--warning-color); }\n .toast-error .toast-title { color: var(--error-color); }\n\n .toast-message {\n font-size: 12.5px;\n line-height: 1.45;\n color: var(--text-secondary);\n }\n\n .toast-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--text-secondary);\n width: 26px;\n height: 26px;\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n padding: 0;\n transition: color 0.15s, background-color 0.15s;\n }\n\n .toast-close:hover {\n color: var(--text-primary);\n background: var(--border-color);\n }\n\n /* Auto-dismiss progress bar */\n .toast-progress {\n position: absolute;\n bottom: 0;\n left: 4px;\n right: 0;\n height: 3px;\n border-radius: 0 0 12px 0;\n animation: toast-progress linear forwards;\n opacity: 0.7;\n }\n\n .toast-info .toast-progress { background: var(--info-color); }\n .toast-success .toast-progress { background: var(--success-color); }\n .toast-warning .toast-progress { background: var(--warning-color); }\n .toast-error .toast-progress { background: var(--error-color); }\n\n @keyframes toast-progress {\n from { width: calc(100% - 4px); }\n to { width: 0; }\n }\n\n @media (max-width: 600px) {\n .toast-container { top: 10px; right: 10px; left: 10px; }\n .toast { min-width: auto; max-width: 100%; }\n }\n `]\n})\nexport class ToastContainerComponent implements OnInit, OnDestroy {\n @HostBinding('class') get themeClass(): string {\n return `theme-${this.currentTheme}`;\n }\n\n toasts: Toast[] = [];\n currentTheme: Theme = 'light';\n private destroy$ = new Subject<void>();\n\n constructor(private toastService: ToastService, private themeService: ThemeService) {}\n\n ngOnInit() {\n this.toastService.toasts\n .pipe(takeUntil(this.destroy$))\n .subscribe(toasts => {\n this.toasts = toasts;\n });\n\n this.themeService.currentTheme$\n .pipe(takeUntil(this.destroy$))\n .subscribe(theme => {\n this.currentTheme = theme;\n });\n }\n\n ngOnDestroy() {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n close(id: string) {\n this.toastService.remove(id);\n }\n}\n","import { Component, OnInit, OnDestroy, HostBinding, Output, EventEmitter, inject } from '@angular/core';\nimport { NgIf, NgFor } from '@angular/common';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { MesAuthService, NotificationDto, PagedList, RealTimeNotificationDto } from './mes-auth.service';\nimport { ToastService } from './toast.service';\nimport { ThemeService, Theme } from './theme.service';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n@Component({\n selector: 'ma-notification-panel',\n standalone: true,\n imports: [NgIf, NgFor],\n template: `\n <div class=\"notification-panel\" [class.open]=\"isOpen\">\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"panel-header-left\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/>\n <path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <h3>Notifications</h3>\n </div>\n <button class=\"close-btn\" (click)=\"close()\" title=\"Close\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n\n <!-- Tabs -->\n <div class=\"tabs\">\n <button class=\"tab-btn\" [class.active]=\"activeTab === 'unread'\" (click)=\"switchTab('unread')\">\n Unread\n <span class=\"tab-count\" *ngIf=\"unreadNotifications.length > 0\">{{ unreadNotifications.length }}</span>\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeTab === 'read'\" (click)=\"switchTab('read')\">\n Read\n <span class=\"tab-count read-count\" *ngIf=\"readNotifications.length > 0\">{{ readNotifications.length }}</span>\n </button>\n </div>\n\n <!-- Notifications List -->\n <div class=\"notifications-list\">\n <ng-container *ngIf=\"currentNotifications.length > 0\">\n <div\n *ngFor=\"let notification of currentNotifications\"\n class=\"notification-item\"\n [class.unread]=\"!notification.isRead\"\n (click)=\"openDetails(notification)\"\n >\n <div class=\"notif-accent\"\n [class.type-info]=\"typeOf(notification) === 'Info'\"\n [class.type-success]=\"typeOf(notification) === 'Success'\"\n [class.type-warning]=\"typeOf(notification) === 'Warning'\"\n [class.type-error]=\"typeOf(notification) === 'Error'\"></div>\n <div class=\"notif-type-icon\"\n [class.type-info]=\"typeOf(notification) === 'Info'\"\n [class.type-success]=\"typeOf(notification) === 'Success'\"\n [class.type-warning]=\"typeOf(notification) === 'Warning'\"\n [class.type-error]=\"typeOf(notification) === 'Error'\">\n <svg *ngIf=\"typeOf(notification) === 'Info'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/><path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <svg *ngIf=\"typeOf(notification) === 'Success'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\"/>\n </svg>\n <svg *ngIf=\"typeOf(notification) === 'Warning'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/>\n </svg>\n <svg *ngIf=\"typeOf(notification) === 'Error'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/>\n </svg>\n </div>\n <div class=\"notification-content\">\n <div class=\"notification-title\">{{ notification.title }}</div>\n <div class=\"notification-message\">{{ getNotificationMessage(notification) }}</div>\n <div class=\"notification-meta\">\n <span class=\"app-name\">{{ notification.sourceAppName }}</span>\n <span class=\"time\">{{ dateLabels.get(notification.id) }}</span>\n </div>\n </div>\n <button class=\"icon-btn read-btn\" (click)=\"markAsRead(notification.id, $event)\" title=\"Mark as read\" *ngIf=\"!notification.isRead\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\"/>\n </svg>\n </button>\n <button class=\"icon-btn delete-btn\" (click)=\"delete(notification.id, $event)\" title=\"Delete\" *ngIf=\"notification.isRead\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\"/><path d=\"M10 11v6\"/><path d=\"M14 11v6\"/><path d=\"M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2\"/>\n </svg>\n </button>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"currentNotifications.length === 0\">\n <div class=\"empty-state\">\n <svg class=\"empty-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"44\" height=\"44\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/>\n <path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <p>No {{ activeTab }} notifications</p>\n </div>\n </ng-container>\n </div>\n\n <!-- Footer Actions -->\n <div class=\"panel-footer\" *ngIf=\"currentNotifications.length > 0\">\n <div class=\"footer-actions\" *ngIf=\"activeTab === 'unread'\">\n <button class=\"action-btn\" (click)=\"markAllAsRead()\" *ngIf=\"unreadNotifications.length > 0\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>\n Mark all read\n </button>\n <button class=\"action-btn delete-all-btn\" (click)=\"deleteAllUnread()\" *ngIf=\"unreadNotifications.length > 0\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\"/></svg>\n Delete all\n </button>\n </div>\n <button class=\"action-btn delete-all-btn\" (click)=\"deleteAllRead()\" *ngIf=\"activeTab === 'read' && readNotifications.length > 0\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\"/></svg>\n Delete all\n </button>\n </div>\n </div>\n\n <!-- Details Modal -->\n <div class=\"modal-overlay\" *ngIf=\"selectedNotification\" (click)=\"closeDetails()\">\n <div class=\"modal-container\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\"\n [class.modal-type-info]=\"typeOf(selectedNotification) === 'Info'\"\n [class.modal-type-success]=\"typeOf(selectedNotification) === 'Success'\"\n [class.modal-type-warning]=\"typeOf(selectedNotification) === 'Warning'\"\n [class.modal-type-error]=\"typeOf(selectedNotification) === 'Error'\">\n <div class=\"modal-header-left\">\n <div class=\"modal-type-icon\">\n <svg *ngIf=\"typeOf(selectedNotification) === 'Info'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/><path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <svg *ngIf=\"typeOf(selectedNotification) === 'Success'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\"/><polyline points=\"22 4 12 14.01 9 11.01\"/>\n </svg>\n <svg *ngIf=\"typeOf(selectedNotification) === 'Warning'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/>\n </svg>\n <svg *ngIf=\"typeOf(selectedNotification) === 'Error'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/>\n </svg>\n </div>\n <h3>{{ selectedNotification.title }}</h3>\n </div>\n <button class=\"close-btn\" (click)=\"closeDetails()\" title=\"Close\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"modal-meta\">\n <span class=\"app-name\">{{ selectedNotification.sourceAppName }}</span>\n <span class=\"time\">{{ selectedNotificationDate }}</span>\n </div>\n <div class=\"modal-body\" [innerHTML]=\"selectedNotificationHtml\"></div>\n <div class=\"modal-footer\">\n <button class=\"action-btn\" (click)=\"closeDetails()\">Close</button>\n </div>\n </div>\n </div>\n `,\n styles: [`\n :host {\n display: block;\n position: relative;\n --primary-color: #1976d2;\n --primary-hover: #1565c0;\n --primary-light: rgba(25, 118, 210, 0.1);\n --success-color: #43a047;\n --error-color: #f44336;\n --error-hover: #d32f2f;\n --unread-accent: #1976d2;\n --info-color: #2196f3;\n --info-bg: rgba(33, 150, 243, 0.1);\n --success-bg: rgba(67, 160, 71, 0.1);\n --warning-color: #f57c00;\n --warning-bg: rgba(245, 124, 0, 0.1);\n --error-bg: rgba(244, 67, 54, 0.1);\n --text-primary: #212121;\n --text-secondary: #616161;\n --text-muted: #9e9e9e;\n --bg-primary: #ffffff;\n --bg-secondary: #f8f9fa;\n --bg-hover: #f0f4ff;\n --bg-unread: rgba(25, 118, 210, 0.06);\n --border-color: #e0e0e0;\n --border-light: #eeeeee;\n --shadow: rgba(0, 0, 0, 0.15);\n }\n\n :host(.theme-dark) {\n display: block;\n position: relative;\n --primary-color: #90caf9;\n --primary-hover: #64b5f6;\n --primary-light: rgba(144, 202, 249, 0.1);\n --success-color: #66bb6a;\n --error-color: #ef5350;\n --error-hover: #c62828;\n --unread-accent: #90caf9;\n --info-color: #64b5f6;\n --info-bg: rgba(100, 181, 246, 0.12);\n --success-bg: rgba(102, 187, 106, 0.12);\n --warning-color: #ffb74d;\n --warning-bg: rgba(255, 183, 77, 0.12);\n --error-bg: rgba(239, 83, 80, 0.12);\n --text-primary: #e0e0e0;\n --text-secondary: #b0b0b0;\n --text-muted: #757575;\n --bg-primary: #1e1e2e;\n --bg-secondary: #27273a;\n --bg-hover: #2a2d4a;\n --bg-unread: rgba(144, 202, 249, 0.08);\n --border-color: #383850;\n --border-light: #2e2e42;\n --shadow: rgba(0, 0, 0, 0.4);\n }\n\n /* ── Panel ── */\n .notification-panel {\n position: fixed;\n top: 0;\n right: -360px;\n width: 360px;\n height: 100vh;\n background: var(--bg-primary);\n box-shadow: -4px 0 24px var(--shadow);\n display: flex;\n flex-direction: column;\n z-index: 1030;\n transition: right 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n .notification-panel.open {\n right: 0;\n }\n\n /* ── Header ── */\n .panel-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 18px;\n border-bottom: 1px solid var(--border-color);\n background: var(--bg-secondary);\n flex-shrink: 0;\n }\n\n .panel-header-left {\n display: flex;\n align-items: center;\n gap: 9px;\n color: var(--primary-color);\n }\n\n .panel-header h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 700;\n color: var(--text-primary);\n letter-spacing: 0.1px;\n }\n\n .close-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--text-muted);\n width: 32px;\n height: 32px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.2s, background-color 0.2s;\n }\n\n .close-btn:hover {\n color: var(--text-primary);\n background-color: var(--bg-hover);\n }\n\n /* ── Tabs ── */\n .tabs {\n display: flex;\n gap: 6px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--border-color);\n background: var(--bg-secondary);\n flex-shrink: 0;\n }\n\n .tab-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 7px 12px;\n background: none;\n border: 1px solid var(--border-color);\n border-radius: 20px;\n color: var(--text-secondary);\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n transition: all 0.18s;\n }\n\n .tab-btn:hover {\n background: var(--bg-hover);\n color: var(--primary-color);\n border-color: var(--primary-color);\n }\n\n .tab-btn.active {\n background: var(--primary-color);\n border-color: var(--primary-color);\n color: white;\n }\n\n .tab-count {\n background: rgba(255,255,255,0.25);\n border-radius: 10px;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n font-size: 10px;\n font-weight: 700;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .tab-btn:not(.active) .tab-count {\n background: var(--error-color);\n color: white;\n }\n\n .read-count {\n background: rgba(255,255,255,0.25);\n }\n\n .tab-btn:not(.active) .read-count {\n background: var(--text-muted);\n }\n\n /* ── Notifications list ── */\n .notifications-list {\n flex: 1;\n overflow-y: auto;\n }\n\n .notification-item {\n display: flex;\n align-items: flex-start;\n gap: 0;\n border-bottom: 1px solid var(--border-light);\n cursor: pointer;\n background: var(--bg-primary);\n transition: background-color 0.15s;\n position: relative;\n }\n\n .notification-item:hover {\n background: var(--bg-hover);\n }\n\n .notification-item.unread {\n background: var(--bg-unread);\n }\n\n /* Left accent bar — type-based color, dimmed for read */\n .notif-accent {\n width: 3px;\n align-self: stretch;\n flex-shrink: 0;\n background: transparent;\n border-radius: 0 2px 2px 0;\n opacity: 0.3;\n }\n\n .notification-item.unread .notif-accent {\n opacity: 1;\n }\n\n .notif-accent.type-info { background: var(--info-color); }\n .notif-accent.type-success { background: var(--success-color); }\n .notif-accent.type-warning { background: var(--warning-color); }\n .notif-accent.type-error { background: var(--error-color); }\n\n /* Small type icon bubble */\n .notif-type-icon {\n flex-shrink: 0;\n width: 26px;\n height: 26px;\n border-radius: 7px;\n display: flex;\n align-items: center;\n justify-content: center;\n align-self: center;\n margin-left: 10px;\n }\n\n .notif-type-icon.type-info { color: var(--info-color); background: var(--info-bg); }\n .notif-type-icon.type-success { color: var(--success-color); background: var(--success-bg); }\n .notif-type-icon.type-warning { color: var(--warning-color); background: var(--warning-bg); }\n .notif-type-icon.type-error { color: var(--error-color); background: var(--error-bg); }\n\n .notification-content {\n flex: 1;\n min-width: 0;\n padding: 12px 8px 12px 12px;\n }\n\n .notification-title {\n font-weight: 600;\n color: var(--text-primary);\n font-size: 13.5px;\n margin-bottom: 3px;\n line-height: 1.35;\n }\n\n .notification-message {\n color: var(--text-secondary);\n font-size: 12px;\n line-height: 1.45;\n margin-bottom: 7px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .notification-meta {\n display: flex;\n justify-content: space-between;\n font-size: 11px;\n color: var(--text-muted);\n }\n\n .app-name {\n font-weight: 600;\n color: var(--primary-color);\n }\n\n /* Icon action buttons */\n .icon-btn {\n background: none;\n border: none;\n cursor: pointer;\n width: 32px;\n height: 32px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n align-self: center;\n margin-right: 8px;\n transition: color 0.15s, background-color 0.15s;\n color: var(--text-muted);\n }\n\n .read-btn:hover {\n color: var(--success-color);\n background: rgba(67, 160, 71, 0.1);\n }\n\n .delete-btn:hover {\n color: var(--error-color);\n background: rgba(244, 67, 54, 0.1);\n }\n\n /* ── Empty state ── */\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n height: 100%;\n padding: 40px 20px;\n color: var(--text-muted);\n }\n\n .empty-icon {\n opacity: 0.35;\n }\n\n .empty-state p {\n margin: 0;\n font-size: 13px;\n }\n\n /* ── Footer ── */\n .panel-footer {\n padding: 10px 14px;\n border-top: 1px solid var(--border-color);\n background: var(--bg-secondary);\n flex-shrink: 0;\n }\n\n .footer-actions {\n display: flex;\n gap: 8px;\n }\n\n .footer-actions .action-btn {\n flex: 1;\n }\n\n .action-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 100%;\n padding: 8px 12px;\n background: var(--primary-color);\n color: white;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n font-size: 12.5px;\n font-weight: 600;\n transition: background-color 0.18s, transform 0.12s;\n }\n\n .action-btn:hover {\n background: var(--primary-hover);\n transform: translateY(-1px);\n }\n\n .delete-all-btn {\n background: var(--error-color);\n }\n\n .delete-all-btn:hover {\n background: var(--error-hover);\n }\n\n /* ── Modal ── */\n .modal-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1060;\n backdrop-filter: blur(2px);\n }\n\n .modal-container {\n background: var(--bg-primary);\n border-radius: 14px;\n width: 90%;\n max-width: 580px;\n max-height: 80vh;\n display: flex;\n flex-direction: column;\n box-shadow: 0 16px 48px rgba(0,0,0,0.25);\n animation: modal-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n @keyframes modal-in {\n from { opacity: 0; transform: scale(0.94) translateY(8px); }\n to { opacity: 1; transform: scale(1) translateY(0); }\n }\n\n .modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid var(--border-color);\n background: var(--bg-secondary);\n border-radius: 14px 14px 0 0;\n border-top: 3px solid transparent;\n }\n\n .modal-header.modal-type-info { border-top-color: var(--info-color); }\n .modal-header.modal-type-success { border-top-color: var(--success-color); }\n .modal-header.modal-type-warning { border-top-color: var(--warning-color); }\n .modal-header.modal-type-error { border-top-color: var(--error-color); }\n\n .modal-header-left {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n }\n\n .modal-type-icon {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .modal-type-info .modal-type-icon { color: var(--info-color); background: var(--info-bg); }\n .modal-type-success .modal-type-icon { color: var(--success-color); background: var(--success-bg); }\n .modal-type-warning .modal-type-icon { color: var(--warning-color); background: var(--warning-bg); }\n .modal-type-error .modal-type-icon { color: var(--error-color); background: var(--error-bg); }\n\n .modal-header h3 {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .modal-meta {\n display: flex;\n justify-content: space-between;\n padding: 8px 20px;\n font-size: 11.5px;\n color: var(--text-muted);\n border-bottom: 1px solid var(--border-light);\n }\n\n .modal-body {\n padding: 20px;\n overflow-y: auto;\n flex: 1;\n color: var(--text-primary);\n font-size: 14px;\n line-height: 1.65;\n }\n\n .modal-footer {\n padding: 12px 20px;\n border-top: 1px solid var(--border-color);\n background: var(--bg-secondary);\n border-radius: 0 0 14px 14px;\n display: flex;\n justify-content: flex-end;\n }\n\n .modal-footer .action-btn {\n width: auto;\n padding: 8px 24px;\n }\n\n @media (max-width: 600px) {\n .notification-panel { width: 100%; right: -100%; }\n .modal-container { width: 95%; max-height: 90vh; }\n }\n `]\n})\nexport class NotificationPanelComponent implements OnInit, OnDestroy {\n @Output() notificationRead = new EventEmitter<void>();\n @HostBinding('class') get themeClass(): string {\n return `theme-${this.currentTheme}`;\n }\n\n isOpen = false;\n notifications: NotificationDto[] = [];\n currentTheme: Theme = 'light';\n activeTab: 'unread' | 'read' = 'unread'; // Default to unread tab\n private destroy$ = new Subject<void>();\n\n // Cached filtered lists — updated explicitly to avoid re-filtering on every CD cycle\n private _unreadNotifications: NotificationDto[] = [];\n private _readNotifications: NotificationDto[] = [];\n\n // Stable time-ago strings keyed by notification id — refreshed every 30s\n dateLabels: Map<string, string> = new Map();\n private dateTimer: ReturnType<typeof setInterval> | null = null;\n\n get unreadNotifications(): NotificationDto[] {\n return this._unreadNotifications;\n }\n\n get readNotifications(): NotificationDto[] {\n return this._readNotifications;\n }\n\n get currentNotifications(): NotificationDto[] {\n return this.activeTab === 'unread' ? this._unreadNotifications : this._readNotifications;\n }\n\n selectedNotification: NotificationDto | null = null;\n selectedNotificationHtml: SafeHtml | null = null;\n selectedNotificationDate: string = '';\n\n // Returns plain text message for list display\n getNotificationMessage(notification: NotificationDto): string {\n return notification.message || '';\n }\n\n // Normalize type to string — API may return integer (0/1/2/3) or string ('Info'/'Warning'/'Error'/'Success')\n typeOf(notification: { type: any }): 'Info' | 'Warning' | 'Error' | 'Success' {\n const t = notification.type;\n if (t === 0 || t === 'Info') return 'Info';\n if (t === 1 || t === 'Warning') return 'Warning';\n if (t === 2 || t === 'Error') return 'Error';\n if (t === 3 || t === 'Success') return 'Success';\n return 'Info';\n }\n\n private toastType(type: any): 'info' | 'warning' | 'error' | 'success' {\n const t = this.typeOf({ type });\n return t.toLowerCase() as 'info' | 'warning' | 'error' | 'success';\n }\n\n private readonly sanitizer = inject(DomSanitizer);\n\n constructor(private authService: MesAuthService, private toastService: ToastService, private themeService: ThemeService) {}\n\n ngOnInit() {\n this.themeService.currentTheme$\n .pipe(takeUntil(this.destroy$))\n .subscribe(theme => {\n this.currentTheme = theme;\n });\n\n this.loadNotifications();\n\n // Refresh time-ago labels every 30s to avoid NG0100 from live new Date() in template\n this.dateTimer = setInterval(() => this.refreshDateLabels(), 30000);\n\n // Listen for new real-time notifications\n this.authService.notifications$\n .pipe(takeUntil(this.destroy$))\n .subscribe((notification: RealTimeNotificationDto) => {\n // Show toast for new notification\n this.toastService.show(\n notification.messageHtml || notification.message || '',\n notification.title,\n this.toastType(notification.type),\n 5000\n );\n // Reload notifications list\n this.loadNotifications();\n });\n }\n\n ngOnDestroy() {\n if (this.dateTimer !== null) {\n clearInterval(this.dateTimer);\n this.dateTimer = null;\n }\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n private loadNotifications() {\n this.authService.getNotifications(1, 50, true).subscribe({ // includeRead = true to get both read and unread\n next: (response: PagedList<NotificationDto>) => {\n this.notifications = response.items || [];\n this.onNotificationsChanged();\n },\n error: (err) => {}\n });\n }\n\n open() {\n this.isOpen = true;\n this.activeTab = 'unread'; // Reset to unread tab when opening\n }\n\n close() {\n this.isOpen = false;\n }\n\n switchTab(tab: 'unread' | 'read') {\n this.activeTab = tab;\n }\n\n openDetails(notification: NotificationDto) {\n this.selectedNotification = notification;\n // Cache computed values to avoid re-rendering on every change detection cycle\n const html = notification.messageHtml || notification.message || '';\n this.selectedNotificationHtml = this.sanitizer.bypassSecurityTrustHtml(html);\n this.selectedNotificationDate = this.formatDate(notification.createdAt);\n // Mark as read when opening details (if not already read)\n if (!notification.isRead) {\n this.authService.markAsRead(notification.id).subscribe({\n next: () => {\n notification.isRead = true;\n this.notificationRead.emit();\n this.onNotificationsChanged();\n },\n error: () => {}\n });\n }\n }\n\n closeDetails() {\n this.selectedNotification = null;\n this.selectedNotificationHtml = null;\n this.selectedNotificationDate = '';\n }\n\n markAsRead(notificationId: string, event?: Event) {\n if (event) {\n event.stopPropagation();\n }\n this.authService.markAsRead(notificationId).subscribe({\n next: () => {\n const notification = this.notifications.find(n => n.id === notificationId);\n if (notification) {\n notification.isRead = true;\n this.notificationRead.emit();\n this.onNotificationsChanged();\n }\n },\n error: (err) => {}\n });\n }\n\n markAllAsRead() {\n this.authService.markAllAsRead().subscribe({\n next: () => {\n this.notifications.forEach(n => n.isRead = true);\n this.notificationRead.emit();\n this.onNotificationsChanged();\n },\n error: (err) => {}\n });\n }\n\n deleteAllRead() {\n const readNotificationIds = this.notifications\n .filter(n => n.isRead)\n .map(n => n.id);\n\n // Delete all read notifications\n const deletePromises = readNotificationIds.map(id =>\n this.authService.deleteNotification(id).toPromise()\n );\n\n Promise.all(deletePromises).then(() => {\n // Remove all read notifications from the local array\n this.notifications = this.notifications.filter(n => !n.isRead);\n this.onNotificationsChanged();\n }).catch((err) => {\n // If bulk delete fails, reload notifications to get current state\n this.loadNotifications();\n });\n }\n\n deleteAllUnread() {\n const unreadNotificationIds = this.notifications\n .filter(n => !n.isRead)\n .map(n => n.id);\n\n // Delete all unread notifications\n const deletePromises = unreadNotificationIds.map(id =>\n this.authService.deleteNotification(id).toPromise()\n );\n\n Promise.all(deletePromises).then(() => {\n // Remove all unread notifications from the local array\n this.notifications = this.notifications.filter(n => n.isRead);\n this.notificationRead.emit();\n this.onNotificationsChanged();\n }).catch((err) => {\n // If bulk delete fails, reload notifications to get current state\n this.loadNotifications();\n });\n }\n\n delete(notificationId: string, event: Event) {\n event.stopPropagation();\n const wasUnread = this.notifications.find(n => n.id === notificationId && !n.isRead) !== undefined;\n this.authService.deleteNotification(notificationId).subscribe({\n next: () => {\n this.notifications = this.notifications.filter(n => n.id !== notificationId);\n if (wasUnread) {\n this.notificationRead.emit();\n }\n this.onNotificationsChanged();\n },\n error: (err) => {}\n });\n }\n\n formatDate(dateString: string): string {\n return this.computeTimeAgo(dateString, new Date());\n }\n\n // Pure computation — takes now as param so it never calls new Date() internally\n private computeTimeAgo(dateString: string, now: Date): string {\n const normalizedDateString = this.parseUtcDate(dateString);\n const date = new Date(normalizedDateString);\n\n if (isNaN(date.getTime())) {\n return 'Invalid date';\n }\n\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n\n if (diffMins < 1) return 'Now';\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n\n return date.toLocaleDateString();\n }\n\n // Rebuild dateLabels map using a single shared now — prevents mid-loop clock drift\n private refreshDateLabels(): void {\n const now = new Date();\n for (const n of this.notifications) {\n this.dateLabels.set(n.id, this.computeTimeAgo(n.createdAt, now));\n }\n }\n\n // Re-run filter once and store results in stable arrays\n private recomputeFilteredLists(): void {\n this._unreadNotifications = this.notifications.filter(n => !n.isRead);\n this._readNotifications = this.notifications.filter(n => n.isRead);\n }\n\n // Single call-site after every notification mutation\n private onNotificationsChanged(): void {\n this.recomputeFilteredLists();\n this.refreshDateLabels();\n }\n\n // Parse date string from server (stored in UTC but without 'Z' suffix or 'T' separator)\n private parseUtcDate(dateStr: string): string {\n // Handle date strings that might be missing the 'T' separator\n // Convert formats like \"2023-12-01 12:30:45\" to \"2023-12-01T12:30:45\"\n let normalized = dateStr.includes('T') ? dateStr : dateStr.replace(' ', 'T');\n\n // If no timezone indicator, assume UTC by appending 'Z'\n if (!normalized.endsWith('Z') && !normalized.includes('+') && !normalized.includes('-', 10)) {\n normalized += 'Z';\n }\n\n return normalized;\n }\n}\n","import { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { ToastContainerComponent } from './toast-container.component';\nimport { UserProfileComponent } from './user-profile.component';\nimport { NotificationPanelComponent } from './notification-panel.component';\n\n@Component({\n selector: 'ma-user',\n standalone: true,\n imports: [ToastContainerComponent, UserProfileComponent, NotificationPanelComponent],\n template: `\n <ma-toast-container></ma-toast-container>\n <div class=\"user-header\">\n <ma-user-profile (notificationClick)=\"notificationPanel.open()\"></ma-user-profile>\n </div>\n <ma-notification-panel #notificationPanel (notificationRead)=\"onNotificationRead()\"></ma-notification-panel>\n `,\n styles: [`\n .user-header {\n display: flex;\n justify-content: flex-end;\n }\n `]\n})\nexport class MaUserComponent implements AfterViewInit {\n @ViewChild(UserProfileComponent) userProfile?: UserProfileComponent;\n\n ngAfterViewInit() {\n // Ensure proper initialization\n if (this.userProfile) {\n this.userProfile.loadUnreadCount();\n }\n }\n\n onNotificationRead() {\n if (this.userProfile) {\n this.userProfile.loadUnreadCount();\n }\n }\n}\n","import { Component, OnInit, OnDestroy, Output, EventEmitter, HostBinding } from '@angular/core';\nimport { NgIf } from '@angular/common';\nimport { MesAuthService } from './mes-auth.service';\nimport { ThemeService, Theme } from './theme.service';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n@Component({\n selector: 'ma-notification-badge',\n standalone: true,\n imports: [NgIf],\n template: `\n <button class=\"notification-btn\" (click)=\"onNotificationClick()\" title=\"Notifications\">\n <span class=\"icon\">🔔</span>\n <span class=\"badge\" *ngIf=\"unreadCount > 0\">{{ unreadCount }}</span>\n </button>\n `,\n styles: [`\n :host {\n --error-color: #f44336;\n }\n\n :host(.theme-dark) {\n --error-color: #ef5350;\n }\n\n .notification-btn {\n position: relative;\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n padding: 8px;\n transition: opacity 0.2s;\n }\n\n .notification-btn:hover {\n opacity: 0.7;\n }\n\n .icon {\n display: inline-block;\n }\n\n .badge {\n position: absolute;\n top: 0;\n right: 0;\n background-color: var(--error-color);\n color: white;\n border-radius: 50%;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: bold;\n }\n `]\n})\nexport class NotificationBadgeComponent implements OnInit, OnDestroy {\n @Output() notificationClick = new EventEmitter<void>();\n @HostBinding('class') get themeClass(): string {\n return `theme-${this.currentTheme}`;\n }\n \n unreadCount = 0;\n currentTheme: Theme = 'light';\n private hasUser = false;\n private destroy$ = new Subject<void>();\n\n constructor(private authService: MesAuthService, private themeService: ThemeService) {}\n\n ngOnInit() {\n this.themeService.currentTheme$\n .pipe(takeUntil(this.destroy$))\n .subscribe(theme => {\n this.currentTheme = theme;\n });\n\n this.authService.currentUser$\n .pipe(takeUntil(this.destroy$))\n .subscribe(user => {\n this.hasUser = !!user;\n if (!this.hasUser) {\n this.unreadCount = 0;\n return;\n }\n this.loadUnreadCount();\n });\n \n // Listen for new notifications\n this.authService.notifications$\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n if (this.hasUser) {\n this.loadUnreadCount();\n }\n });\n }\n\n ngOnDestroy() {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n private loadUnreadCount() {\n if (!this.hasUser) {\n this.unreadCount = 0;\n return;\n }\n\n this.authService.getUnreadCount().subscribe({\n next: (response: any) => {\n this.unreadCount = response.unreadCount || 0;\n },\n error: (err) => console.error('Error loading unread count:', err)\n });\n }\n\n onNotificationClick() {\n this.notificationClick.emit();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1.MesAuthService","i3.ThemeService","i1.ToastService","i2.ThemeService","i2.ToastService"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mesauth-angular.mjs","sources":["../../src/mes-auth.service.ts","../../src/mes-auth.interceptor.ts","../../src/mes-auth.module.ts","../../src/theme.service.ts","../../src/user-profile.component.ts","../../src/toast.service.ts","../../src/toast-container.component.ts","../../src/notification-panel.component.ts","../../src/ma-user.component.ts","../../src/notification-badge.component.ts","../../src/mesauth-angular.ts"],"sourcesContent":["import { inject, Injectable, InjectionToken, EnvironmentProviders, makeEnvironmentProviders, provideAppInitializer, NgZone } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { HubConnection, HubConnectionBuilder, LogLevel } from '@microsoft/signalr';\nimport { BehaviorSubject, Subject, Observable, of, EMPTY } from 'rxjs';\nimport { tap, catchError } from 'rxjs/operators';\nimport { Router } from '@angular/router';\n\nexport interface MesAuthConfig {\n apiBaseUrl: string;\n withCredentials?: boolean;\n userBaseUrl?: string;\n}\n\n/** Injection token for MesAuth configuration */\nexport const MES_AUTH_CONFIG = new InjectionToken<MesAuthConfig>('MES_AUTH_CONFIG');\n\n/**\n * Provides MesAuth with configuration.\n * This is the recommended way to set up mesauth-angular in standalone apps.\n *\n * @example\n * ```typescript\n * // app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideHttpClient(withInterceptors([mesAuthInterceptor])),\n * provideMesAuth({\n * apiBaseUrl: 'https://auth.example.com',\n * userBaseUrl: 'https://app.example.com'\n * })\n * ]\n * };\n * ```\n */\nexport function provideMesAuth(config: MesAuthConfig): EnvironmentProviders {\n return makeEnvironmentProviders([\n { provide: MES_AUTH_CONFIG, useValue: config },\n MesAuthService,\n provideAppInitializer(() => {\n const mesAuthService = inject(MesAuthService);\n const httpClient = inject(HttpClient);\n const router = inject(Router);\n const ngZone = inject(NgZone);\n mesAuthService.init(config, httpClient, router, ngZone);\n })\n ]);\n}\n\nexport interface IUser {\n userId?: string;\n userName?: string;\n fullName?: string;\n gender?: string;\n email?: string;\n phoneNumber?: string;\n department?: string;\n position?: string;\n tokenVersion?: string;\n permEndpoint?: string;\n perms?: Set<string>;\n employeeCode?: string;\n avatarPath?: string;\n loginMethod?: number;\n hrFullNameVn?: string;\n hrFullNameEn?: string;\n hrPosition?: string;\n hrJobTitle?: string;\n hrGender?: string;\n hrMobile?: string;\n hrEmail?: string;\n hrJoinDate?: string;\n hrBirthDate?: string;\n hrWorkStatus?: string;\n hrDoiTuong?: string;\n hrTeamCode?: string;\n hrLineCode?: string;\n}\n\nexport enum NotificationType {\n Info = 'Info',\n Warning = 'Warning',\n Error = 'Error',\n Success = 'Success'\n}\n\nexport interface NotificationDto {\n id: string;\n title: string;\n message: string;\n messageHtml?: string;\n url?: string;\n type: NotificationType;\n isRead: boolean;\n createdAt: string;\n sourceAppName: string;\n sourceAppIconUrl?: string;\n}\n\nexport interface FrontEndRoute {\n id: number;\n roleId: string;\n roleName: string;\n routePath: string;\n routeName: string;\n description?: string;\n icon?: string;\n cssClass?: string;\n parentId?: number | null;\n sortOrder: number;\n isLabel: boolean;\n isActive: boolean;\n createdAt: string;\n updatedAt?: string;\n children: FrontEndRoute[];\n}\n\nexport interface UserFrontEndRoutesGrouped {\n appId: string;\n appName: string;\n feUrl?: string;\n routes: FrontEndRoute[];\n}\n\nexport interface FrontEndRouteMaster {\n id: number;\n appId: string;\n routePath: string;\n routeName: string;\n description?: string;\n icon?: string;\n cssClass?: string;\n parentId?: number | null;\n sortOrder: number;\n isLabel: boolean;\n isActive: boolean;\n createdAt: string;\n updatedAt?: string;\n}\n\nexport interface CreateFrontEndRouteDto {\n routePath: string;\n routeName: string;\n description?: string;\n icon?: string;\n cssClass?: string;\n parentId?: number | null;\n sortOrder?: number;\n isLabel?: boolean;\n}\n\nexport interface PagedList<T> {\n items: T[];\n totalCount: number;\n page: number;\n pageSize: number;\n totalPages: number;\n hasNext: boolean;\n hasPrevious: boolean;\n}\n\nexport interface RealTimeNotificationDto {\n id: string;\n title: string;\n message: string;\n messageHtml?: string;\n url?: string;\n type: NotificationType;\n createdAt: string;\n sourceAppName: string;\n sourceAppIconUrl?: string;\n}\n\n@Injectable()\nexport class MesAuthService {\n private hubConnection: HubConnection | null = null;\n private _currentUser = new BehaviorSubject<IUser | null>(null);\n public currentUser$: Observable<IUser | null> = this._currentUser.asObservable();\n private _notifications = new Subject<any>();\n public notifications$: Observable<any> = this._notifications.asObservable();\n\n private apiBase = '';\n private config: MesAuthConfig | null = null;\n private http!: HttpClient;\n private router?: Router;\n private ngZone: NgZone | null = null;\n\n constructor() {\n // Empty constructor - all dependencies passed to init()\n }\n\n init(config: MesAuthConfig, httpClient: HttpClient, router?: Router, ngZone?: NgZone) {\n this.config = config;\n this.http = httpClient;\n this.router = router;\n this.ngZone = ngZone ?? null;\n this.apiBase = config.apiBaseUrl.replace(/\\/$/, '');\n\n // Fetch user once on init. Route changes do NOT re-fetch the user.\n // Auth state is maintained via cookies; 401 errors are handled by HTTP interceptors.\n // SignalR handles real-time notification delivery without polling.\n this.fetchCurrentUser().subscribe();\n }\n\n getConfig(): MesAuthConfig | null {\n return this.config;\n }\n\n private fetchCurrentUser(): Observable<any> {\n if (!this.apiBase) return EMPTY;\n const url = `${this.apiBase}/auth/me`;\n return this.http.get(url, { withCredentials: this.config?.withCredentials ?? true }).pipe(\n tap((u) => {\n this._currentUser.next(u);\n if (u && this.config) {\n this.startConnection(this.config);\n }\n }),\n catchError((err) => {\n // Silently handle auth errors (401/403) - user is not logged in\n if (err.status === 401 || err.status === 403) {\n this._currentUser.next(null);\n }\n return of(null);\n })\n );\n }\n\n public getUnreadCount(): Observable<any> {\n return this.http.get(`${this.apiBase}/notif/me/unread-count`, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n public getNotifications(page: number = 1, pageSize: number = 20, includeRead: boolean = false, type?: string): Observable<any> {\n let url = `${this.apiBase}/notif/me?page=${page}&pageSize=${pageSize}&includeRead=${includeRead}`;\n if (type) {\n url += `&type=${type}`;\n }\n return this.http.get(url, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n public markAsRead(notificationId: string): Observable<any> {\n return this.http.patch(`${this.apiBase}/notif/${notificationId}/read`, {}, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n public markAllAsRead(): Observable<any> {\n return this.http.patch(`${this.apiBase}/notif/me/read-all`, {}, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n public deleteNotification(notificationId: string): Observable<any> {\n return this.http.delete(`${this.apiBase}/notif/${notificationId}`, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n /**\n * Get frontend routes assigned to the current user\n * Returns routes grouped by application\n */\n public getFrontEndRoutes(): Observable<UserFrontEndRoutesGrouped[]> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.get<UserFrontEndRoutesGrouped[]>(`${this.apiBase}/fe-routes/me`, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n /**\n * Get master routes for a specific application\n * @param appId - The application ID\n */\n public getRouteMasters(appId: string): Observable<FrontEndRouteMaster[]> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.get<FrontEndRouteMaster[]>(`${this.apiBase}/fe-routes/masters/${appId}`, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n /**\n * Register/sync frontend routes for an application\n * This is typically called on app startup to sync routes from the frontend app\n * @param appId - The application ID (passed via X-App-Id header)\n * @param routes - Array of route definitions\n */\n public registerFrontEndRoutes(appId: string, routes: CreateFrontEndRouteDto[]): Observable<any> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n const headers = { 'X-App-Id': appId };\n return this.http.post(`${this.apiBase}/fe-routes/register`, routes, {\n headers,\n withCredentials: this.config?.withCredentials ?? true\n });\n }\n\n /**\n * Create a new route master\n * @param appId - The application ID\n * @param route - Route details\n */\n public createRouteMaster(appId: string, route: CreateFrontEndRouteDto): Observable<FrontEndRouteMaster> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.post<FrontEndRouteMaster>(`${this.apiBase}/fe-routes/masters`, {\n appId,\n ...route\n }, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n /**\n * Update an existing route master\n * @param routeId - The route master ID\n * @param route - Updated route details\n */\n public updateRouteMaster(routeId: number, route: Partial<CreateFrontEndRouteDto> & { isActive?: boolean }): Observable<FrontEndRouteMaster> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.put<FrontEndRouteMaster>(`${this.apiBase}/fe-routes/masters/${routeId}`, route, {\n withCredentials: this.config?.withCredentials ?? true\n });\n }\n\n /**\n * Delete a route master\n * @param routeId - The route master ID\n */\n public deleteRouteMaster(routeId: number): Observable<any> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.delete(`${this.apiBase}/fe-routes/masters/${routeId}`, {\n withCredentials: this.config?.withCredentials ?? true\n });\n }\n\n /**\n * Assign a route to a role\n * @param routeMasterId - The route master ID\n * @param roleId - The role ID (GUID)\n */\n public assignRouteToRole(routeMasterId: number, roleId: string): Observable<any> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.post(`${this.apiBase}/fe-routes/mappings`, {\n routeMasterId,\n roleId\n }, { withCredentials: this.config?.withCredentials ?? true });\n }\n\n /**\n * Remove a route assignment from a role\n * @param mappingId - The mapping ID\n */\n public removeRouteFromRole(mappingId: number): Observable<any> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.delete(`${this.apiBase}/fe-routes/mappings/${mappingId}`, {\n withCredentials: this.config?.withCredentials ?? true\n });\n }\n\n /**\n * Get route-to-role mappings for a specific role\n * @param roleId - The role ID (GUID)\n */\n public getRouteMappingsByRole(roleId: string): Observable<any[]> {\n if (!this.apiBase) throw new Error('MesAuth not initialized');\n return this.http.get<any[]>(`${this.apiBase}/fe-routes/mappings?roleId=${roleId}`, {\n withCredentials: this.config?.withCredentials ?? true\n });\n }\n\n private startConnection(config: MesAuthConfig) {\n if (this.hubConnection) return;\n const signalrUrl = config.apiBaseUrl.replace(/\\/$/, '') + '/hub/notification';\n const builder = new HubConnectionBuilder()\n .withUrl(signalrUrl, { withCredentials: config.withCredentials ?? true })\n .withAutomaticReconnect()\n .configureLogging(LogLevel.Warning);\n\n this.hubConnection = builder.build();\n\n this.hubConnection.on('ReceiveNotification', (n: any) => {\n if (this.ngZone) {\n this.ngZone.run(() => this._notifications.next(n));\n } else {\n this._notifications.next(n);\n }\n });\n\n this.hubConnection.start().then(() => {}).catch((err) => {});\n\n this.hubConnection.onclose(() => {});\n this.hubConnection.onreconnecting(() => {});\n this.hubConnection.onreconnected(() => {});\n }\n\n public stop() {\n if (!this.hubConnection) return;\n this.hubConnection.stop().catch(() => {});\n this.hubConnection = null;\n }\n\n public logout(): Observable<any> {\n const url = `${this.apiBase}/auth/logout`;\n return this.http.post(url, {}, { withCredentials: this.config?.withCredentials ?? true }).pipe(\n tap(() => {\n this._currentUser.next(null);\n this.stop();\n })\n );\n }\n\n public get currentUser(): IUser | null {\n return this._currentUser.value;\n }\n\n public get isAuthenticated(): boolean {\n return this._currentUser.value !== null;\n }\n\n /**\n * Refreshes the current user from the server.\n * Returns an Observable that completes when the user data is loaded.\n * Callers can subscribe to wait for completion before proceeding (e.g., navigating after login).\n */\n public refreshUser(): Observable<any> {\n return this.fetchCurrentUser();\n }\n}\n","import { inject } from '@angular/core';\nimport { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';\nimport { throwError, timer } from 'rxjs';\nimport { catchError, switchMap } from 'rxjs/operators';\nimport { Router } from '@angular/router';\nimport { MesAuthService } from './mes-auth.service';\n\n// Track if we're currently redirecting to prevent loopback\nlet isRedirecting = false;\n\n/**\n * Functional HTTP interceptor for handling 401/403 auth errors.\n * Redirects to login page on 401, and to 403 page on 403.\n * Includes loopback prevention to avoid infinite redirects.\n */\nexport const mesAuthInterceptor: HttpInterceptorFn = (req, next) => {\n const authService = inject(MesAuthService);\n const router = inject(Router);\n\n return next(req).pipe(\n catchError((error: HttpErrorResponse) => {\n const status = error.status;\n\n // Check if we should handle this error and prevent loopback\n if ((status === 401 || status === 403) && !isRedirecting) {\n const config = authService.getConfig();\n const baseUrl = config?.userBaseUrl || '';\n\n const currentUrl = router.url + (window.location.hash || '');\n const returnUrl = encodeURIComponent(currentUrl);\n\n // Avoid loops if already on auth/unauth pages\n const isLoginPage = currentUrl.includes('/login');\n const is403Page = currentUrl.includes('/403');\n const isAuthPage = currentUrl.includes('/auth');\n // Public pages that should never trigger a 401 redirect (e.g., register, password reset)\n const isPublicPage = currentUrl.includes('/register')\n || currentUrl.includes('/forgot-password')\n || currentUrl.includes('/reset-password');\n // Skip redirect for the initial /auth/me check (app startup when not logged in)\n const isMeAuthPage = req.url.includes('/auth/me');\n\n if (status === 401 && !isLoginPage && !isAuthPage && !isMeAuthPage && !isPublicPage) {\n // Wait 1.5s for the concurrent refresh's Set-Cookie to be processed, then retry once.\n // If retry also gets 401, redirect to login.\n return timer(1500).pipe(\n switchMap(() => next(req)),\n catchError((retryError: HttpErrorResponse) => {\n if (retryError.status === 401) {\n isRedirecting = true;\n setTimeout(() => { isRedirecting = false; }, 5000);\n window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;\n }\n return throwError(() => retryError);\n })\n );\n } else if (status === 403 && !is403Page) {\n isRedirecting = true;\n setTimeout(() => { isRedirecting = false; }, 5000);\n let redirectUrl = `${baseUrl}/403?returnUrl=${returnUrl}`;\n if (error.error && error.error.required) {\n redirectUrl += `&required=${encodeURIComponent(error.error.required)}`;\n }\n window.location.href = redirectUrl;\n }\n }\n return throwError(() => error);\n })\n );\n};\n","import { NgModule } from '@angular/core';\nimport { MesAuthService } from './mes-auth.service';\n\n@NgModule({\n providers: [\n MesAuthService\n ]\n})\nexport class MesAuthModule {}\n","import { Injectable, OnDestroy } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\nexport type Theme = 'light' | 'dark';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class ThemeService implements OnDestroy {\n private _currentTheme = new BehaviorSubject<Theme>('light');\n public currentTheme$: Observable<Theme> = this._currentTheme.asObservable();\n private observer: MutationObserver | null = null;\n\n constructor() {\n this.detectTheme();\n this.startWatching();\n }\n\n ngOnDestroy(): void {\n this.stopWatching();\n }\n\n private detectTheme(): void {\n const html = document.documentElement;\n const isDark = html.classList.contains('dark') ||\n html.getAttribute('data-theme') === 'dark' ||\n html.getAttribute('theme') === 'dark' ||\n html.getAttribute('data-coreui-theme') === 'dark';\n\n this._currentTheme.next(isDark ? 'dark' : 'light');\n }\n\n private startWatching(): void {\n if (typeof MutationObserver === 'undefined') {\n // Fallback for older browsers - check periodically\n setInterval(() => this.detectTheme(), 1000);\n return;\n }\n\n this.observer = new MutationObserver(() => {\n this.detectTheme();\n });\n\n this.observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'data-theme', 'theme', 'data-coreui-theme']\n });\n }\n\n private stopWatching(): void {\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n }\n\n get currentTheme(): Theme {\n return this._currentTheme.value;\n }\n\n // Method to manually set theme if needed\n setTheme(theme: Theme): void {\n this._currentTheme.next(theme);\n }\n\n // Re-detect theme from DOM\n refreshTheme(): void {\n this.detectTheme();\n }\n}","import { Component, OnInit, OnDestroy, Output, EventEmitter, HostBinding, HostListener, signal, ChangeDetectorRef } from '@angular/core';\nimport { NgIf } from '@angular/common';\nimport { Router } from '@angular/router';\nimport { MesAuthService, IUser } from './mes-auth.service';\nimport { ThemeService, Theme } from './theme.service';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n@Component({\n selector: 'ma-user-profile',\n standalone: true,\n imports: [NgIf],\n template: `\n <div class=\"user-profile-container\">\n <!-- Not logged in -->\n <ng-container *ngIf=\"!currentUser()\">\n <button class=\"login-btn\" (click)=\"onLogin()\">Login</button>\n </ng-container>\n\n <!-- Logged in -->\n <ng-container *ngIf=\"currentUser()\">\n <div class=\"user-header\">\n <!-- Notification Bell -->\n <button class=\"notification-btn\" [class.has-unread]=\"unreadCount > 0\" (click)=\"onNotificationClick()\" title=\"Notifications\" aria-label=\"Notifications\">\n <svg class=\"bell-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/>\n <path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <span class=\"badge\" *ngIf=\"unreadCount > 0\">{{ unreadCount > 99 ? '99+' : unreadCount }}</span>\n </button>\n\n <!-- User Avatar + Dropdown -->\n <div class=\"user-menu-wrapper\">\n <button class=\"user-menu-btn\" (click)=\"toggleDropdown()\" [attr.aria-label]=\"'User menu for ' + (currentUser().fullName || currentUser().userName)\" aria-haspopup=\"true\" [attr.aria-expanded]=\"dropdownOpen\">\n <div class=\"avatar-ring\" [class.active]=\"dropdownOpen\">\n <img\n *ngIf=\"currentUser().fullName || currentUser().userName\"\n [src]=\"getAvatarUrl(currentUser())\"\n [alt]=\"currentUser().fullName || currentUser().userName\"\n class=\"avatar\"\n />\n <span *ngIf=\"!(currentUser().fullName || currentUser().userName)\" class=\"avatar-initial\">\n {{ getLastNameInitial(currentUser()) }}\n </span>\n </div>\n </button>\n\n <div class=\"mes-dropdown-menu\" *ngIf=\"dropdownOpen\">\n <!-- User info header -->\n <div class=\"mes-dropdown-header\">\n <div class=\"dropdown-avatar-wrap\">\n <img\n *ngIf=\"currentUser().fullName || currentUser().userName\"\n [src]=\"getAvatarUrl(currentUser())\"\n [alt]=\"currentUser().fullName || currentUser().userName\"\n class=\"dropdown-avatar\"\n />\n <span *ngIf=\"!(currentUser().fullName || currentUser().userName)\" class=\"dropdown-avatar-initial\">\n {{ getLastNameInitial(currentUser()) }}\n </span>\n </div>\n <div class=\"dropdown-user-info\">\n <span class=\"dropdown-user-name\">{{ currentUser().fullName || currentUser().userName }}</span>\n <span class=\"dropdown-user-sub\" *ngIf=\"currentUser().position || currentUser().department\">\n {{ currentUser().position || currentUser().department }}\n </span>\n </div>\n </div>\n\n <div class=\"mes-dropdown-divider\"></div>\n\n <button class=\"mes-dropdown-item profile-link\" (click)=\"onViewProfile()\">\n <svg class=\"item-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2\"/><circle cx=\"12\" cy=\"7\" r=\"4\"/>\n </svg>\n View Profile\n </button>\n\n <div class=\"mes-dropdown-divider\"></div>\n\n <button class=\"mes-dropdown-item logout-item\" (click)=\"onLogout()\">\n <svg class=\"item-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4\"/><polyline points=\"16 17 21 12 16 7\"/><line x1=\"21\" y1=\"12\" x2=\"9\" y2=\"12\"/>\n </svg>\n Logout\n </button>\n </div>\n </div>\n </div>\n </ng-container>\n </div>\n `,\n styles: [`\n :host {\n --primary-color: #1976d2;\n --primary-hover: #1565c0;\n --primary-light: rgba(25, 118, 210, 0.12);\n --primary-glow: rgba(25, 118, 210, 0.3);\n --error-color: #f44336;\n --error-light: rgba(244, 67, 54, 0.1);\n --text-primary: #212121;\n --text-secondary: #616161;\n --text-muted: #9e9e9e;\n --bg-primary: #ffffff;\n --bg-secondary: #f8f9fa;\n --bg-hover: #f0f4ff;\n --border-color: #e0e0e0;\n --shadow: rgba(0, 0, 0, 0.12);\n --shadow-lg: rgba(0, 0, 0, 0.18);\n }\n\n :host(.theme-dark) {\n --primary-color: #90caf9;\n --primary-hover: #64b5f6;\n --primary-light: rgba(144, 202, 249, 0.12);\n --primary-glow: rgba(144, 202, 249, 0.25);\n --error-color: #ef5350;\n --error-light: rgba(239, 83, 80, 0.12);\n --text-primary: #e0e0e0;\n --text-secondary: #b0b0b0;\n --text-muted: #757575;\n --bg-primary: #1e1e2e;\n --bg-secondary: #27273a;\n --bg-hover: #2a2d4a;\n --border-color: #383850;\n --shadow: rgba(0, 0, 0, 0.35);\n --shadow-lg: rgba(0, 0, 0, 0.5);\n }\n\n .user-profile-container {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .login-btn {\n padding: 7px 18px;\n background-color: var(--primary-color);\n color: white;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n font-weight: 500;\n font-size: 13px;\n letter-spacing: 0.2px;\n transition: background-color 0.2s, transform 0.15s;\n }\n\n .login-btn:hover {\n background-color: var(--primary-hover);\n transform: translateY(-1px);\n }\n\n .user-header {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n /* ── Bell button ── */\n .notification-btn {\n position: relative;\n background: none;\n border: none;\n cursor: pointer;\n padding: 8px;\n border-radius: 10px;\n color: var(--text-secondary);\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.2s, background-color 0.2s;\n }\n\n .notification-btn:hover {\n background-color: var(--primary-light);\n color: var(--primary-color);\n }\n\n .notification-btn.has-unread {\n color: var(--primary-color);\n }\n\n .bell-icon {\n display: block;\n transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n .notification-btn:hover .bell-icon {\n transform: rotate(-20deg) scale(1.15);\n }\n\n .badge {\n position: absolute;\n top: 2px;\n right: 2px;\n background-color: var(--error-color);\n color: white;\n border-radius: 10px;\n min-width: 17px;\n height: 17px;\n padding: 0 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 10px;\n font-weight: 700;\n line-height: 1;\n box-shadow: 0 0 0 2px var(--bg-primary);\n animation: badge-pop 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);\n }\n\n @keyframes badge-pop {\n from { transform: scale(0); }\n to { transform: scale(1); }\n }\n\n /* ── Avatar ── */\n .user-menu-wrapper {\n position: relative;\n }\n\n .user-menu-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 2px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: transform 0.2s;\n }\n\n .user-menu-btn:hover {\n transform: scale(1.06);\n }\n\n .avatar-ring {\n border-radius: 50%;\n padding: 2px;\n border: 2px solid transparent;\n transition: border-color 0.25s, box-shadow 0.25s;\n }\n\n .avatar-ring.active,\n .user-menu-btn:hover .avatar-ring {\n border-color: var(--primary-color);\n box-shadow: 0 0 0 3px var(--primary-glow);\n }\n\n .avatar {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n object-fit: cover;\n display: block;\n }\n\n .avatar-initial {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n font-size: 15px;\n }\n\n /* ── Dropdown ── */\n .mes-dropdown-menu {\n position: absolute;\n top: calc(100% + 10px);\n right: 0;\n background: var(--bg-primary);\n border: 1px solid var(--border-color);\n border-radius: 14px;\n box-shadow: 0 8px 32px var(--shadow-lg), 0 2px 8px var(--shadow);\n min-width: 220px;\n z-index: 1000;\n overflow: hidden;\n animation: dropdown-in 0.16s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n @keyframes dropdown-in {\n from { opacity: 0; transform: translateY(-8px) scale(0.96); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n }\n\n .mes-dropdown-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n background: var(--bg-secondary);\n }\n\n .dropdown-avatar-wrap {\n flex-shrink: 0;\n }\n\n .dropdown-avatar {\n width: 46px;\n height: 46px;\n border-radius: 50%;\n object-fit: cover;\n border: 2px solid var(--primary-color);\n display: block;\n }\n\n .dropdown-avatar-initial {\n width: 46px;\n height: 46px;\n border-radius: 50%;\n background: linear-gradient(135deg, var(--primary-color), var(--primary-hover));\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 700;\n font-size: 18px;\n border: 2px solid var(--primary-color);\n }\n\n .dropdown-user-info {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n }\n\n .dropdown-user-name {\n font-weight: 600;\n font-size: 14px;\n color: var(--text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .dropdown-user-sub {\n font-size: 11px;\n color: var(--primary-color);\n font-weight: 500;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .mes-dropdown-divider {\n height: 1px;\n background: var(--border-color);\n }\n\n .mes-dropdown-item {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 11px 16px;\n border: none;\n background: none;\n text-align: left;\n cursor: pointer;\n font-size: 13.5px;\n font-weight: 500;\n transition: background-color 0.15s;\n }\n\n .item-icon {\n flex-shrink: 0;\n opacity: 0.8;\n }\n\n .profile-link {\n color: var(--primary-color);\n }\n\n .profile-link:hover {\n background-color: var(--primary-light);\n }\n\n .logout-item {\n color: var(--error-color);\n }\n\n .logout-item:hover {\n background-color: var(--error-light);\n }\n\n @media (max-width: 768px) {\n .avatar, .avatar-initial { width: 32px; height: 32px; font-size: 13px; }\n }\n `]\n})\nexport class UserProfileComponent implements OnInit, OnDestroy {\n @Output() notificationClick = new EventEmitter<void>();\n @HostBinding('class') get themeClass(): string {\n return `theme-${this.currentTheme}`;\n }\n\n currentUser = signal<IUser | null>(null);\n currentTheme: Theme = 'light';\n unreadCount = 0;\n dropdownOpen = false;\n private hasUser = false;\n private destroy$ = new Subject<void>();\n\n // Signal to force avatar refresh\n avatarRefresh = signal<number>(Date.now());\n\n constructor(private authService: MesAuthService, private router: Router, private themeService: ThemeService, private cdr: ChangeDetectorRef) {}\n\n ngOnInit() {\n this.authService.currentUser$\n .pipe(takeUntil(this.destroy$))\n .subscribe(user => {\n this.currentUser.set(user);\n this.hasUser = !!user;\n // Force avatar refresh when user changes\n this.avatarRefresh.set(Date.now());\n if (!this.hasUser) {\n this.unreadCount = 0;\n } else {\n this.loadUnreadCount();\n }\n this.cdr.markForCheck();\n });\n\n this.themeService.currentTheme$\n .pipe(takeUntil(this.destroy$))\n .subscribe(theme => {\n this.currentTheme = theme;\n });\n\n // Listen for new real-time notifications (SignalR only)\n this.authService.notifications$\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n if (this.hasUser) {\n this.loadUnreadCount();\n }\n });\n }\n\n ngOnDestroy() {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n loadUnreadCount() {\n if (!this.hasUser) {\n this.unreadCount = 0;\n return;\n }\n\n this.authService.getUnreadCount().subscribe({\n next: (response: any) => {\n this.unreadCount = response.unreadCount || 0;\n },\n error: (err) => {}\n });\n }\n\n getAvatarUrl(user: IUser): string {\n // Use the refresh signal to force update\n const refresh = this.avatarRefresh();\n const config = this.authService.getConfig();\n const baseUrl = config?.apiBaseUrl || '';\n \n // If user has avatarPath, use it directly\n if (user.avatarPath) {\n // If avatarPath is already a full URL, use it as-is\n if (user.avatarPath.startsWith('http://') || user.avatarPath.startsWith('https://')) {\n return user.avatarPath;\n }\n // If it's a relative path, construct full URL with refresh timestamp\n return `${baseUrl.replace(/\\/$/, '')}${user.avatarPath}?t=${refresh}`;\n }\n\n // Fallback: construct URL using userId\n const userId = user.userId;\n if (userId && baseUrl) {\n return `${baseUrl.replace(/\\/$/, '')}/auth/${userId}/avatar?t=${refresh}`;\n }\n \n // Fallback to UI avatars service if no userId or baseUrl\n const displayName = user.userName || user.userId || 'User';\n return `https://ui-avatars.com/api/?name=${encodeURIComponent(displayName)}&background=1976d2&color=fff`;\n }\n\n getLastNameInitial(user: IUser): string {\n const fullName = user.fullName || user.userName || 'U';\n const parts = fullName.split(' ');\n const lastPart = parts[parts.length - 1];\n return lastPart.charAt(0).toUpperCase();\n }\n\n toggleDropdown() {\n this.dropdownOpen = !this.dropdownOpen;\n }\n\n @HostListener('document:click', ['$event'])\n onDocumentClick(event: Event) {\n const target = event.target as HTMLElement;\n const clickedInside = target.closest('.user-menu-wrapper');\n if (!clickedInside) {\n this.dropdownOpen = false;\n }\n }\n\n onLogin() {\n const config = this.authService.getConfig();\n const baseUrl = config?.userBaseUrl || '';\n const returnUrl = encodeURIComponent(this.router.url);\n window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;\n }\n\n onViewProfile() {\n this.router.navigate(['/profile']);\n this.dropdownOpen = false;\n }\n\n onLogout() {\n this.authService.logout().subscribe({\n next: () => {\n // Clear current user after successful logout\n this.dropdownOpen = false;\n \n // Navigate to login with return URL\n const config = this.authService.getConfig();\n const baseUrl = config?.userBaseUrl || '';\n const returnUrl = encodeURIComponent(window.location.href);\n window.location.href = `${baseUrl}/login?returnUrl=${returnUrl}`;\n },\n error: (err) => {\n // Still navigate to login even if logout fails\n const config = this.authService.getConfig();\n const baseUrl = config?.userBaseUrl || '';\n window.location.href = `${baseUrl}/login`;\n }\n });\n }\n\n onNotificationClick() {\n this.notificationClick.emit();\n }\n}\n\n","import { Injectable } from '@angular/core';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\nexport interface Toast {\n id: string;\n message: string;\n title?: string;\n type: 'info' | 'success' | 'warning' | 'error';\n duration?: number;\n}\n\n@Injectable({ providedIn: 'root' })\nexport class ToastService {\n private toasts$ = new BehaviorSubject<Toast[]>([]);\n public toasts: Observable<Toast[]> = this.toasts$.asObservable();\n\n show(message: string, title?: string, type: 'info' | 'success' | 'warning' | 'error' = 'info', duration: number = 5000) {\n const id = Math.random().toString(36).substr(2, 9);\n const toast: Toast = {\n id,\n message,\n title,\n type,\n duration\n };\n\n const currentToasts = this.toasts$.value;\n this.toasts$.next([...currentToasts, toast]);\n\n if (duration > 0) {\n setTimeout(() => {\n this.remove(id);\n }, duration);\n }\n\n return id;\n }\n\n remove(id: string) {\n const currentToasts = this.toasts$.value;\n this.toasts$.next(currentToasts.filter(t => t.id !== id));\n }\n\n clear() {\n this.toasts$.next([]);\n }\n}\n","import { Component, OnInit, OnDestroy, HostBinding } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { ToastService, Toast } from './toast.service';\nimport { ThemeService, Theme } from './theme.service';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n@Component({\n selector: 'ma-toast-container',\n standalone: true,\n imports: [CommonModule],\n template: `\n <div class=\"toast-container\">\n <div *ngFor=\"let toast of toasts\" class=\"toast toast-{{ toast.type }}\">\n\n <!-- Type icon -->\n <div class=\"toast-icon\">\n <svg *ngIf=\"toast.type === 'info'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/><path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <svg *ngIf=\"toast.type === 'success'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\"/><polyline points=\"22 4 12 14.01 9 11.01\"/>\n </svg>\n <svg *ngIf=\"toast.type === 'warning'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/>\n </svg>\n <svg *ngIf=\"toast.type === 'error'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/>\n </svg>\n </div>\n\n <!-- Content -->\n <div class=\"toast-content\">\n <div *ngIf=\"toast.title\" class=\"toast-title\">{{ toast.title }}</div>\n <div class=\"toast-message\" [innerHTML]=\"toast.message\"></div>\n </div>\n\n <!-- Close -->\n <button class=\"toast-close\" (click)=\"close(toast.id)\" aria-label=\"Close\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n\n <!-- Auto-dismiss progress bar — hidden when duration <= 0 (persistent toast) -->\n <div class=\"toast-progress\" *ngIf=\"toast.duration == null || toast.duration > 0\" [style.animation-duration]=\"(toast.duration ?? 5000) + 'ms'\"></div>\n </div>\n </div>\n `,\n styles: [`\n :host {\n --info-color: #2196f3;\n --info-bg: rgba(33, 150, 243, 0.1);\n --success-color: #43a047;\n --success-bg: rgba(67, 160, 71, 0.1);\n --warning-color: #f57c00;\n --warning-bg: rgba(245, 124, 0, 0.1);\n --error-color: #e53935;\n --error-bg: rgba(229, 57, 53, 0.1);\n --text-primary: #212121;\n --text-secondary: #757575;\n --bg-primary: #ffffff;\n --border-color: rgba(0, 0, 0, 0.08);\n --shadow: rgba(0, 0, 0, 0.1);\n --shadow-lg: rgba(0, 0, 0, 0.18);\n }\n\n :host(.theme-dark) {\n --info-color: #64b5f6;\n --info-bg: rgba(100, 181, 246, 0.12);\n --success-color: #66bb6a;\n --success-bg: rgba(102, 187, 106, 0.12);\n --warning-color: #ffb74d;\n --warning-bg: rgba(255, 183, 77, 0.12);\n --error-color: #ef5350;\n --error-bg: rgba(239, 83, 80, 0.12);\n --text-primary: #e0e0e0;\n --text-secondary: #9e9e9e;\n --bg-primary: #1e1e2e;\n --border-color: rgba(255, 255, 255, 0.08);\n --shadow: rgba(0, 0, 0, 0.35);\n --shadow-lg: rgba(0, 0, 0, 0.5);\n }\n\n .toast-container {\n position: fixed;\n top: 20px;\n right: 20px;\n z-index: 9999;\n pointer-events: none;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .toast {\n position: relative;\n display: flex;\n align-items: flex-start;\n gap: 11px;\n padding: 13px 13px 16px 16px;\n border-radius: 12px;\n background: var(--bg-primary);\n border: 1px solid var(--border-color);\n box-shadow: 0 8px 28px var(--shadow-lg), 0 2px 8px var(--shadow);\n pointer-events: auto;\n min-width: 300px;\n max-width: 420px;\n overflow: hidden;\n animation: toast-in 0.35s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n @keyframes toast-in {\n from { opacity: 0; transform: translateX(36px) scale(0.96); }\n to { opacity: 1; transform: translateX(0) scale(1); }\n }\n\n /* Left accent bar via ::before */\n .toast::before {\n content: '';\n position: absolute;\n left: 0; top: 0; bottom: 0;\n width: 4px;\n border-radius: 12px 0 0 12px;\n }\n\n .toast-info::before { background: var(--info-color); }\n .toast-success::before { background: var(--success-color); }\n .toast-warning::before { background: var(--warning-color); }\n .toast-error::before { background: var(--error-color); }\n\n /* Type icon bubble */\n .toast-icon {\n flex-shrink: 0;\n width: 34px;\n height: 34px;\n border-radius: 9px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-left: 2px;\n }\n\n .toast-info .toast-icon { color: var(--info-color); background: var(--info-bg); }\n .toast-success .toast-icon { color: var(--success-color); background: var(--success-bg); }\n .toast-warning .toast-icon { color: var(--warning-color); background: var(--warning-bg); }\n .toast-error .toast-icon { color: var(--error-color); background: var(--error-bg); }\n\n .toast-content {\n flex: 1;\n min-width: 0;\n padding-top: 1px;\n }\n\n .toast-title {\n font-weight: 700;\n font-size: 13.5px;\n margin-bottom: 3px;\n line-height: 1.3;\n }\n\n .toast-info .toast-title { color: var(--info-color); }\n .toast-success .toast-title { color: var(--success-color); }\n .toast-warning .toast-title { color: var(--warning-color); }\n .toast-error .toast-title { color: var(--error-color); }\n\n .toast-message {\n font-size: 12.5px;\n line-height: 1.45;\n color: var(--text-secondary);\n }\n\n .toast-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--text-secondary);\n width: 26px;\n height: 26px;\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n padding: 0;\n transition: color 0.15s, background-color 0.15s;\n }\n\n .toast-close:hover {\n color: var(--text-primary);\n background: var(--border-color);\n }\n\n /* Auto-dismiss progress bar */\n .toast-progress {\n position: absolute;\n bottom: 0;\n left: 4px;\n right: 0;\n height: 3px;\n border-radius: 0 0 12px 0;\n animation: toast-progress linear forwards;\n opacity: 0.7;\n }\n\n .toast-info .toast-progress { background: var(--info-color); }\n .toast-success .toast-progress { background: var(--success-color); }\n .toast-warning .toast-progress { background: var(--warning-color); }\n .toast-error .toast-progress { background: var(--error-color); }\n\n @keyframes toast-progress {\n from { width: calc(100% - 4px); }\n to { width: 0; }\n }\n\n @media (max-width: 600px) {\n .toast-container { top: 10px; right: 10px; left: 10px; }\n .toast { min-width: auto; max-width: 100%; }\n }\n `]\n})\nexport class ToastContainerComponent implements OnInit, OnDestroy {\n @HostBinding('class') get themeClass(): string {\n return `theme-${this.currentTheme}`;\n }\n\n toasts: Toast[] = [];\n currentTheme: Theme = 'light';\n private destroy$ = new Subject<void>();\n\n constructor(private toastService: ToastService, private themeService: ThemeService) {}\n\n ngOnInit() {\n this.toastService.toasts\n .pipe(takeUntil(this.destroy$))\n .subscribe(toasts => {\n this.toasts = toasts;\n });\n\n this.themeService.currentTheme$\n .pipe(takeUntil(this.destroy$))\n .subscribe(theme => {\n this.currentTheme = theme;\n });\n }\n\n ngOnDestroy() {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n close(id: string) {\n this.toastService.remove(id);\n }\n}\n","import { Component, OnInit, OnDestroy, HostBinding, Output, EventEmitter, inject } from '@angular/core';\nimport { NgIf, NgFor } from '@angular/common';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { MesAuthService, NotificationDto, PagedList, RealTimeNotificationDto } from './mes-auth.service';\nimport { ToastService } from './toast.service';\nimport { ThemeService, Theme } from './theme.service';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n@Component({\n selector: 'ma-notification-panel',\n standalone: true,\n imports: [NgIf, NgFor],\n template: `\n <div class=\"notification-panel\" [class.open]=\"isOpen\">\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"panel-header-left\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/>\n <path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <h3>Notifications</h3>\n </div>\n <button class=\"close-btn\" (click)=\"close()\" title=\"Close\" aria-label=\"Close notifications\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n\n <!-- Tabs -->\n <div class=\"tabs\">\n <button class=\"tab-btn\" [class.active]=\"activeTab === 'unread'\" (click)=\"switchTab('unread')\">\n Unread\n <span class=\"tab-count\" *ngIf=\"unreadNotifications.length > 0\">{{ unreadNotifications.length }}</span>\n </button>\n <button class=\"tab-btn\" [class.active]=\"activeTab === 'read'\" (click)=\"switchTab('read')\">\n Read\n <span class=\"tab-count read-count\" *ngIf=\"readNotifications.length > 0\">{{ readNotifications.length }}</span>\n </button>\n </div>\n\n <!-- Notifications List -->\n <div class=\"notifications-list\">\n <ng-container *ngIf=\"currentNotifications.length > 0\">\n <div\n *ngFor=\"let notification of currentNotifications\"\n class=\"notification-item\"\n [class.unread]=\"!notification.isRead\"\n (click)=\"openDetails(notification)\"\n >\n @let t = typeOf(notification);\n <div class=\"notif-accent\"\n [class.type-info]=\"t === 'Info'\"\n [class.type-success]=\"t === 'Success'\"\n [class.type-warning]=\"t === 'Warning'\"\n [class.type-error]=\"t === 'Error'\"></div>\n <div class=\"notif-type-icon\"\n [class.type-info]=\"t === 'Info'\"\n [class.type-success]=\"t === 'Success'\"\n [class.type-warning]=\"t === 'Warning'\"\n [class.type-error]=\"t === 'Error'\">\n <svg *ngIf=\"t === 'Info'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/><path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <svg *ngIf=\"t === 'Success'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\"/>\n </svg>\n <svg *ngIf=\"t === 'Warning'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"/>\n </svg>\n <svg *ngIf=\"t === 'Error'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/>\n </svg>\n </div>\n <div class=\"notification-content\">\n <div class=\"notification-title\">{{ notification.title }}</div>\n <div class=\"notification-message\">{{ getNotificationMessage(notification) }}</div>\n <div class=\"notification-meta\">\n <span class=\"app-name\">{{ notification.sourceAppName }}</span>\n <span class=\"time\">{{ dateLabels.get(notification.id) }}</span>\n </div>\n </div>\n <button class=\"icon-btn read-btn\" (click)=\"markAsRead(notification.id, $event)\" title=\"Mark as read\" aria-label=\"Mark as read\" *ngIf=\"!notification.isRead\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\"/>\n </svg>\n </button>\n <button class=\"icon-btn delete-btn\" (click)=\"delete(notification.id, $event)\" title=\"Delete\" aria-label=\"Delete notification\" *ngIf=\"notification.isRead\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\"/><path d=\"M10 11v6\"/><path d=\"M14 11v6\"/><path d=\"M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2\"/>\n </svg>\n </button>\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"currentNotifications.length === 0\">\n <div class=\"empty-state\">\n <svg class=\"empty-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"44\" height=\"44\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/>\n <path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <p>No {{ activeTab }} notifications</p>\n </div>\n </ng-container>\n </div>\n\n <!-- Footer Actions -->\n <div class=\"panel-footer\" *ngIf=\"currentNotifications.length > 0\">\n <div class=\"footer-actions\" *ngIf=\"activeTab === 'unread'\">\n <button class=\"action-btn\" (click)=\"markAllAsRead()\" *ngIf=\"unreadNotifications.length > 0\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>\n Mark all read\n </button>\n <button class=\"action-btn delete-all-btn\" (click)=\"deleteAllUnread()\" *ngIf=\"unreadNotifications.length > 0\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\"/></svg>\n Delete all\n </button>\n </div>\n <button class=\"action-btn delete-all-btn\" (click)=\"deleteAllRead()\" *ngIf=\"activeTab === 'read' && readNotifications.length > 0\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"/><path d=\"M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6\"/></svg>\n Delete all\n </button>\n </div>\n </div>\n\n <!-- Details Modal -->\n <div class=\"modal-overlay\" *ngIf=\"selectedNotification\" (click)=\"closeDetails()\">\n <div class=\"modal-container\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\"\n [class.modal-type-info]=\"typeOf(selectedNotification) === 'Info'\"\n [class.modal-type-success]=\"typeOf(selectedNotification) === 'Success'\"\n [class.modal-type-warning]=\"typeOf(selectedNotification) === 'Warning'\"\n [class.modal-type-error]=\"typeOf(selectedNotification) === 'Error'\">\n <div class=\"modal-header-left\">\n <div class=\"modal-type-icon\">\n <svg *ngIf=\"typeOf(selectedNotification) === 'Info'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"/><path d=\"M13.73 21a2 2 0 0 1-3.46 0\"/>\n </svg>\n <svg *ngIf=\"typeOf(selectedNotification) === 'Success'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M22 11.08V12a10 10 0 1 1-5.93-9.14\"/><polyline points=\"22 4 12 14.01 9 11.01\"/>\n </svg>\n <svg *ngIf=\"typeOf(selectedNotification) === 'Warning'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/>\n </svg>\n <svg *ngIf=\"typeOf(selectedNotification) === 'Error'\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"15\" y1=\"9\" x2=\"9\" y2=\"15\"/><line x1=\"9\" y1=\"9\" x2=\"15\" y2=\"15\"/>\n </svg>\n </div>\n <h3>{{ selectedNotification.title }}</h3>\n </div>\n <button class=\"close-btn\" (click)=\"closeDetails()\" title=\"Close\" aria-label=\"Close notification detail\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n </div>\n <div class=\"modal-meta\">\n <span class=\"app-name\">{{ selectedNotification.sourceAppName }}</span>\n <span class=\"time\">{{ selectedNotificationDate }}</span>\n </div>\n <div class=\"modal-body\" [innerHTML]=\"selectedNotificationHtml\"></div>\n <div class=\"modal-footer\">\n <button class=\"action-btn\" (click)=\"closeDetails()\">Close</button>\n </div>\n </div>\n </div>\n `,\n styles: [`\n :host {\n display: block;\n position: relative;\n --primary-color: #1976d2;\n --primary-hover: #1565c0;\n --primary-light: rgba(25, 118, 210, 0.1);\n --success-color: #43a047;\n --error-color: #f44336;\n --error-hover: #d32f2f;\n --unread-accent: #1976d2;\n --info-color: #2196f3;\n --info-bg: rgba(33, 150, 243, 0.1);\n --success-bg: rgba(67, 160, 71, 0.1);\n --warning-color: #f57c00;\n --warning-bg: rgba(245, 124, 0, 0.1);\n --error-bg: rgba(244, 67, 54, 0.1);\n --text-primary: #212121;\n --text-secondary: #616161;\n --text-muted: #9e9e9e;\n --bg-primary: #ffffff;\n --bg-secondary: #f8f9fa;\n --bg-hover: #f0f4ff;\n --bg-unread: rgba(25, 118, 210, 0.06);\n --border-color: #e0e0e0;\n --border-light: #eeeeee;\n --shadow: rgba(0, 0, 0, 0.15);\n }\n\n :host(.theme-dark) {\n display: block;\n position: relative;\n --primary-color: #90caf9;\n --primary-hover: #64b5f6;\n --primary-light: rgba(144, 202, 249, 0.1);\n --success-color: #66bb6a;\n --error-color: #ef5350;\n --error-hover: #c62828;\n --unread-accent: #90caf9;\n --info-color: #64b5f6;\n --info-bg: rgba(100, 181, 246, 0.12);\n --success-bg: rgba(102, 187, 106, 0.12);\n --warning-color: #ffb74d;\n --warning-bg: rgba(255, 183, 77, 0.12);\n --error-bg: rgba(239, 83, 80, 0.12);\n --text-primary: #e0e0e0;\n --text-secondary: #b0b0b0;\n --text-muted: #757575;\n --bg-primary: #1e1e2e;\n --bg-secondary: #27273a;\n --bg-hover: #2a2d4a;\n --bg-unread: rgba(144, 202, 249, 0.08);\n --border-color: #383850;\n --border-light: #2e2e42;\n --shadow: rgba(0, 0, 0, 0.4);\n }\n\n /* ── Panel ── */\n .notification-panel {\n position: fixed;\n top: 0;\n right: -360px;\n width: 360px;\n height: 100vh;\n background: var(--bg-primary);\n box-shadow: -4px 0 24px var(--shadow);\n display: flex;\n flex-direction: column;\n z-index: 1030;\n transition: right 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n .notification-panel.open {\n right: 0;\n }\n\n /* ── Header ── */\n .panel-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 18px;\n border-bottom: 1px solid var(--border-color);\n background: var(--bg-secondary);\n flex-shrink: 0;\n }\n\n .panel-header-left {\n display: flex;\n align-items: center;\n gap: 9px;\n color: var(--primary-color);\n }\n\n .panel-header h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 700;\n color: var(--text-primary);\n letter-spacing: 0.1px;\n }\n\n .close-btn {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--text-muted);\n width: 32px;\n height: 32px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.2s, background-color 0.2s;\n }\n\n .close-btn:hover {\n color: var(--text-primary);\n background-color: var(--bg-hover);\n }\n\n /* ── Tabs ── */\n .tabs {\n display: flex;\n gap: 6px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--border-color);\n background: var(--bg-secondary);\n flex-shrink: 0;\n }\n\n .tab-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 7px 12px;\n background: none;\n border: 1px solid var(--border-color);\n border-radius: 20px;\n color: var(--text-secondary);\n cursor: pointer;\n font-size: 13px;\n font-weight: 500;\n transition: all 0.18s;\n }\n\n .tab-btn:hover {\n background: var(--bg-hover);\n color: var(--primary-color);\n border-color: var(--primary-color);\n }\n\n .tab-btn.active {\n background: var(--primary-color);\n border-color: var(--primary-color);\n color: white;\n }\n\n .tab-count {\n background: rgba(255,255,255,0.25);\n border-radius: 10px;\n min-width: 18px;\n height: 18px;\n padding: 0 5px;\n font-size: 10px;\n font-weight: 700;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .tab-btn:not(.active) .tab-count {\n background: var(--error-color);\n color: white;\n }\n\n .read-count {\n background: rgba(255,255,255,0.25);\n }\n\n .tab-btn:not(.active) .read-count {\n background: var(--text-muted);\n }\n\n /* ── Notifications list ── */\n .notifications-list {\n flex: 1;\n overflow-y: auto;\n }\n\n .notification-item {\n display: flex;\n align-items: flex-start;\n gap: 0;\n border-bottom: 1px solid var(--border-light);\n cursor: pointer;\n background: var(--bg-primary);\n transition: background-color 0.15s;\n position: relative;\n }\n\n .notification-item:hover {\n background: var(--bg-hover);\n }\n\n .notification-item.unread {\n background: var(--bg-unread);\n }\n\n /* Left accent bar — type-based color, dimmed for read */\n .notif-accent {\n width: 3px;\n align-self: stretch;\n flex-shrink: 0;\n background: transparent;\n border-radius: 0 2px 2px 0;\n opacity: 0.3;\n }\n\n .notification-item.unread .notif-accent {\n opacity: 1;\n }\n\n .notif-accent.type-info { background: var(--info-color); }\n .notif-accent.type-success { background: var(--success-color); }\n .notif-accent.type-warning { background: var(--warning-color); }\n .notif-accent.type-error { background: var(--error-color); }\n\n /* Small type icon bubble */\n .notif-type-icon {\n flex-shrink: 0;\n width: 26px;\n height: 26px;\n border-radius: 7px;\n display: flex;\n align-items: center;\n justify-content: center;\n align-self: center;\n margin-left: 10px;\n }\n\n .notif-type-icon.type-info { color: var(--info-color); background: var(--info-bg); }\n .notif-type-icon.type-success { color: var(--success-color); background: var(--success-bg); }\n .notif-type-icon.type-warning { color: var(--warning-color); background: var(--warning-bg); }\n .notif-type-icon.type-error { color: var(--error-color); background: var(--error-bg); }\n\n .notification-content {\n flex: 1;\n min-width: 0;\n padding: 12px 8px 12px 12px;\n }\n\n .notification-title {\n font-weight: 600;\n color: var(--text-primary);\n font-size: 13.5px;\n margin-bottom: 3px;\n line-height: 1.35;\n }\n\n .notification-message {\n color: var(--text-secondary);\n font-size: 12px;\n line-height: 1.45;\n margin-bottom: 7px;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .notification-meta {\n display: flex;\n justify-content: space-between;\n font-size: 11px;\n color: var(--text-muted);\n }\n\n .app-name {\n font-weight: 600;\n color: var(--primary-color);\n }\n\n /* Icon action buttons */\n .icon-btn {\n background: none;\n border: none;\n cursor: pointer;\n width: 32px;\n height: 32px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n align-self: center;\n margin-right: 8px;\n transition: color 0.15s, background-color 0.15s;\n color: var(--text-muted);\n }\n\n .read-btn:hover {\n color: var(--success-color);\n background: rgba(67, 160, 71, 0.1);\n }\n\n .delete-btn:hover {\n color: var(--error-color);\n background: rgba(244, 67, 54, 0.1);\n }\n\n /* ── Empty state ── */\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n height: 100%;\n padding: 40px 20px;\n color: var(--text-muted);\n }\n\n .empty-icon {\n opacity: 0.35;\n }\n\n .empty-state p {\n margin: 0;\n font-size: 13px;\n }\n\n /* ── Footer ── */\n .panel-footer {\n padding: 10px 14px;\n border-top: 1px solid var(--border-color);\n background: var(--bg-secondary);\n flex-shrink: 0;\n }\n\n .footer-actions {\n display: flex;\n gap: 8px;\n }\n\n .footer-actions .action-btn {\n flex: 1;\n }\n\n .action-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 100%;\n padding: 8px 12px;\n background: var(--primary-color);\n color: white;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n font-size: 12.5px;\n font-weight: 600;\n transition: background-color 0.18s, transform 0.12s;\n }\n\n .action-btn:hover {\n background: var(--primary-hover);\n transform: translateY(-1px);\n }\n\n .delete-all-btn {\n background: var(--error-color);\n }\n\n .delete-all-btn:hover {\n background: var(--error-hover);\n }\n\n /* ── Modal ── */\n .modal-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1060;\n backdrop-filter: blur(2px);\n }\n\n .modal-container {\n background: var(--bg-primary);\n border-radius: 14px;\n width: 90%;\n max-width: 580px;\n max-height: 80vh;\n display: flex;\n flex-direction: column;\n box-shadow: 0 16px 48px rgba(0,0,0,0.25);\n animation: modal-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n }\n\n @keyframes modal-in {\n from { opacity: 0; transform: scale(0.94) translateY(8px); }\n to { opacity: 1; transform: scale(1) translateY(0); }\n }\n\n .modal-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px 20px;\n border-bottom: 1px solid var(--border-color);\n background: var(--bg-secondary);\n border-radius: 14px 14px 0 0;\n border-top: 3px solid transparent;\n }\n\n .modal-header.modal-type-info { border-top-color: var(--info-color); }\n .modal-header.modal-type-success { border-top-color: var(--success-color); }\n .modal-header.modal-type-warning { border-top-color: var(--warning-color); }\n .modal-header.modal-type-error { border-top-color: var(--error-color); }\n\n .modal-header-left {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n }\n\n .modal-type-icon {\n flex-shrink: 0;\n width: 32px;\n height: 32px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .modal-type-info .modal-type-icon { color: var(--info-color); background: var(--info-bg); }\n .modal-type-success .modal-type-icon { color: var(--success-color); background: var(--success-bg); }\n .modal-type-warning .modal-type-icon { color: var(--warning-color); background: var(--warning-bg); }\n .modal-type-error .modal-type-icon { color: var(--error-color); background: var(--error-bg); }\n\n .modal-header h3 {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .modal-meta {\n display: flex;\n justify-content: space-between;\n padding: 8px 20px;\n font-size: 11.5px;\n color: var(--text-muted);\n border-bottom: 1px solid var(--border-light);\n }\n\n .modal-body {\n padding: 20px;\n overflow-y: auto;\n flex: 1;\n color: var(--text-primary);\n font-size: 14px;\n line-height: 1.65;\n }\n\n .modal-footer {\n padding: 12px 20px;\n border-top: 1px solid var(--border-color);\n background: var(--bg-secondary);\n border-radius: 0 0 14px 14px;\n display: flex;\n justify-content: flex-end;\n }\n\n .modal-footer .action-btn {\n width: auto;\n padding: 8px 24px;\n }\n\n @media (max-width: 600px) {\n .notification-panel { width: 100%; right: -100%; }\n .modal-container { width: 95%; max-height: 90vh; }\n }\n `]\n})\nexport class NotificationPanelComponent implements OnInit, OnDestroy {\n @Output() notificationRead = new EventEmitter<void>();\n @HostBinding('class') get themeClass(): string {\n return `theme-${this.currentTheme}`;\n }\n\n isOpen = false;\n notifications: NotificationDto[] = [];\n currentTheme: Theme = 'light';\n activeTab: 'unread' | 'read' = 'unread'; // Default to unread tab\n private destroy$ = new Subject<void>();\n\n // Cached filtered lists — updated explicitly to avoid re-filtering on every CD cycle\n private _unreadNotifications: NotificationDto[] = [];\n private _readNotifications: NotificationDto[] = [];\n\n // Stable time-ago strings keyed by notification id — refreshed every 30s\n dateLabels: Map<string, string> = new Map();\n private dateTimer: ReturnType<typeof setInterval> | null = null;\n\n get unreadNotifications(): NotificationDto[] {\n return this._unreadNotifications;\n }\n\n get readNotifications(): NotificationDto[] {\n return this._readNotifications;\n }\n\n get currentNotifications(): NotificationDto[] {\n return this.activeTab === 'unread' ? this._unreadNotifications : this._readNotifications;\n }\n\n selectedNotification: NotificationDto | null = null;\n selectedNotificationHtml: SafeHtml | null = null;\n selectedNotificationDate: string = '';\n\n // Returns plain text message for list display\n getNotificationMessage(notification: NotificationDto): string {\n return notification.message || '';\n }\n\n // Normalize type to string — API may return integer or string\n // Backend enum: Info=0, Success=1, Warning=2, Error=3\n typeOf(notification: { type: any }): 'Info' | 'Success' | 'Warning' | 'Error' {\n const t = notification.type;\n if (t === 0 || t === 'Info') return 'Info';\n if (t === 1 || t === 'Success') return 'Success';\n if (t === 2 || t === 'Warning') return 'Warning';\n if (t === 3 || t === 'Error') return 'Error';\n return 'Info';\n }\n\n private toastType(type: any): 'info' | 'warning' | 'error' | 'success' {\n const t = this.typeOf({ type });\n return t.toLowerCase() as 'info' | 'warning' | 'error' | 'success';\n }\n\n private readonly sanitizer = inject(DomSanitizer);\n\n constructor(private authService: MesAuthService, private toastService: ToastService, private themeService: ThemeService) {}\n\n ngOnInit() {\n this.themeService.currentTheme$\n .pipe(takeUntil(this.destroy$))\n .subscribe(theme => {\n this.currentTheme = theme;\n });\n\n this.loadNotifications();\n\n // Refresh time-ago labels every 30s to avoid NG0100 from live new Date() in template\n this.dateTimer = setInterval(() => this.refreshDateLabels(), 30000);\n\n // Listen for new real-time notifications\n this.authService.notifications$\n .pipe(takeUntil(this.destroy$))\n .subscribe((notification: RealTimeNotificationDto) => {\n // Show toast for new notification\n this.toastService.show(\n notification.messageHtml || notification.message || '',\n notification.title,\n this.toastType(notification.type),\n 5000\n );\n // Reload notifications list\n this.loadNotifications();\n });\n }\n\n ngOnDestroy() {\n if (this.dateTimer !== null) {\n clearInterval(this.dateTimer);\n this.dateTimer = null;\n }\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n private loadNotifications() {\n this.authService.getNotifications(1, 50, true).subscribe({ // includeRead = true to get both read and unread\n next: (response: PagedList<NotificationDto>) => {\n this.notifications = response.items || [];\n this.onNotificationsChanged();\n },\n error: (err) => {}\n });\n }\n\n open() {\n this.isOpen = true;\n this.activeTab = 'unread'; // Reset to unread tab when opening\n }\n\n close() {\n this.isOpen = false;\n }\n\n switchTab(tab: 'unread' | 'read') {\n this.activeTab = tab;\n }\n\n openDetails(notification: NotificationDto) {\n this.selectedNotification = notification;\n // Cache computed values to avoid re-rendering on every change detection cycle\n const html = notification.messageHtml || notification.message || '';\n this.selectedNotificationHtml = this.sanitizer.bypassSecurityTrustHtml(html);\n this.selectedNotificationDate = this.formatDate(notification.createdAt);\n // Mark as read when opening details (if not already read)\n if (!notification.isRead) {\n this.authService.markAsRead(notification.id).subscribe({\n next: () => {\n notification.isRead = true;\n this.notificationRead.emit();\n this.onNotificationsChanged();\n },\n error: () => {}\n });\n }\n }\n\n closeDetails() {\n this.selectedNotification = null;\n this.selectedNotificationHtml = null;\n this.selectedNotificationDate = '';\n }\n\n markAsRead(notificationId: string, event?: Event) {\n if (event) {\n event.stopPropagation();\n }\n this.authService.markAsRead(notificationId).subscribe({\n next: () => {\n const notification = this.notifications.find(n => n.id === notificationId);\n if (notification) {\n notification.isRead = true;\n this.notificationRead.emit();\n this.onNotificationsChanged();\n }\n },\n error: (err) => {}\n });\n }\n\n markAllAsRead() {\n this.authService.markAllAsRead().subscribe({\n next: () => {\n this.notifications.forEach(n => n.isRead = true);\n this.notificationRead.emit();\n this.onNotificationsChanged();\n },\n error: (err) => {}\n });\n }\n\n deleteAllRead() {\n const readNotificationIds = this.notifications\n .filter(n => n.isRead)\n .map(n => n.id);\n\n // Delete all read notifications\n const deletePromises = readNotificationIds.map(id =>\n this.authService.deleteNotification(id).toPromise()\n );\n\n Promise.all(deletePromises).then(() => {\n // Remove all read notifications from the local array\n this.notifications = this.notifications.filter(n => !n.isRead);\n this.onNotificationsChanged();\n }).catch((err) => {\n // If bulk delete fails, reload notifications to get current state\n this.loadNotifications();\n });\n }\n\n deleteAllUnread() {\n const unreadNotificationIds = this.notifications\n .filter(n => !n.isRead)\n .map(n => n.id);\n\n // Delete all unread notifications\n const deletePromises = unreadNotificationIds.map(id =>\n this.authService.deleteNotification(id).toPromise()\n );\n\n Promise.all(deletePromises).then(() => {\n // Remove all unread notifications from the local array\n this.notifications = this.notifications.filter(n => n.isRead);\n this.notificationRead.emit();\n this.onNotificationsChanged();\n }).catch((err) => {\n // If bulk delete fails, reload notifications to get current state\n this.loadNotifications();\n });\n }\n\n delete(notificationId: string, event: Event) {\n event.stopPropagation();\n const wasUnread = this.notifications.find(n => n.id === notificationId && !n.isRead) !== undefined;\n this.authService.deleteNotification(notificationId).subscribe({\n next: () => {\n this.notifications = this.notifications.filter(n => n.id !== notificationId);\n if (wasUnread) {\n this.notificationRead.emit();\n }\n this.onNotificationsChanged();\n },\n error: (err) => {}\n });\n }\n\n formatDate(dateString: string): string {\n return this.computeTimeAgo(dateString, new Date());\n }\n\n // Pure computation — takes now as param so it never calls new Date() internally\n private computeTimeAgo(dateString: string, now: Date): string {\n const normalizedDateString = this.parseUtcDate(dateString);\n const date = new Date(normalizedDateString);\n\n if (isNaN(date.getTime())) {\n return 'Invalid date';\n }\n\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n const diffDays = Math.floor(diffMs / 86400000);\n\n if (diffMins < 1) return 'Now';\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 7) return `${diffDays}d ago`;\n\n return date.toLocaleDateString();\n }\n\n // Rebuild dateLabels map using a single shared now — prevents mid-loop clock drift\n private refreshDateLabels(): void {\n const now = new Date();\n for (const n of this.notifications) {\n this.dateLabels.set(n.id, this.computeTimeAgo(n.createdAt, now));\n }\n }\n\n // Re-run filter once and store results in stable arrays\n private recomputeFilteredLists(): void {\n this._unreadNotifications = this.notifications.filter(n => !n.isRead);\n this._readNotifications = this.notifications.filter(n => n.isRead);\n }\n\n // Single call-site after every notification mutation\n private onNotificationsChanged(): void {\n this.recomputeFilteredLists();\n this.refreshDateLabels();\n }\n\n // Parse date string from server (stored in UTC but without 'Z' suffix or 'T' separator)\n private parseUtcDate(dateStr: string): string {\n // Handle date strings that might be missing the 'T' separator\n // Convert formats like \"2023-12-01 12:30:45\" to \"2023-12-01T12:30:45\"\n let normalized = dateStr.includes('T') ? dateStr : dateStr.replace(' ', 'T');\n\n // If no timezone indicator, assume UTC by appending 'Z'\n if (!normalized.endsWith('Z') && !normalized.includes('+') && !normalized.includes('-', 10)) {\n normalized += 'Z';\n }\n\n return normalized;\n }\n}\n","import { Component, ViewChild, AfterViewInit } from '@angular/core';\nimport { ToastContainerComponent } from './toast-container.component';\nimport { UserProfileComponent } from './user-profile.component';\nimport { NotificationPanelComponent } from './notification-panel.component';\n\n@Component({\n selector: 'ma-user',\n standalone: true,\n imports: [ToastContainerComponent, UserProfileComponent, NotificationPanelComponent],\n template: `\n <ma-toast-container></ma-toast-container>\n <div class=\"user-header\">\n <ma-user-profile (notificationClick)=\"notificationPanel.open()\"></ma-user-profile>\n </div>\n <ma-notification-panel #notificationPanel (notificationRead)=\"onNotificationRead()\"></ma-notification-panel>\n `,\n styles: [`\n .user-header {\n display: flex;\n justify-content: flex-end;\n }\n `]\n})\nexport class MaUserComponent implements AfterViewInit {\n @ViewChild(UserProfileComponent) userProfile?: UserProfileComponent;\n\n ngAfterViewInit() {\n // Ensure proper initialization\n if (this.userProfile) {\n this.userProfile.loadUnreadCount();\n }\n }\n\n onNotificationRead() {\n if (this.userProfile) {\n this.userProfile.loadUnreadCount();\n }\n }\n}\n","import { Component, OnInit, OnDestroy, Output, EventEmitter, HostBinding } from '@angular/core';\nimport { NgIf } from '@angular/common';\nimport { MesAuthService } from './mes-auth.service';\nimport { ThemeService, Theme } from './theme.service';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\n@Component({\n selector: 'ma-notification-badge',\n standalone: true,\n imports: [NgIf],\n template: `\n <button class=\"notification-btn\" (click)=\"onNotificationClick()\" title=\"Notifications\">\n <span class=\"icon\">🔔</span>\n <span class=\"badge\" *ngIf=\"unreadCount > 0\">{{ unreadCount }}</span>\n </button>\n `,\n styles: [`\n :host {\n --error-color: #f44336;\n }\n\n :host(.theme-dark) {\n --error-color: #ef5350;\n }\n\n .notification-btn {\n position: relative;\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n padding: 8px;\n transition: opacity 0.2s;\n }\n\n .notification-btn:hover {\n opacity: 0.7;\n }\n\n .icon {\n display: inline-block;\n }\n\n .badge {\n position: absolute;\n top: 0;\n right: 0;\n background-color: var(--error-color);\n color: white;\n border-radius: 50%;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: bold;\n }\n `]\n})\nexport class NotificationBadgeComponent implements OnInit, OnDestroy {\n @Output() notificationClick = new EventEmitter<void>();\n @HostBinding('class') get themeClass(): string {\n return `theme-${this.currentTheme}`;\n }\n \n unreadCount = 0;\n currentTheme: Theme = 'light';\n private hasUser = false;\n private destroy$ = new Subject<void>();\n\n constructor(private authService: MesAuthService, private themeService: ThemeService) {}\n\n ngOnInit() {\n this.themeService.currentTheme$\n .pipe(takeUntil(this.destroy$))\n .subscribe(theme => {\n this.currentTheme = theme;\n });\n\n this.authService.currentUser$\n .pipe(takeUntil(this.destroy$))\n .subscribe(user => {\n this.hasUser = !!user;\n if (!this.hasUser) {\n this.unreadCount = 0;\n return;\n }\n this.loadUnreadCount();\n });\n \n // Listen for new notifications\n this.authService.notifications$\n .pipe(takeUntil(this.destroy$))\n .subscribe(() => {\n if (this.hasUser) {\n this.loadUnreadCount();\n }\n });\n }\n\n ngOnDestroy() {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n private loadUnreadCount() {\n if (!this.hasUser) {\n this.unreadCount = 0;\n return;\n }\n\n this.authService.getUnreadCount().subscribe({\n next: (response: any) => {\n this.unreadCount = response.unreadCount || 0;\n },\n error: (err) => console.error('Error loading unread count:', err)\n });\n }\n\n onNotificationClick() {\n this.notificationClick.emit();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1.MesAuthService","i3.ThemeService","i1.ToastService","i2.ThemeService","i2.ToastService"],"mappings":"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"}
|
package/index.d.ts
CHANGED
|
@@ -360,7 +360,7 @@ declare class NotificationPanelComponent implements OnInit, OnDestroy {
|
|
|
360
360
|
getNotificationMessage(notification: NotificationDto): string;
|
|
361
361
|
typeOf(notification: {
|
|
362
362
|
type: any;
|
|
363
|
-
}): 'Info' | '
|
|
363
|
+
}): 'Info' | 'Success' | 'Warning' | 'Error';
|
|
364
364
|
private toastType;
|
|
365
365
|
private readonly sanitizer;
|
|
366
366
|
constructor(authService: MesAuthService, toastService: ToastService, themeService: ThemeService);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mesauth-angular",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "Angular helper library to connect to a backend API and SignalR hub to surface the current logged-in user and incoming notifications with dark/light theme support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|